2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
243 int main P((int argc, char **argv));
244 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
245 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
246 RETSIGTYPE CmailSigHandler P((int sig));
247 RETSIGTYPE IntSigHandler P((int sig));
248 RETSIGTYPE TermSizeSigHandler P((int sig));
249 void CreateGCs P((int redo));
250 void CreateAnyPieces P((void));
251 void CreateXIMPieces P((void));
252 void CreateXPMPieces P((void));
253 void CreateXPMBoard P((char *s, int n));
254 void CreatePieces P((void));
255 void CreatePieceMenus P((void));
256 Widget CreateMenuBar P((Menu *mb));
257 Widget CreateButtonBar P ((MenuItem *mi));
259 char *InsertPxlSize P((char *pattern, int targetPxlSize));
260 XFontSet CreateFontSet P((char *base_fnt_lst));
262 char *FindFont P((char *pattern, int targetPxlSize));
264 void PieceMenuPopup P((Widget w, XEvent *event,
265 String *params, Cardinal *num_params));
266 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
267 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
268 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
269 u_int wreq, u_int hreq));
270 void CreateGrid P((void));
271 int EventToSquare P((int x, int limit));
272 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
273 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
274 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
275 void HandleUserMove P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AnimateUserMove P((Widget w, XEvent * event,
278 String * params, Cardinal * nParams));
279 void HandlePV P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void SelectPV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void StopPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void WhiteClock P((Widget w, XEvent *event,
286 String *prms, Cardinal *nprms));
287 void BlackClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void DrawPositionProc P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
293 void CommentClick P((Widget w, XEvent * event,
294 String * params, Cardinal * nParams));
295 void CommentPopUp P((char *title, char *label));
296 void CommentPopDown P((void));
297 void ICSInputBoxPopUp P((void));
298 void ICSInputBoxPopDown P((void));
299 void FileNamePopUp P((char *label, char *def, char *filter,
300 FileProc proc, char *openMode));
301 void FileNamePopDown P((void));
302 void FileNameCallback P((Widget w, XtPointer client_data,
303 XtPointer call_data));
304 void FileNameAction P((Widget w, XEvent *event,
305 String *prms, Cardinal *nprms));
306 void AskQuestionReplyAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionProc P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionPopDown P((void));
311 void PromotionPopDown P((void));
312 void PromotionCallback P((Widget w, XtPointer client_data,
313 XtPointer call_data));
314 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
315 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
316 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
317 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
319 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
321 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
323 void LoadPositionProc P((Widget w, XEvent *event,
324 String *prms, Cardinal *nprms));
325 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
327 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
329 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
331 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
333 void PastePositionProc P((Widget w, XEvent *event, String *prms,
335 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
337 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void SavePositionProc P((Widget w, XEvent *event,
340 String *prms, Cardinal *nprms));
341 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
344 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
348 void MachineWhiteProc P((Widget w, XEvent *event,
349 String *prms, Cardinal *nprms));
350 void AnalyzeModeProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeFileProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
356 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void IcsClientProc P((Widget w, XEvent *event, String *prms,
360 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void EditPositionProc P((Widget w, XEvent *event,
362 String *prms, Cardinal *nprms));
363 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void EditCommentProc P((Widget w, XEvent *event,
365 String *prms, Cardinal *nprms));
366 void IcsInputBoxProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void StopObservingProc P((Widget w, XEvent *event, String *prms,
385 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
387 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
388 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
389 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
396 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
398 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
401 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
403 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
405 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
406 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
407 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
408 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
410 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
413 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
415 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
417 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
418 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
419 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
420 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
422 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
424 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
426 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
428 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
429 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
431 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
433 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
435 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
437 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void DisplayMove P((int moveNumber));
449 void DisplayTitle P((char *title));
450 void ICSInitScript P((void));
451 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
452 void ErrorPopUp P((char *title, char *text, int modal));
453 void ErrorPopDown P((void));
454 static char *ExpandPathName P((char *path));
455 static void CreateAnimVars P((void));
456 static void DragPieceMove P((int x, int y));
457 static void DrawDragPiece P((void));
458 char *ModeToWidgetName P((GameMode mode));
459 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void GameListOptionsPopDown P(());
477 void GenericPopDown P(());
478 void update_ics_width P(());
479 int get_term_width P(());
480 int CopyMemoProc P(());
481 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
482 Boolean IsDrawArrowEnabled P(());
485 * XBoard depends on Xt R4 or higher
487 int xtVersion = XtSpecificationRelease;
492 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
493 jailSquareColor, highlightSquareColor, premoveHighlightColor;
494 Pixel lowTimeWarningColor;
495 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
496 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
497 wjPieceGC, bjPieceGC, prelineGC, countGC;
498 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
499 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
500 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
501 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
502 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
503 ICSInputShell, fileNameShell, askQuestionShell;
504 Widget historyShell, evalGraphShell, gameListShell;
505 int hOffset; // [HGM] dual
506 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
507 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
508 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
510 XFontSet fontSet, clockFontSet;
513 XFontStruct *clockFontStruct;
515 Font coordFontID, countFontID;
516 XFontStruct *coordFontStruct, *countFontStruct;
517 XtAppContext appContext;
519 char *oldICSInteractionTitle;
523 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
525 Position commentX = -1, commentY = -1;
526 Dimension commentW, commentH;
527 typedef unsigned int BoardSize;
529 Boolean chessProgram;
531 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
532 int squareSize, smallLayout = 0, tinyLayout = 0,
533 marginW, marginH, // [HGM] for run-time resizing
534 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
535 ICSInputBoxUp = False, askQuestionUp = False,
536 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
537 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
538 Pixel timerForegroundPixel, timerBackgroundPixel;
539 Pixel buttonForegroundPixel, buttonBackgroundPixel;
540 char *chessDir, *programName, *programVersion,
541 *gameCopyFilename, *gamePasteFilename;
542 Boolean alwaysOnTop = False;
543 Boolean saveSettingsOnExit;
544 char *settingsFileName;
545 char *icsTextMenuString;
547 char *firstChessProgramNames;
548 char *secondChessProgramNames;
550 WindowPlacement wpMain;
551 WindowPlacement wpConsole;
552 WindowPlacement wpComment;
553 WindowPlacement wpMoveHistory;
554 WindowPlacement wpEvalGraph;
555 WindowPlacement wpEngineOutput;
556 WindowPlacement wpGameList;
557 WindowPlacement wpTags;
559 extern Widget shells[];
560 extern Boolean shellUp[];
564 Pixmap pieceBitmap[2][(int)BlackPawn];
565 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
566 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
567 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
568 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
569 Pixmap xpmBoardBitmap[2];
570 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
571 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
572 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
573 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
574 XImage *ximLightSquare, *ximDarkSquare;
577 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
578 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
580 #define White(piece) ((int)(piece) < (int)BlackPawn)
582 /* Variables for doing smooth animation. This whole thing
583 would be much easier if the board was double-buffered,
584 but that would require a fairly major rewrite. */
589 GC blitGC, pieceGC, outlineGC;
590 XPoint startSquare, prevFrame, mouseDelta;
594 int startBoardX, startBoardY;
597 /* There can be two pieces being animated at once: a player
598 can begin dragging a piece before the remote opponent has moved. */
600 static AnimState game, player;
602 /* Bitmaps for use as masks when drawing XPM pieces.
603 Need one for each black and white piece. */
604 static Pixmap xpmMask[BlackKing + 1];
606 /* This magic number is the number of intermediate frames used
607 in each half of the animation. For short moves it's reduced
608 by 1. The total number of frames will be factor * 2 + 1. */
611 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
613 MenuItem fileMenu[] = {
614 {N_("New Game Ctrl+N"), "New Game", ResetProc},
615 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
616 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
617 {"----", NULL, NothingProc},
618 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
619 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
620 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
621 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
622 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
623 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
624 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
625 {"----", NULL, NothingProc},
626 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
627 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
628 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
629 {"----", NULL, NothingProc},
630 {N_("Mail Move"), "Mail Move", MailMoveProc},
631 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
632 {"----", NULL, NothingProc},
633 {N_("Quit Ctr+Q"), "Exit", QuitProc},
637 MenuItem editMenu[] = {
638 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
639 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
640 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
641 {"----", NULL, NothingProc},
642 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
643 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
644 {"----", NULL, NothingProc},
645 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
646 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
647 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
648 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
649 {N_("Edit Book"), "Edit Book", EditBookProc},
650 {"----", NULL, NothingProc},
651 {N_("Revert Home"), "Revert", RevertProc},
652 {N_("Annotate"), "Annotate", AnnotateProc},
653 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
654 {"----", NULL, NothingProc},
655 {N_("Backward Alt+Left"), "Backward", BackwardProc},
656 {N_("Forward Alt+Right"), "Forward", ForwardProc},
657 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
658 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
662 MenuItem viewMenu[] = {
663 {N_("Flip View F2"), "Flip View", FlipViewProc},
664 {"----", NULL, NothingProc},
665 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
666 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
667 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
668 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
669 {N_("ICS text menu"), "ICStex", IcsTextProc},
670 {"----", NULL, NothingProc},
671 {N_("Tags"), "Show Tags", EditTagsProc},
672 {N_("Comments"), "Show Comments", EditCommentProc},
673 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
674 {"----", NULL, NothingProc},
675 {N_("Board..."), "Board Options", BoardOptionsProc},
676 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
680 MenuItem modeMenu[] = {
681 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
682 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
683 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
684 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
685 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
686 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
687 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
688 {N_("Training"), "Training", TrainingProc},
689 {N_("ICS Client"), "ICS Client", IcsClientProc},
690 {"----", NULL, NothingProc},
691 {N_("Machine Match"), "Machine Match", MatchProc},
692 {N_("Pause Pause"), "Pause", PauseProc},
696 MenuItem actionMenu[] = {
697 {N_("Accept F3"), "Accept", AcceptProc},
698 {N_("Decline F4"), "Decline", DeclineProc},
699 {N_("Rematch F12"), "Rematch", RematchProc},
700 {"----", NULL, NothingProc},
701 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
702 {N_("Draw F6"), "Draw", DrawProc},
703 {N_("Adjourn F7"), "Adjourn", AdjournProc},
704 {N_("Abort F8"),"Abort", AbortProc},
705 {N_("Resign F9"), "Resign", ResignProc},
706 {"----", NULL, NothingProc},
707 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
708 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
709 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
710 {"----", NULL, NothingProc},
711 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
712 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
713 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
717 MenuItem engineMenu[] = {
718 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
719 {"----", NULL, NothingProc},
720 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
721 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
722 {"----", NULL, NothingProc},
723 {N_("Hint"), "Hint", HintProc},
724 {N_("Book"), "Book", BookProc},
725 {"----", NULL, NothingProc},
726 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
727 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
731 MenuItem optionsMenu[] = {
732 #define OPTIONSDIALOG
734 {N_("General ..."), "General", OptionsProc},
736 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
737 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
738 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
739 {N_("ICS ..."), "ICS", IcsOptionsProc},
740 {N_("Match ..."), "Match", MatchOptionsProc},
741 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
742 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
743 // {N_(" ..."), "", OptionsProc},
744 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
745 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
746 {"----", NULL, NothingProc},
747 #ifndef OPTIONSDIALOG
748 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
749 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
750 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
751 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
752 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
753 {N_("Blindfold"), "Blindfold", BlindfoldProc},
754 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
756 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
758 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
759 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
760 {N_("Move Sound"), "Move Sound", MoveSoundProc},
761 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
762 {N_("One-Click Moving"), "OneClick", OneClickProc},
763 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
764 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
765 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
766 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
767 // {N_("Premove"), "Premove", PremoveProc},
768 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
769 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
770 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
771 {"----", NULL, NothingProc},
773 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
774 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
778 MenuItem helpMenu[] = {
779 {N_("Info XBoard"), "Info XBoard", InfoProc},
780 {N_("Man XBoard F1"), "Man XBoard", ManProc},
781 {"----", NULL, NothingProc},
782 {N_("About XBoard"), "About XBoard", AboutProc},
787 {N_("File"), "File", fileMenu},
788 {N_("Edit"), "Edit", editMenu},
789 {N_("View"), "View", viewMenu},
790 {N_("Mode"), "Mode", modeMenu},
791 {N_("Action"), "Action", actionMenu},
792 {N_("Engine"), "Engine", engineMenu},
793 {N_("Options"), "Options", optionsMenu},
794 {N_("Help"), "Help", helpMenu},
798 #define PAUSE_BUTTON "P"
799 MenuItem buttonBar[] = {
800 {"<<", "<<", ToStartProc},
801 {"<", "<", BackwardProc},
802 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
803 {">", ">", ForwardProc},
804 {">>", ">>", ToEndProc},
808 #define PIECE_MENU_SIZE 18
809 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
810 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
811 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
812 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
813 N_("Empty square"), N_("Clear board") },
814 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
815 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
816 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
817 N_("Empty square"), N_("Clear board") }
819 /* must be in same order as pieceMenuStrings! */
820 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
821 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
822 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
823 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
824 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
825 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
826 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
827 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
828 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
831 #define DROP_MENU_SIZE 6
832 String dropMenuStrings[DROP_MENU_SIZE] = {
833 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
835 /* must be in same order as dropMenuStrings! */
836 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
837 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
838 WhiteRook, WhiteQueen
846 DropMenuEnables dmEnables[] = {
864 { XtNborderWidth, 0 },
865 { XtNdefaultDistance, 0 },
869 { XtNborderWidth, 0 },
870 { XtNresizable, (XtArgVal) True },
874 { XtNborderWidth, 0 },
880 { XtNjustify, (XtArgVal) XtJustifyRight },
881 { XtNlabel, (XtArgVal) "..." },
882 { XtNresizable, (XtArgVal) True },
883 { XtNresize, (XtArgVal) False }
886 Arg messageArgs[] = {
887 { XtNjustify, (XtArgVal) XtJustifyLeft },
888 { XtNlabel, (XtArgVal) "..." },
889 { XtNresizable, (XtArgVal) True },
890 { XtNresize, (XtArgVal) False }
894 { XtNborderWidth, 0 },
895 { XtNjustify, (XtArgVal) XtJustifyLeft }
898 XtResource clientResources[] = {
899 { "flashCount", "flashCount", XtRInt, sizeof(int),
900 XtOffset(AppDataPtr, flashCount), XtRImmediate,
901 (XtPointer) FLASH_COUNT },
904 XrmOptionDescRec shellOptions[] = {
905 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
906 { "-flash", "flashCount", XrmoptionNoArg, "3" },
907 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
910 XtActionsRec boardActions[] = {
911 { "DrawPosition", DrawPositionProc },
912 { "HandleUserMove", HandleUserMove },
913 { "AnimateUserMove", AnimateUserMove },
914 { "HandlePV", HandlePV },
915 { "SelectPV", SelectPV },
916 { "StopPV", StopPV },
917 { "FileNameAction", FileNameAction },
918 { "AskQuestionProc", AskQuestionProc },
919 { "AskQuestionReplyAction", AskQuestionReplyAction },
920 { "PieceMenuPopup", PieceMenuPopup },
921 { "WhiteClock", WhiteClock },
922 { "BlackClock", BlackClock },
923 { "Iconify", Iconify },
924 { "ResetProc", ResetProc },
925 { "NewVariantProc", NewVariantProc },
926 { "LoadGameProc", LoadGameProc },
927 { "LoadNextGameProc", LoadNextGameProc },
928 { "LoadPrevGameProc", LoadPrevGameProc },
929 { "LoadSelectedProc", LoadSelectedProc },
930 { "SetFilterProc", SetFilterProc },
931 { "ReloadGameProc", ReloadGameProc },
932 { "LoadPositionProc", LoadPositionProc },
933 { "LoadNextPositionProc", LoadNextPositionProc },
934 { "LoadPrevPositionProc", LoadPrevPositionProc },
935 { "ReloadPositionProc", ReloadPositionProc },
936 { "CopyPositionProc", CopyPositionProc },
937 { "PastePositionProc", PastePositionProc },
938 { "CopyGameProc", CopyGameProc },
939 { "CopyGameListProc", CopyGameListProc },
940 { "PasteGameProc", PasteGameProc },
941 { "SaveGameProc", SaveGameProc },
942 { "SavePositionProc", SavePositionProc },
943 { "MailMoveProc", MailMoveProc },
944 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
945 { "QuitProc", QuitProc },
946 { "MachineWhiteProc", MachineWhiteProc },
947 { "MachineBlackProc", MachineBlackProc },
948 { "AnalysisModeProc", AnalyzeModeProc },
949 { "AnalyzeFileProc", AnalyzeFileProc },
950 { "TwoMachinesProc", TwoMachinesProc },
951 { "IcsClientProc", IcsClientProc },
952 { "EditGameProc", EditGameProc },
953 { "EditPositionProc", EditPositionProc },
954 { "TrainingProc", EditPositionProc },
955 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
956 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
957 { "ShowGameListProc", ShowGameListProc },
958 { "ShowMoveListProc", HistoryShowProc},
959 { "EditTagsProc", EditCommentProc },
960 { "EditBookProc", EditBookProc },
961 { "EditCommentProc", EditCommentProc },
962 { "IcsInputBoxProc", IcsInputBoxProc },
963 { "PauseProc", PauseProc },
964 { "AcceptProc", AcceptProc },
965 { "DeclineProc", DeclineProc },
966 { "RematchProc", RematchProc },
967 { "CallFlagProc", CallFlagProc },
968 { "DrawProc", DrawProc },
969 { "AdjournProc", AdjournProc },
970 { "AbortProc", AbortProc },
971 { "ResignProc", ResignProc },
972 { "AdjuWhiteProc", AdjuWhiteProc },
973 { "AdjuBlackProc", AdjuBlackProc },
974 { "AdjuDrawProc", AdjuDrawProc },
975 { "TypeInProc", TypeInProc },
976 { "EnterKeyProc", EnterKeyProc },
977 { "UpKeyProc", UpKeyProc },
978 { "DownKeyProc", DownKeyProc },
979 { "StopObservingProc", StopObservingProc },
980 { "StopExaminingProc", StopExaminingProc },
981 { "UploadProc", UploadProc },
982 { "BackwardProc", BackwardProc },
983 { "ForwardProc", ForwardProc },
984 { "ToStartProc", ToStartProc },
985 { "ToEndProc", ToEndProc },
986 { "RevertProc", RevertProc },
987 { "AnnotateProc", AnnotateProc },
988 { "TruncateGameProc", TruncateGameProc },
989 { "MoveNowProc", MoveNowProc },
990 { "RetractMoveProc", RetractMoveProc },
991 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
992 { "UciMenuProc", (XtActionProc) UciMenuProc },
993 { "TimeControlProc", (XtActionProc) TimeControlProc },
994 { "FlipViewProc", FlipViewProc },
995 { "PonderNextMoveProc", PonderNextMoveProc },
996 #ifndef OPTIONSDIALOG
997 { "AlwaysQueenProc", AlwaysQueenProc },
998 { "AnimateDraggingProc", AnimateDraggingProc },
999 { "AnimateMovingProc", AnimateMovingProc },
1000 { "AutoflagProc", AutoflagProc },
1001 { "AutoflipProc", AutoflipProc },
1002 { "BlindfoldProc", BlindfoldProc },
1003 { "FlashMovesProc", FlashMovesProc },
1005 { "HighlightDraggingProc", HighlightDraggingProc },
1007 { "HighlightLastMoveProc", HighlightLastMoveProc },
1008 // { "IcsAlarmProc", IcsAlarmProc },
1009 { "MoveSoundProc", MoveSoundProc },
1010 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1011 { "PopupExitMessageProc", PopupExitMessageProc },
1012 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1013 // { "PremoveProc", PremoveProc },
1014 { "ShowCoordsProc", ShowCoordsProc },
1015 { "ShowThinkingProc", ShowThinkingProc },
1016 { "HideThinkingProc", HideThinkingProc },
1017 { "TestLegalityProc", TestLegalityProc },
1019 { "SaveSettingsProc", SaveSettingsProc },
1020 { "SaveOnExitProc", SaveOnExitProc },
1021 { "InfoProc", InfoProc },
1022 { "ManProc", ManProc },
1023 { "HintProc", HintProc },
1024 { "BookProc", BookProc },
1025 { "AboutGameProc", AboutGameProc },
1026 { "AboutProc", AboutProc },
1027 { "DebugProc", DebugProc },
1028 { "NothingProc", NothingProc },
1029 { "CommentClick", (XtActionProc) CommentClick },
1030 { "CommentPopDown", (XtActionProc) CommentPopDown },
1031 { "TagsPopDown", (XtActionProc) TagsPopDown },
1032 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1033 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1034 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1035 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1036 { "GameListPopDown", (XtActionProc) GameListPopDown },
1037 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1038 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1039 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1040 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1041 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1042 { "GenericPopDown", (XtActionProc) GenericPopDown },
1043 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1046 char globalTranslations[] =
1047 ":<Key>F9: ResignProc() \n \
1048 :Ctrl<Key>n: ResetProc() \n \
1049 :Meta<Key>V: NewVariantProc() \n \
1050 :Ctrl<Key>o: LoadGameProc() \n \
1051 :Meta<Key>Next: LoadNextGameProc() \n \
1052 :Meta<Key>Prior: LoadPrevGameProc() \n \
1053 :Ctrl<Key>s: SaveGameProc() \n \
1054 :Ctrl<Key>c: CopyGameProc() \n \
1055 :Ctrl<Key>v: PasteGameProc() \n \
1056 :Ctrl<Key>O: LoadPositionProc() \n \
1057 :Shift<Key>Next: LoadNextPositionProc() \n \
1058 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1059 :Ctrl<Key>S: SavePositionProc() \n \
1060 :Ctrl<Key>C: CopyPositionProc() \n \
1061 :Ctrl<Key>V: PastePositionProc() \n \
1062 :Ctrl<Key>q: QuitProc() \n \
1063 :Ctrl<Key>w: MachineWhiteProc() \n \
1064 :Ctrl<Key>b: MachineBlackProc() \n \
1065 :Ctrl<Key>t: TwoMachinesProc() \n \
1066 :Ctrl<Key>a: AnalysisModeProc() \n \
1067 :Ctrl<Key>f: AnalyzeFileProc() \n \
1068 :Ctrl<Key>e: EditGameProc() \n \
1069 :Ctrl<Key>E: EditPositionProc() \n \
1070 :Meta<Key>O: EngineOutputProc() \n \
1071 :Meta<Key>E: EvalGraphProc() \n \
1072 :Meta<Key>G: ShowGameListProc() \n \
1073 :Meta<Key>H: ShowMoveListProc() \n \
1074 :<Key>Pause: PauseProc() \n \
1075 :<Key>F3: AcceptProc() \n \
1076 :<Key>F4: DeclineProc() \n \
1077 :<Key>F12: RematchProc() \n \
1078 :<Key>F5: CallFlagProc() \n \
1079 :<Key>F6: DrawProc() \n \
1080 :<Key>F7: AdjournProc() \n \
1081 :<Key>F8: AbortProc() \n \
1082 :<Key>F10: StopObservingProc() \n \
1083 :<Key>F11: StopExaminingProc() \n \
1084 :Meta Ctrl<Key>F12: DebugProc() \n \
1085 :Meta<Key>End: ToEndProc() \n \
1086 :Meta<Key>Right: ForwardProc() \n \
1087 :Meta<Key>Home: ToStartProc() \n \
1088 :Meta<Key>Left: BackwardProc() \n \
1089 :<Key>Home: RevertProc() \n \
1090 :<Key>End: TruncateGameProc() \n \
1091 :Ctrl<Key>m: MoveNowProc() \n \
1092 :Ctrl<Key>x: RetractMoveProc() \n \
1093 :Meta<Key>J: EngineMenuProc() \n \
1094 :Meta<Key>U: UciMenuProc() \n \
1095 :Meta<Key>T: TimeControlProc() \n \
1096 :Ctrl<Key>P: PonderNextMoveProc() \n "
1097 #ifndef OPTIONSDIALOG
1099 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1100 :Ctrl<Key>F: AutoflagProc() \n \
1101 :Ctrl<Key>A: AnimateMovingProc() \n \
1102 :Ctrl<Key>L: TestLegalityProc() \n \
1103 :Ctrl<Key>H: HideThinkingProc() \n "
1106 :<Key>-: Iconify() \n \
1107 :<Key>F1: ManProc() \n \
1108 :<Key>F2: FlipViewProc() \n \
1109 <KeyDown>.: BackwardProc() \n \
1110 <KeyUp>.: ForwardProc() \n \
1111 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1112 \"Send to chess program:\",,1) \n \
1113 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1114 \"Send to second chess program:\",,2) \n";
1116 char boardTranslations[] =
1117 "<Btn1Down>: HandleUserMove(0) \n \
1118 Shift<Btn1Up>: HandleUserMove(1) \n \
1119 <Btn1Up>: HandleUserMove(0) \n \
1120 <Btn1Motion>: AnimateUserMove() \n \
1121 <Btn3Motion>: HandlePV() \n \
1122 <Btn3Up>: PieceMenuPopup(menuB) \n \
1123 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1124 PieceMenuPopup(menuB) \n \
1125 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1126 PieceMenuPopup(menuW) \n \
1127 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1128 PieceMenuPopup(menuW) \n \
1129 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1130 PieceMenuPopup(menuB) \n";
1132 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1133 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1135 char ICSInputTranslations[] =
1136 "<Key>Up: UpKeyProc() \n "
1137 "<Key>Down: DownKeyProc() \n "
1138 "<Key>Return: EnterKeyProc() \n";
1140 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1141 // as the widget is destroyed before the up-click can call extend-end
1142 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1144 String xboardResources[] = {
1145 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1146 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1147 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1152 /* Max possible square size */
1153 #define MAXSQSIZE 256
1155 static int xpm_avail[MAXSQSIZE];
1157 #ifdef HAVE_DIR_STRUCT
1159 /* Extract piece size from filename */
1161 xpm_getsize(name, len, ext)
1172 if ((p=strchr(name, '.')) == NULL ||
1173 StrCaseCmp(p+1, ext) != 0)
1179 while (*p && isdigit(*p))
1186 /* Setup xpm_avail */
1188 xpm_getavail(dirname, ext)
1196 for (i=0; i<MAXSQSIZE; ++i)
1199 if (appData.debugMode)
1200 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1202 dir = opendir(dirname);
1205 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1206 programName, dirname);
1210 while ((ent=readdir(dir)) != NULL) {
1211 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1212 if (i > 0 && i < MAXSQSIZE)
1222 xpm_print_avail(fp, ext)
1228 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1229 for (i=1; i<MAXSQSIZE; ++i) {
1235 /* Return XPM piecesize closest to size */
1237 xpm_closest_to(dirname, size, ext)
1243 int sm_diff = MAXSQSIZE;
1247 xpm_getavail(dirname, ext);
1249 if (appData.debugMode)
1250 xpm_print_avail(stderr, ext);
1252 for (i=1; i<MAXSQSIZE; ++i) {
1255 diff = (diff<0) ? -diff : diff;
1256 if (diff < sm_diff) {
1264 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1270 #else /* !HAVE_DIR_STRUCT */
1271 /* If we are on a system without a DIR struct, we can't
1272 read the directory, so we can't collect a list of
1273 filenames, etc., so we can't do any size-fitting. */
1275 xpm_closest_to(dirname, size, ext)
1280 fprintf(stderr, _("\
1281 Warning: No DIR structure found on this system --\n\
1282 Unable to autosize for XPM/XIM pieces.\n\
1283 Please report this error to %s.\n\
1284 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1287 #endif /* HAVE_DIR_STRUCT */
1289 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1290 "magenta", "cyan", "white" };
1294 TextColors textColors[(int)NColorClasses];
1296 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1298 parse_color(str, which)
1302 char *p, buf[100], *d;
1305 if (strlen(str) > 99) /* watch bounds on buf */
1310 for (i=0; i<which; ++i) {
1317 /* Could be looking at something like:
1319 .. in which case we want to stop on a comma also */
1320 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1324 return -1; /* Use default for empty field */
1327 if (which == 2 || isdigit(*p))
1330 while (*p && isalpha(*p))
1335 for (i=0; i<8; ++i) {
1336 if (!StrCaseCmp(buf, cnames[i]))
1337 return which? (i+40) : (i+30);
1339 if (!StrCaseCmp(buf, "default")) return -1;
1341 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1346 parse_cpair(cc, str)
1350 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1351 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1356 /* bg and attr are optional */
1357 textColors[(int)cc].bg = parse_color(str, 1);
1358 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1359 textColors[(int)cc].attr = 0;
1365 /* Arrange to catch delete-window events */
1366 Atom wm_delete_window;
1368 CatchDeleteWindow(Widget w, String procname)
1371 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1372 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1373 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1380 XtSetArg(args[0], XtNiconic, False);
1381 XtSetValues(shellWidget, args, 1);
1383 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1386 //---------------------------------------------------------------------------------------------------------
1387 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1390 #define CW_USEDEFAULT (1<<31)
1391 #define ICS_TEXT_MENU_SIZE 90
1392 #define DEBUG_FILE "xboard.debug"
1393 #define SetCurrentDirectory chdir
1394 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1398 // these two must some day move to frontend.h, when they are implemented
1399 Boolean GameListIsUp();
1401 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1404 // front-end part of option handling
1406 // [HGM] This platform-dependent table provides the location for storing the color info
1407 extern char *crWhite, * crBlack;
1411 &appData.whitePieceColor,
1412 &appData.blackPieceColor,
1413 &appData.lightSquareColor,
1414 &appData.darkSquareColor,
1415 &appData.highlightSquareColor,
1416 &appData.premoveHighlightColor,
1417 &appData.lowTimeWarningColor,
1428 // [HGM] font: keep a font for each square size, even non-stndard ones
1429 #define NUM_SIZES 18
1430 #define MAX_SIZE 130
1431 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1432 char *fontTable[NUM_FONTS][MAX_SIZE];
1435 ParseFont(char *name, int number)
1436 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1438 if(sscanf(name, "size%d:", &size)) {
1439 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1440 // defer processing it until we know if it matches our board size
1441 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1442 fontTable[number][size] = strdup(strchr(name, ':')+1);
1443 fontValid[number][size] = True;
1448 case 0: // CLOCK_FONT
1449 appData.clockFont = strdup(name);
1451 case 1: // MESSAGE_FONT
1452 appData.font = strdup(name);
1454 case 2: // COORD_FONT
1455 appData.coordFont = strdup(name);
1460 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1465 { // only 2 fonts currently
1466 appData.clockFont = CLOCK_FONT_NAME;
1467 appData.coordFont = COORD_FONT_NAME;
1468 appData.font = DEFAULT_FONT_NAME;
1473 { // no-op, until we identify the code for this already in XBoard and move it here
1477 ParseColor(int n, char *name)
1478 { // in XBoard, just copy the color-name string
1479 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1483 ParseTextAttribs(ColorClass cc, char *s)
1485 (&appData.colorShout)[cc] = strdup(s);
1489 ParseBoardSize(void *addr, char *name)
1491 appData.boardSize = strdup(name);
1496 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1500 SetCommPortDefaults()
1501 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1504 // [HGM] args: these three cases taken out to stay in front-end
1506 SaveFontArg(FILE *f, ArgDescriptor *ad)
1509 int i, n = (int)(intptr_t)ad->argLoc;
1511 case 0: // CLOCK_FONT
1512 name = appData.clockFont;
1514 case 1: // MESSAGE_FONT
1515 name = appData.font;
1517 case 2: // COORD_FONT
1518 name = appData.coordFont;
1523 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1524 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1525 fontTable[n][squareSize] = strdup(name);
1526 fontValid[n][squareSize] = True;
1529 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1530 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1535 { // nothing to do, as the sounds are at all times represented by their text-string names already
1539 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1540 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1541 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1545 SaveColor(FILE *f, ArgDescriptor *ad)
1546 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1547 if(colorVariable[(int)(intptr_t)ad->argLoc])
1548 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1552 SaveBoardSize(FILE *f, char *name, void *addr)
1553 { // wrapper to shield back-end from BoardSize & sizeInfo
1554 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1558 ParseCommPortSettings(char *s)
1559 { // no such option in XBoard (yet)
1562 extern Widget engineOutputShell;
1565 GetActualPlacement(Widget wg, WindowPlacement *wp)
1575 XtSetArg(args[i], XtNx, &x); i++;
1576 XtSetArg(args[i], XtNy, &y); i++;
1577 XtSetArg(args[i], XtNwidth, &w); i++;
1578 XtSetArg(args[i], XtNheight, &h); i++;
1579 XtGetValues(wg, args, i);
1588 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1589 // In XBoard this will have to wait until awareness of window parameters is implemented
1590 GetActualPlacement(shellWidget, &wpMain);
1591 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1592 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1593 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1594 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1595 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1596 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1600 PrintCommPortSettings(FILE *f, char *name)
1601 { // This option does not exist in XBoard
1605 MySearchPath(char *installDir, char *name, char *fullname)
1606 { // just append installDir and name. Perhaps ExpandPath should be used here?
1607 name = ExpandPathName(name);
1608 if(name && name[0] == '/')
1609 safeStrCpy(fullname, name, MSG_SIZ );
1611 sprintf(fullname, "%s%c%s", installDir, '/', name);
1617 MyGetFullPathName(char *name, char *fullname)
1618 { // should use ExpandPath?
1619 name = ExpandPathName(name);
1620 safeStrCpy(fullname, name, MSG_SIZ );
1625 EnsureOnScreen(int *x, int *y, int minX, int minY)
1632 { // [HGM] args: allows testing if main window is realized from back-end
1633 return xBoardWindow != 0;
1637 PopUpStartupDialog()
1638 { // start menu not implemented in XBoard
1642 ConvertToLine(int argc, char **argv)
1644 static char line[128*1024], buf[1024];
1648 for(i=1; i<argc; i++)
1650 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1651 && argv[i][0] != '{' )
1652 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1654 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1655 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1658 line[strlen(line)-1] = NULLCHAR;
1662 //--------------------------------------------------------------------------------------------
1664 extern Boolean twoBoards, partnerUp;
1667 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1669 #define BoardSize int
1670 void InitDrawingSizes(BoardSize boardSize, int flags)
1671 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1672 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1674 XtGeometryResult gres;
1677 if(!formWidget) return;
1680 * Enable shell resizing.
1682 shellArgs[0].value = (XtArgVal) &w;
1683 shellArgs[1].value = (XtArgVal) &h;
1684 XtGetValues(shellWidget, shellArgs, 2);
1686 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1687 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1688 XtSetValues(shellWidget, &shellArgs[2], 4);
1690 XtSetArg(args[0], XtNdefaultDistance, &sep);
1691 XtGetValues(formWidget, args, 1);
1693 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1694 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1695 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1697 hOffset = boardWidth + 10;
1698 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1699 secondSegments[i] = gridSegments[i];
1700 secondSegments[i].x1 += hOffset;
1701 secondSegments[i].x2 += hOffset;
1704 XtSetArg(args[0], XtNwidth, boardWidth);
1705 XtSetArg(args[1], XtNheight, boardHeight);
1706 XtSetValues(boardWidget, args, 2);
1708 timerWidth = (boardWidth - sep) / 2;
1709 XtSetArg(args[0], XtNwidth, timerWidth);
1710 XtSetValues(whiteTimerWidget, args, 1);
1711 XtSetValues(blackTimerWidget, args, 1);
1713 XawFormDoLayout(formWidget, False);
1715 if (appData.titleInWindow) {
1717 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1718 XtSetArg(args[i], XtNheight, &h); i++;
1719 XtGetValues(titleWidget, args, i);
1721 w = boardWidth - 2*bor;
1723 XtSetArg(args[0], XtNwidth, &w);
1724 XtGetValues(menuBarWidget, args, 1);
1725 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1728 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1729 if (gres != XtGeometryYes && appData.debugMode) {
1731 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1732 programName, gres, w, h, wr, hr);
1736 XawFormDoLayout(formWidget, True);
1739 * Inhibit shell resizing.
1741 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1742 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1743 shellArgs[4].value = shellArgs[2].value = w;
1744 shellArgs[5].value = shellArgs[3].value = h;
1745 XtSetValues(shellWidget, &shellArgs[0], 6);
1747 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1750 for(i=0; i<4; i++) {
1752 for(p=0; p<=(int)WhiteKing; p++)
1753 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1754 if(gameInfo.variant == VariantShogi) {
1755 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1756 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1757 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1758 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1759 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1762 if(gameInfo.variant == VariantGothic) {
1763 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1766 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1767 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1768 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1771 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1772 for(p=0; p<=(int)WhiteKing; p++)
1773 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1774 if(gameInfo.variant == VariantShogi) {
1775 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1776 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1777 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1778 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1779 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1782 if(gameInfo.variant == VariantGothic) {
1783 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1786 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1787 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1788 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1793 for(i=0; i<2; i++) {
1795 for(p=0; p<=(int)WhiteKing; p++)
1796 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1797 if(gameInfo.variant == VariantShogi) {
1798 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1799 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1800 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1801 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1802 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1805 if(gameInfo.variant == VariantGothic) {
1806 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1809 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1810 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1811 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1821 void ParseIcsTextColors()
1822 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1823 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1824 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1825 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1826 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1827 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1828 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1829 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1830 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1831 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1832 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1834 if (appData.colorize) {
1836 _("%s: can't parse color names; disabling colorization\n"),
1839 appData.colorize = FALSE;
1844 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1845 XrmValue vFrom, vTo;
1846 int forceMono = False;
1848 if (!appData.monoMode) {
1849 vFrom.addr = (caddr_t) appData.lightSquareColor;
1850 vFrom.size = strlen(appData.lightSquareColor);
1851 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1852 if (vTo.addr == NULL) {
1853 appData.monoMode = True;
1856 lightSquareColor = *(Pixel *) vTo.addr;
1859 if (!appData.monoMode) {
1860 vFrom.addr = (caddr_t) appData.darkSquareColor;
1861 vFrom.size = strlen(appData.darkSquareColor);
1862 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1863 if (vTo.addr == NULL) {
1864 appData.monoMode = True;
1867 darkSquareColor = *(Pixel *) vTo.addr;
1870 if (!appData.monoMode) {
1871 vFrom.addr = (caddr_t) appData.whitePieceColor;
1872 vFrom.size = strlen(appData.whitePieceColor);
1873 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1874 if (vTo.addr == NULL) {
1875 appData.monoMode = True;
1878 whitePieceColor = *(Pixel *) vTo.addr;
1881 if (!appData.monoMode) {
1882 vFrom.addr = (caddr_t) appData.blackPieceColor;
1883 vFrom.size = strlen(appData.blackPieceColor);
1884 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1885 if (vTo.addr == NULL) {
1886 appData.monoMode = True;
1889 blackPieceColor = *(Pixel *) vTo.addr;
1893 if (!appData.monoMode) {
1894 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1895 vFrom.size = strlen(appData.highlightSquareColor);
1896 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1897 if (vTo.addr == NULL) {
1898 appData.monoMode = True;
1901 highlightSquareColor = *(Pixel *) vTo.addr;
1905 if (!appData.monoMode) {
1906 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1907 vFrom.size = strlen(appData.premoveHighlightColor);
1908 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1909 if (vTo.addr == NULL) {
1910 appData.monoMode = True;
1913 premoveHighlightColor = *(Pixel *) vTo.addr;
1921 { // [HGM] taken out of main
1923 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1924 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1925 appData.bitmapDirectory = DEF_BITMAP_DIR;
1927 if (appData.bitmapDirectory[0] != NULLCHAR) {
1931 CreateXPMBoard(appData.liteBackTextureFile, 1);
1932 CreateXPMBoard(appData.darkBackTextureFile, 0);
1936 /* Create regular pieces */
1937 if (!useImages) CreatePieces();
1946 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1947 XSetWindowAttributes window_attributes;
1949 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1950 XrmValue vFrom, vTo;
1951 XtGeometryResult gres;
1954 int forceMono = False;
1956 srandom(time(0)); // [HGM] book: make random truly random
1958 setbuf(stdout, NULL);
1959 setbuf(stderr, NULL);
1962 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1963 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1967 programName = strrchr(argv[0], '/');
1968 if (programName == NULL)
1969 programName = argv[0];
1974 XtSetLanguageProc(NULL, NULL, NULL);
1975 bindtextdomain(PACKAGE, LOCALEDIR);
1976 textdomain(PACKAGE);
1980 XtAppInitialize(&appContext, "XBoard", shellOptions,
1981 XtNumber(shellOptions),
1982 &argc, argv, xboardResources, NULL, 0);
1983 appData.boardSize = "";
1984 InitAppData(ConvertToLine(argc, argv));
1986 if (p == NULL) p = "/tmp";
1987 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1988 gameCopyFilename = (char*) malloc(i);
1989 gamePasteFilename = (char*) malloc(i);
1990 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1991 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1993 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1994 clientResources, XtNumber(clientResources),
1997 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1998 static char buf[MSG_SIZ];
1999 EscapeExpand(buf, appData.firstInitString);
2000 appData.firstInitString = strdup(buf);
2001 EscapeExpand(buf, appData.secondInitString);
2002 appData.secondInitString = strdup(buf);
2003 EscapeExpand(buf, appData.firstComputerString);
2004 appData.firstComputerString = strdup(buf);
2005 EscapeExpand(buf, appData.secondComputerString);
2006 appData.secondComputerString = strdup(buf);
2009 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2012 if (chdir(chessDir) != 0) {
2013 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2019 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2020 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2021 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2022 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2025 setbuf(debugFP, NULL);
2029 if (appData.debugMode) {
2030 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2034 /* [HGM,HR] make sure board size is acceptable */
2035 if(appData.NrFiles > BOARD_FILES ||
2036 appData.NrRanks > BOARD_RANKS )
2037 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2040 /* This feature does not work; animation needs a rewrite */
2041 appData.highlightDragging = FALSE;
2045 xDisplay = XtDisplay(shellWidget);
2046 xScreen = DefaultScreen(xDisplay);
2047 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2049 gameInfo.variant = StringToVariant(appData.variant);
2050 InitPosition(FALSE);
2053 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2055 if (isdigit(appData.boardSize[0])) {
2056 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2057 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2058 &fontPxlSize, &smallLayout, &tinyLayout);
2060 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2061 programName, appData.boardSize);
2065 /* Find some defaults; use the nearest known size */
2066 SizeDefaults *szd, *nearest;
2067 int distance = 99999;
2068 nearest = szd = sizeDefaults;
2069 while (szd->name != NULL) {
2070 if (abs(szd->squareSize - squareSize) < distance) {
2072 distance = abs(szd->squareSize - squareSize);
2073 if (distance == 0) break;
2077 if (i < 2) lineGap = nearest->lineGap;
2078 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2079 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2080 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2081 if (i < 6) smallLayout = nearest->smallLayout;
2082 if (i < 7) tinyLayout = nearest->tinyLayout;
2085 SizeDefaults *szd = sizeDefaults;
2086 if (*appData.boardSize == NULLCHAR) {
2087 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2088 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2091 if (szd->name == NULL) szd--;
2092 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2094 while (szd->name != NULL &&
2095 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2096 if (szd->name == NULL) {
2097 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2098 programName, appData.boardSize);
2102 squareSize = szd->squareSize;
2103 lineGap = szd->lineGap;
2104 clockFontPxlSize = szd->clockFontPxlSize;
2105 coordFontPxlSize = szd->coordFontPxlSize;
2106 fontPxlSize = szd->fontPxlSize;
2107 smallLayout = szd->smallLayout;
2108 tinyLayout = szd->tinyLayout;
2109 // [HGM] font: use defaults from settings file if available and not overruled
2111 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2112 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2113 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2114 appData.font = fontTable[MESSAGE_FONT][squareSize];
2115 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2116 appData.coordFont = fontTable[COORD_FONT][squareSize];
2118 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2119 if (strlen(appData.pixmapDirectory) > 0) {
2120 p = ExpandPathName(appData.pixmapDirectory);
2122 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2123 appData.pixmapDirectory);
2126 if (appData.debugMode) {
2127 fprintf(stderr, _("\
2128 XBoard square size (hint): %d\n\
2129 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2131 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2132 if (appData.debugMode) {
2133 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2136 defaultLineGap = lineGap;
2137 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2139 /* [HR] height treated separately (hacked) */
2140 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2141 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2142 if (appData.showJail == 1) {
2143 /* Jail on top and bottom */
2144 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2145 XtSetArg(boardArgs[2], XtNheight,
2146 boardHeight + 2*(lineGap + squareSize));
2147 } else if (appData.showJail == 2) {
2149 XtSetArg(boardArgs[1], XtNwidth,
2150 boardWidth + 2*(lineGap + squareSize));
2151 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2154 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2155 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2159 * Determine what fonts to use.
2162 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2163 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2164 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2165 fontSet = CreateFontSet(appData.font);
2166 clockFontSet = CreateFontSet(appData.clockFont);
2168 /* For the coordFont, use the 0th font of the fontset. */
2169 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2170 XFontStruct **font_struct_list;
2171 char **font_name_list;
2172 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2173 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2174 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2177 appData.font = FindFont(appData.font, fontPxlSize);
2178 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2179 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2180 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2181 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2182 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2183 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2185 countFontID = coordFontID; // [HGM] holdings
2186 countFontStruct = coordFontStruct;
2188 xdb = XtDatabase(xDisplay);
2190 XrmPutLineResource(&xdb, "*international: True");
2191 vTo.size = sizeof(XFontSet);
2192 vTo.addr = (XtPointer) &fontSet;
2193 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2195 XrmPutStringResource(&xdb, "*font", appData.font);
2199 * Detect if there are not enough colors available and adapt.
2201 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2202 appData.monoMode = True;
2205 forceMono = MakeColors();
2208 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2210 appData.monoMode = True;
2213 if (appData.lowTimeWarning && !appData.monoMode) {
2214 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2215 vFrom.size = strlen(appData.lowTimeWarningColor);
2216 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2217 if (vTo.addr == NULL)
2218 appData.monoMode = True;
2220 lowTimeWarningColor = *(Pixel *) vTo.addr;
2223 if (appData.monoMode && appData.debugMode) {
2224 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2225 (unsigned long) XWhitePixel(xDisplay, xScreen),
2226 (unsigned long) XBlackPixel(xDisplay, xScreen));
2229 ParseIcsTextColors();
2230 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2231 textColors[ColorNone].attr = 0;
2233 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2239 layoutName = "tinyLayout";
2240 } else if (smallLayout) {
2241 layoutName = "smallLayout";
2243 layoutName = "normalLayout";
2245 /* Outer layoutWidget is there only to provide a name for use in
2246 resources that depend on the layout style */
2248 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2249 layoutArgs, XtNumber(layoutArgs));
2251 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2252 formArgs, XtNumber(formArgs));
2253 XtSetArg(args[0], XtNdefaultDistance, &sep);
2254 XtGetValues(formWidget, args, 1);
2257 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2258 XtSetArg(args[0], XtNtop, XtChainTop);
2259 XtSetArg(args[1], XtNbottom, XtChainTop);
2260 XtSetArg(args[2], XtNright, XtChainLeft);
2261 XtSetValues(menuBarWidget, args, 3);
2263 widgetList[j++] = whiteTimerWidget =
2264 XtCreateWidget("whiteTime", labelWidgetClass,
2265 formWidget, timerArgs, XtNumber(timerArgs));
2267 XtSetArg(args[0], XtNfontSet, clockFontSet);
2269 XtSetArg(args[0], XtNfont, clockFontStruct);
2271 XtSetArg(args[1], XtNtop, XtChainTop);
2272 XtSetArg(args[2], XtNbottom, XtChainTop);
2273 XtSetValues(whiteTimerWidget, args, 3);
2275 widgetList[j++] = blackTimerWidget =
2276 XtCreateWidget("blackTime", labelWidgetClass,
2277 formWidget, timerArgs, XtNumber(timerArgs));
2279 XtSetArg(args[0], XtNfontSet, clockFontSet);
2281 XtSetArg(args[0], XtNfont, clockFontStruct);
2283 XtSetArg(args[1], XtNtop, XtChainTop);
2284 XtSetArg(args[2], XtNbottom, XtChainTop);
2285 XtSetValues(blackTimerWidget, args, 3);
2287 if (appData.titleInWindow) {
2288 widgetList[j++] = titleWidget =
2289 XtCreateWidget("title", labelWidgetClass, formWidget,
2290 titleArgs, XtNumber(titleArgs));
2291 XtSetArg(args[0], XtNtop, XtChainTop);
2292 XtSetArg(args[1], XtNbottom, XtChainTop);
2293 XtSetValues(titleWidget, args, 2);
2296 if (appData.showButtonBar) {
2297 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2298 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2299 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2300 XtSetArg(args[2], XtNtop, XtChainTop);
2301 XtSetArg(args[3], XtNbottom, XtChainTop);
2302 XtSetValues(buttonBarWidget, args, 4);
2305 widgetList[j++] = messageWidget =
2306 XtCreateWidget("message", labelWidgetClass, formWidget,
2307 messageArgs, XtNumber(messageArgs));
2308 XtSetArg(args[0], XtNtop, XtChainTop);
2309 XtSetArg(args[1], XtNbottom, XtChainTop);
2310 XtSetValues(messageWidget, args, 2);
2312 widgetList[j++] = boardWidget =
2313 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2314 XtNumber(boardArgs));
2316 XtManageChildren(widgetList, j);
2318 timerWidth = (boardWidth - sep) / 2;
2319 XtSetArg(args[0], XtNwidth, timerWidth);
2320 XtSetValues(whiteTimerWidget, args, 1);
2321 XtSetValues(blackTimerWidget, args, 1);
2323 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2324 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2325 XtGetValues(whiteTimerWidget, args, 2);
2327 if (appData.showButtonBar) {
2328 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2329 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2330 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2334 * formWidget uses these constraints but they are stored
2338 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2339 XtSetValues(menuBarWidget, args, i);
2340 if (appData.titleInWindow) {
2343 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2344 XtSetValues(whiteTimerWidget, args, i);
2346 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2347 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2348 XtSetValues(blackTimerWidget, args, i);
2350 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2351 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2352 XtSetValues(titleWidget, args, i);
2354 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2355 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2356 XtSetValues(messageWidget, args, i);
2357 if (appData.showButtonBar) {
2359 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2360 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2361 XtSetValues(buttonBarWidget, args, i);
2365 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2366 XtSetValues(whiteTimerWidget, args, i);
2368 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2369 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2370 XtSetValues(blackTimerWidget, args, i);
2372 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2373 XtSetValues(titleWidget, args, i);
2375 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2376 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2377 XtSetValues(messageWidget, args, i);
2378 if (appData.showButtonBar) {
2380 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2381 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2382 XtSetValues(buttonBarWidget, args, i);
2387 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2388 XtSetValues(whiteTimerWidget, args, i);
2390 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2391 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2392 XtSetValues(blackTimerWidget, args, i);
2394 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2395 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2396 XtSetValues(messageWidget, args, i);
2397 if (appData.showButtonBar) {
2399 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2400 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2401 XtSetValues(buttonBarWidget, args, i);
2405 XtSetArg(args[0], XtNfromVert, messageWidget);
2406 XtSetArg(args[1], XtNtop, XtChainTop);
2407 XtSetArg(args[2], XtNbottom, XtChainBottom);
2408 XtSetArg(args[3], XtNleft, XtChainLeft);
2409 XtSetArg(args[4], XtNright, XtChainRight);
2410 XtSetValues(boardWidget, args, 5);
2412 XtRealizeWidget(shellWidget);
2415 XtSetArg(args[0], XtNx, wpMain.x);
2416 XtSetArg(args[1], XtNy, wpMain.y);
2417 XtSetValues(shellWidget, args, 2);
2421 * Correct the width of the message and title widgets.
2422 * It is not known why some systems need the extra fudge term.
2423 * The value "2" is probably larger than needed.
2425 XawFormDoLayout(formWidget, False);
2427 #define WIDTH_FUDGE 2
2429 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2430 XtSetArg(args[i], XtNheight, &h); i++;
2431 XtGetValues(messageWidget, args, i);
2432 if (appData.showButtonBar) {
2434 XtSetArg(args[i], XtNwidth, &w); i++;
2435 XtGetValues(buttonBarWidget, args, i);
2436 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2438 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2441 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2442 if (gres != XtGeometryYes && appData.debugMode) {
2443 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2444 programName, gres, w, h, wr, hr);
2447 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2448 /* The size used for the child widget in layout lags one resize behind
2449 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2451 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2452 if (gres != XtGeometryYes && appData.debugMode) {
2453 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2454 programName, gres, w, h, wr, hr);
2457 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2458 XtSetArg(args[1], XtNright, XtChainRight);
2459 XtSetValues(messageWidget, args, 2);
2461 if (appData.titleInWindow) {
2463 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2464 XtSetArg(args[i], XtNheight, &h); i++;
2465 XtGetValues(titleWidget, args, i);
2467 w = boardWidth - 2*bor;
2469 XtSetArg(args[0], XtNwidth, &w);
2470 XtGetValues(menuBarWidget, args, 1);
2471 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2474 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2475 if (gres != XtGeometryYes && appData.debugMode) {
2477 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2478 programName, gres, w, h, wr, hr);
2481 XawFormDoLayout(formWidget, True);
2483 xBoardWindow = XtWindow(boardWidget);
2485 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2486 // not need to go into InitDrawingSizes().
2490 * Create X checkmark bitmap and initialize option menu checks.
2492 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2493 checkmark_bits, checkmark_width, checkmark_height);
2494 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2495 #ifndef OPTIONSDIALOG
2496 if (appData.alwaysPromoteToQueen) {
2497 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2500 if (appData.animateDragging) {
2501 XtSetValues(XtNameToWidget(menuBarWidget,
2502 "menuOptions.Animate Dragging"),
2505 if (appData.animate) {
2506 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2509 if (appData.autoCallFlag) {
2510 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2513 if (appData.autoFlipView) {
2514 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2517 if (appData.blindfold) {
2518 XtSetValues(XtNameToWidget(menuBarWidget,
2519 "menuOptions.Blindfold"), args, 1);
2521 if (appData.flashCount > 0) {
2522 XtSetValues(XtNameToWidget(menuBarWidget,
2523 "menuOptions.Flash Moves"),
2527 if (appData.highlightDragging) {
2528 XtSetValues(XtNameToWidget(menuBarWidget,
2529 "menuOptions.Highlight Dragging"),
2533 if (appData.highlightLastMove) {
2534 XtSetValues(XtNameToWidget(menuBarWidget,
2535 "menuOptions.Highlight Last Move"),
2538 if (appData.highlightMoveWithArrow) {
2539 XtSetValues(XtNameToWidget(menuBarWidget,
2540 "menuOptions.Arrow"),
2543 // if (appData.icsAlarm) {
2544 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2547 if (appData.ringBellAfterMoves) {
2548 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2551 if (appData.oneClick) {
2552 XtSetValues(XtNameToWidget(menuBarWidget,
2553 "menuOptions.OneClick"), args, 1);
2555 if (appData.periodicUpdates) {
2556 XtSetValues(XtNameToWidget(menuBarWidget,
2557 "menuOptions.Periodic Updates"), args, 1);
2559 if (appData.ponderNextMove) {
2560 XtSetValues(XtNameToWidget(menuBarWidget,
2561 "menuOptions.Ponder Next Move"), args, 1);
2563 if (appData.popupExitMessage) {
2564 XtSetValues(XtNameToWidget(menuBarWidget,
2565 "menuOptions.Popup Exit Message"), args, 1);
2567 if (appData.popupMoveErrors) {
2568 XtSetValues(XtNameToWidget(menuBarWidget,
2569 "menuOptions.Popup Move Errors"), args, 1);
2571 // if (appData.premove) {
2572 // XtSetValues(XtNameToWidget(menuBarWidget,
2573 // "menuOptions.Premove"), args, 1);
2575 if (appData.showCoords) {
2576 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2579 if (appData.hideThinkingFromHuman) {
2580 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2583 if (appData.testLegality) {
2584 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2588 if (saveSettingsOnExit) {
2589 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2596 ReadBitmap(&wIconPixmap, "icon_white.bm",
2597 icon_white_bits, icon_white_width, icon_white_height);
2598 ReadBitmap(&bIconPixmap, "icon_black.bm",
2599 icon_black_bits, icon_black_width, icon_black_height);
2600 iconPixmap = wIconPixmap;
2602 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2603 XtSetValues(shellWidget, args, i);
2606 * Create a cursor for the board widget.
2608 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2609 XChangeWindowAttributes(xDisplay, xBoardWindow,
2610 CWCursor, &window_attributes);
2613 * Inhibit shell resizing.
2615 shellArgs[0].value = (XtArgVal) &w;
2616 shellArgs[1].value = (XtArgVal) &h;
2617 XtGetValues(shellWidget, shellArgs, 2);
2618 shellArgs[4].value = shellArgs[2].value = w;
2619 shellArgs[5].value = shellArgs[3].value = h;
2620 XtSetValues(shellWidget, &shellArgs[2], 4);
2621 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2622 marginH = h - boardHeight;
2624 CatchDeleteWindow(shellWidget, "QuitProc");
2632 if (appData.animate || appData.animateDragging)
2635 XtAugmentTranslations(formWidget,
2636 XtParseTranslationTable(globalTranslations));
2637 XtAugmentTranslations(boardWidget,
2638 XtParseTranslationTable(boardTranslations));
2639 XtAugmentTranslations(whiteTimerWidget,
2640 XtParseTranslationTable(whiteTranslations));
2641 XtAugmentTranslations(blackTimerWidget,
2642 XtParseTranslationTable(blackTranslations));
2644 /* Why is the following needed on some versions of X instead
2645 * of a translation? */
2646 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2647 (XtEventHandler) EventProc, NULL);
2649 XtAddEventHandler(formWidget, KeyPressMask, False,
2650 (XtEventHandler) MoveTypeInProc, NULL);
2652 /* [AS] Restore layout */
2653 if( wpMoveHistory.visible ) {
2657 if( wpEvalGraph.visible )
2662 if( wpEngineOutput.visible ) {
2663 EngineOutputPopUp();
2668 if (errorExitStatus == -1) {
2669 if (appData.icsActive) {
2670 /* We now wait until we see "login:" from the ICS before
2671 sending the logon script (problems with timestamp otherwise) */
2672 /*ICSInitScript();*/
2673 if (appData.icsInputBox) ICSInputBoxPopUp();
2677 signal(SIGWINCH, TermSizeSigHandler);
2679 signal(SIGINT, IntSigHandler);
2680 signal(SIGTERM, IntSigHandler);
2681 if (*appData.cmailGameName != NULLCHAR) {
2682 signal(SIGUSR1, CmailSigHandler);
2685 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2687 // XtSetKeyboardFocus(shellWidget, formWidget);
2688 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2690 XtAppMainLoop(appContext);
2691 if (appData.debugMode) fclose(debugFP); // [DM] debug
2698 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2699 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2701 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2702 unlink(gameCopyFilename);
2703 unlink(gamePasteFilename);
2706 RETSIGTYPE TermSizeSigHandler(int sig)
2719 CmailSigHandler(sig)
2725 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2727 /* Activate call-back function CmailSigHandlerCallBack() */
2728 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2730 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2734 CmailSigHandlerCallBack(isr, closure, message, count, error)
2742 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2744 /**** end signal code ****/
2750 /* try to open the icsLogon script, either in the location given
2751 * or in the users HOME directory
2758 f = fopen(appData.icsLogon, "r");
2761 homedir = getenv("HOME");
2762 if (homedir != NULL)
2764 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2765 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2766 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2767 f = fopen(buf, "r");
2772 ProcessICSInitScript(f);
2774 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2797 if (!menuBarWidget) return;
2798 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2800 DisplayError("menuEdit.Revert", 0);
2802 XtSetSensitive(w, !grey);
2804 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2806 DisplayError("menuEdit.Annotate", 0);
2808 XtSetSensitive(w, !grey);
2813 SetMenuEnables(enab)
2817 if (!menuBarWidget) return;
2818 while (enab->name != NULL) {
2819 w = XtNameToWidget(menuBarWidget, enab->name);
2821 DisplayError(enab->name, 0);
2823 XtSetSensitive(w, enab->value);
2829 Enables icsEnables[] = {
2830 { "menuFile.Mail Move", False },
2831 { "menuFile.Reload CMail Message", False },
2832 { "menuMode.Machine Black", False },
2833 { "menuMode.Machine White", False },
2834 { "menuMode.Analysis Mode", False },
2835 { "menuMode.Analyze File", False },
2836 { "menuMode.Two Machines", False },
2837 { "menuMode.Machine Match", False },
2839 { "menuEngine.Hint", False },
2840 { "menuEngine.Book", False },
2841 { "menuEngine.Move Now", False },
2842 #ifndef OPTIONSDIALOG
2843 { "menuOptions.Periodic Updates", False },
2844 { "menuOptions.Hide Thinking", False },
2845 { "menuOptions.Ponder Next Move", False },
2848 { "menuEngine.Engine #1 Settings", False },
2849 { "menuEngine.Engine #2 Settings", False },
2850 { "menuEngine.Load Engine", False },
2851 { "menuEdit.Annotate", False },
2852 { "menuOptions.Match", False },
2856 Enables ncpEnables[] = {
2857 { "menuFile.Mail Move", False },
2858 { "menuFile.Reload CMail Message", False },
2859 { "menuMode.Machine White", False },
2860 { "menuMode.Machine Black", False },
2861 { "menuMode.Analysis Mode", False },
2862 { "menuMode.Analyze File", False },
2863 { "menuMode.Two Machines", False },
2864 { "menuMode.Machine Match", False },
2865 { "menuMode.ICS Client", False },
2866 { "menuView.ICStex", False },
2867 { "menuView.ICS Input Box", False },
2868 { "Action", False },
2869 { "menuEdit.Revert", False },
2870 { "menuEdit.Annotate", False },
2871 { "menuEngine.Engine #1 Settings", False },
2872 { "menuEngine.Engine #2 Settings", False },
2873 { "menuEngine.Move Now", False },
2874 { "menuEngine.Retract Move", False },
2875 { "menuOptions.ICS", False },
2876 #ifndef OPTIONSDIALOG
2877 { "menuOptions.Auto Flag", False },
2878 { "menuOptions.Auto Flip View", False },
2879 // { "menuOptions.ICS Alarm", False },
2880 { "menuOptions.Move Sound", False },
2881 { "menuOptions.Hide Thinking", False },
2882 { "menuOptions.Periodic Updates", False },
2883 { "menuOptions.Ponder Next Move", False },
2885 { "menuEngine.Hint", False },
2886 { "menuEngine.Book", False },
2890 Enables gnuEnables[] = {
2891 { "menuMode.ICS Client", False },
2892 { "menuView.ICStex", False },
2893 { "menuView.ICS Input Box", False },
2894 { "menuAction.Accept", False },
2895 { "menuAction.Decline", False },
2896 { "menuAction.Rematch", False },
2897 { "menuAction.Adjourn", False },
2898 { "menuAction.Stop Examining", False },
2899 { "menuAction.Stop Observing", False },
2900 { "menuAction.Upload to Examine", False },
2901 { "menuEdit.Revert", False },
2902 { "menuEdit.Annotate", False },
2903 { "menuOptions.ICS", False },
2905 /* The next two options rely on SetCmailMode being called *after* */
2906 /* SetGNUMode so that when GNU is being used to give hints these */
2907 /* menu options are still available */
2909 { "menuFile.Mail Move", False },
2910 { "menuFile.Reload CMail Message", False },
2911 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2912 { "menuMode.Machine White", True },
2913 { "menuMode.Machine Black", True },
2914 { "menuMode.Analysis Mode", True },
2915 { "menuMode.Analyze File", True },
2916 { "menuMode.Two Machines", True },
2917 { "menuMode.Machine Match", True },
2918 { "menuEngine.Engine #1 Settings", True },
2919 { "menuEngine.Engine #2 Settings", True },
2920 { "menuEngine.Hint", True },
2921 { "menuEngine.Book", True },
2922 { "menuEngine.Move Now", True },
2923 { "menuEngine.Retract Move", True },
2928 Enables cmailEnables[] = {
2930 { "menuAction.Call Flag", False },
2931 { "menuAction.Draw", True },
2932 { "menuAction.Adjourn", False },
2933 { "menuAction.Abort", False },
2934 { "menuAction.Stop Observing", False },
2935 { "menuAction.Stop Examining", False },
2936 { "menuFile.Mail Move", True },
2937 { "menuFile.Reload CMail Message", True },
2941 Enables trainingOnEnables[] = {
2942 { "menuMode.Edit Comment", False },
2943 { "menuMode.Pause", False },
2944 { "menuEdit.Forward", False },
2945 { "menuEdit.Backward", False },
2946 { "menuEdit.Forward to End", False },
2947 { "menuEdit.Back to Start", False },
2948 { "menuEngine.Move Now", False },
2949 { "menuEdit.Truncate Game", False },
2953 Enables trainingOffEnables[] = {
2954 { "menuMode.Edit Comment", True },
2955 { "menuMode.Pause", True },
2956 { "menuEdit.Forward", True },
2957 { "menuEdit.Backward", True },
2958 { "menuEdit.Forward to End", True },
2959 { "menuEdit.Back to Start", True },
2960 { "menuEngine.Move Now", True },
2961 { "menuEdit.Truncate Game", True },
2965 Enables machineThinkingEnables[] = {
2966 { "menuFile.Load Game", False },
2967 // { "menuFile.Load Next Game", False },
2968 // { "menuFile.Load Previous Game", False },
2969 // { "menuFile.Reload Same Game", False },
2970 { "menuEdit.Paste Game", False },
2971 { "menuFile.Load Position", False },
2972 // { "menuFile.Load Next Position", False },
2973 // { "menuFile.Load Previous Position", False },
2974 // { "menuFile.Reload Same Position", False },
2975 { "menuEdit.Paste Position", False },
2976 { "menuMode.Machine White", False },
2977 { "menuMode.Machine Black", False },
2978 { "menuMode.Two Machines", False },
2979 // { "menuMode.Machine Match", False },
2980 { "menuEngine.Retract Move", False },
2984 Enables userThinkingEnables[] = {
2985 { "menuFile.Load Game", True },
2986 // { "menuFile.Load Next Game", True },
2987 // { "menuFile.Load Previous Game", True },
2988 // { "menuFile.Reload Same Game", True },
2989 { "menuEdit.Paste Game", True },
2990 { "menuFile.Load Position", True },
2991 // { "menuFile.Load Next Position", True },
2992 // { "menuFile.Load Previous Position", True },
2993 // { "menuFile.Reload Same Position", True },
2994 { "menuEdit.Paste Position", True },
2995 { "menuMode.Machine White", True },
2996 { "menuMode.Machine Black", True },
2997 { "menuMode.Two Machines", True },
2998 // { "menuMode.Machine Match", True },
2999 { "menuEngine.Retract Move", True },
3005 SetMenuEnables(icsEnables);
3008 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3009 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3010 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3018 SetMenuEnables(ncpEnables);
3024 SetMenuEnables(gnuEnables);
3030 SetMenuEnables(cmailEnables);
3036 SetMenuEnables(trainingOnEnables);
3037 if (appData.showButtonBar) {
3038 XtSetSensitive(buttonBarWidget, False);
3044 SetTrainingModeOff()
3046 SetMenuEnables(trainingOffEnables);
3047 if (appData.showButtonBar) {
3048 XtSetSensitive(buttonBarWidget, True);
3053 SetUserThinkingEnables()
3055 if (appData.noChessProgram) return;
3056 SetMenuEnables(userThinkingEnables);
3060 SetMachineThinkingEnables()
3062 if (appData.noChessProgram) return;
3063 SetMenuEnables(machineThinkingEnables);
3065 case MachinePlaysBlack:
3066 case MachinePlaysWhite:
3067 case TwoMachinesPlay:
3068 XtSetSensitive(XtNameToWidget(menuBarWidget,
3069 ModeToWidgetName(gameMode)), True);
3076 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3077 #define HISTORY_SIZE 64
3078 static char *history[HISTORY_SIZE];
3079 int histIn = 0, histP = 0;
3082 SaveInHistory(char *cmd)
3084 if (history[histIn] != NULL) {
3085 free(history[histIn]);
3086 history[histIn] = NULL;
3088 if (*cmd == NULLCHAR) return;
3089 history[histIn] = StrSave(cmd);
3090 histIn = (histIn + 1) % HISTORY_SIZE;
3091 if (history[histIn] != NULL) {
3092 free(history[histIn]);
3093 history[histIn] = NULL;
3099 PrevInHistory(char *cmd)
3102 if (histP == histIn) {
3103 if (history[histIn] != NULL) free(history[histIn]);
3104 history[histIn] = StrSave(cmd);
3106 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3107 if (newhp == histIn || history[newhp] == NULL) return NULL;
3109 return history[histP];
3115 if (histP == histIn) return NULL;
3116 histP = (histP + 1) % HISTORY_SIZE;
3117 return history[histP];
3119 // end of borrowed code
3121 #define Abs(n) ((n)<0 ? -(n) : (n))
3125 InsertPxlSize(pattern, targetPxlSize)
3129 char *base_fnt_lst, strInt[12], *p, *q;
3130 int alternatives, i, len, strIntLen;
3133 * Replace the "*" (if present) in the pixel-size slot of each
3134 * alternative with the targetPxlSize.
3138 while ((p = strchr(p, ',')) != NULL) {
3142 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3143 strIntLen = strlen(strInt);
3144 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3148 while (alternatives--) {
3149 char *comma = strchr(p, ',');
3150 for (i=0; i<14; i++) {
3151 char *hyphen = strchr(p, '-');
3153 if (comma && hyphen > comma) break;
3154 len = hyphen + 1 - p;
3155 if (i == 7 && *p == '*' && len == 2) {
3157 memcpy(q, strInt, strIntLen);
3167 len = comma + 1 - p;
3174 return base_fnt_lst;
3178 CreateFontSet(base_fnt_lst)
3182 char **missing_list;
3186 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3187 &missing_list, &missing_count, &def_string);
3188 if (appData.debugMode) {
3190 XFontStruct **font_struct_list;
3191 char **font_name_list;
3192 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3194 fprintf(debugFP, " got list %s, locale %s\n",
3195 XBaseFontNameListOfFontSet(fntSet),
3196 XLocaleOfFontSet(fntSet));
3197 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3198 for (i = 0; i < count; i++) {
3199 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3202 for (i = 0; i < missing_count; i++) {
3203 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3206 if (fntSet == NULL) {
3207 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3212 #else // not ENABLE_NLS
3214 * Find a font that matches "pattern" that is as close as
3215 * possible to the targetPxlSize. Prefer fonts that are k
3216 * pixels smaller to fonts that are k pixels larger. The
3217 * pattern must be in the X Consortium standard format,
3218 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3219 * The return value should be freed with XtFree when no
3223 FindFont(pattern, targetPxlSize)
3227 char **fonts, *p, *best, *scalable, *scalableTail;
3228 int i, j, nfonts, minerr, err, pxlSize;
3230 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3232 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3233 programName, pattern);
3240 for (i=0; i<nfonts; i++) {
3243 if (*p != '-') continue;
3245 if (*p == NULLCHAR) break;
3246 if (*p++ == '-') j++;
3248 if (j < 7) continue;
3251 scalable = fonts[i];
3254 err = pxlSize - targetPxlSize;
3255 if (Abs(err) < Abs(minerr) ||
3256 (minerr > 0 && err < 0 && -err == minerr)) {
3262 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3263 /* If the error is too big and there is a scalable font,
3264 use the scalable font. */
3265 int headlen = scalableTail - scalable;
3266 p = (char *) XtMalloc(strlen(scalable) + 10);
3267 while (isdigit(*scalableTail)) scalableTail++;
3268 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3270 p = (char *) XtMalloc(strlen(best) + 2);
3271 safeStrCpy(p, best, strlen(best)+1 );
3273 if (appData.debugMode) {
3274 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3275 pattern, targetPxlSize, p);
3277 XFreeFontNames(fonts);
3283 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3284 // must be called before all non-first callse to CreateGCs()
3285 XtReleaseGC(shellWidget, highlineGC);
3286 XtReleaseGC(shellWidget, lightSquareGC);
3287 XtReleaseGC(shellWidget, darkSquareGC);
3288 XtReleaseGC(shellWidget, lineGC);
3289 if (appData.monoMode) {
3290 if (DefaultDepth(xDisplay, xScreen) == 1) {
3291 XtReleaseGC(shellWidget, wbPieceGC);
3293 XtReleaseGC(shellWidget, bwPieceGC);
3296 XtReleaseGC(shellWidget, prelineGC);
3297 XtReleaseGC(shellWidget, jailSquareGC);
3298 XtReleaseGC(shellWidget, wdPieceGC);
3299 XtReleaseGC(shellWidget, wlPieceGC);
3300 XtReleaseGC(shellWidget, wjPieceGC);
3301 XtReleaseGC(shellWidget, bdPieceGC);
3302 XtReleaseGC(shellWidget, blPieceGC);
3303 XtReleaseGC(shellWidget, bjPieceGC);
3307 void CreateGCs(int redo)
3309 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3310 | GCBackground | GCFunction | GCPlaneMask;
3311 XGCValues gc_values;
3314 gc_values.plane_mask = AllPlanes;
3315 gc_values.line_width = lineGap;
3316 gc_values.line_style = LineSolid;
3317 gc_values.function = GXcopy;
3320 DeleteGCs(); // called a second time; clean up old GCs first
3321 } else { // [HGM] grid and font GCs created on first call only
3322 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3323 gc_values.background = XWhitePixel(xDisplay, xScreen);
3324 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3325 XSetFont(xDisplay, coordGC, coordFontID);
3327 // [HGM] make font for holdings counts (white on black)
3328 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3329 gc_values.background = XBlackPixel(xDisplay, xScreen);
3330 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3331 XSetFont(xDisplay, countGC, countFontID);
3333 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3334 gc_values.background = XBlackPixel(xDisplay, xScreen);
3335 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3337 if (appData.monoMode) {
3338 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3339 gc_values.background = XWhitePixel(xDisplay, xScreen);
3340 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3342 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3343 gc_values.background = XBlackPixel(xDisplay, xScreen);
3344 lightSquareGC = wbPieceGC
3345 = XtGetGC(shellWidget, value_mask, &gc_values);
3347 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3348 gc_values.background = XWhitePixel(xDisplay, xScreen);
3349 darkSquareGC = bwPieceGC
3350 = XtGetGC(shellWidget, value_mask, &gc_values);
3352 if (DefaultDepth(xDisplay, xScreen) == 1) {
3353 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3354 gc_values.function = GXcopyInverted;
3355 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3356 gc_values.function = GXcopy;
3357 if (XBlackPixel(xDisplay, xScreen) == 1) {
3358 bwPieceGC = darkSquareGC;
3359 wbPieceGC = copyInvertedGC;
3361 bwPieceGC = copyInvertedGC;
3362 wbPieceGC = lightSquareGC;
3366 gc_values.foreground = highlightSquareColor;
3367 gc_values.background = highlightSquareColor;
3368 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3370 gc_values.foreground = premoveHighlightColor;
3371 gc_values.background = premoveHighlightColor;
3372 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3374 gc_values.foreground = lightSquareColor;
3375 gc_values.background = darkSquareColor;
3376 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3378 gc_values.foreground = darkSquareColor;
3379 gc_values.background = lightSquareColor;
3380 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3382 gc_values.foreground = jailSquareColor;
3383 gc_values.background = jailSquareColor;
3384 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3386 gc_values.foreground = whitePieceColor;
3387 gc_values.background = darkSquareColor;
3388 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3390 gc_values.foreground = whitePieceColor;
3391 gc_values.background = lightSquareColor;
3392 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3394 gc_values.foreground = whitePieceColor;
3395 gc_values.background = jailSquareColor;
3396 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3398 gc_values.foreground = blackPieceColor;
3399 gc_values.background = darkSquareColor;
3400 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3402 gc_values.foreground = blackPieceColor;
3403 gc_values.background = lightSquareColor;
3404 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3406 gc_values.foreground = blackPieceColor;
3407 gc_values.background = jailSquareColor;
3408 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3412 void loadXIM(xim, xmask, filename, dest, mask)
3425 fp = fopen(filename, "rb");
3427 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3434 for (y=0; y<h; ++y) {
3435 for (x=0; x<h; ++x) {
3440 XPutPixel(xim, x, y, blackPieceColor);
3442 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3445 XPutPixel(xim, x, y, darkSquareColor);
3447 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3450 XPutPixel(xim, x, y, whitePieceColor);
3452 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3455 XPutPixel(xim, x, y, lightSquareColor);
3457 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3465 /* create Pixmap of piece */
3466 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3468 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3471 /* create Pixmap of clipmask
3472 Note: We assume the white/black pieces have the same
3473 outline, so we make only 6 masks. This is okay
3474 since the XPM clipmask routines do the same. */
3476 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3478 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3481 /* now create the 1-bit version */
3482 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3485 values.foreground = 1;
3486 values.background = 0;
3488 /* Don't use XtGetGC, not read only */
3489 maskGC = XCreateGC(xDisplay, *mask,
3490 GCForeground | GCBackground, &values);
3491 XCopyPlane(xDisplay, temp, *mask, maskGC,
3492 0, 0, squareSize, squareSize, 0, 0, 1);
3493 XFreePixmap(xDisplay, temp);
3498 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3500 void CreateXIMPieces()
3505 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3510 /* The XSynchronize calls were copied from CreatePieces.
3511 Not sure if needed, but can't hurt */
3512 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3515 /* temp needed by loadXIM() */
3516 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3517 0, 0, ss, ss, AllPlanes, XYPixmap);
3519 if (strlen(appData.pixmapDirectory) == 0) {
3523 if (appData.monoMode) {
3524 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3528 fprintf(stderr, _("\nLoading XIMs...\n"));
3530 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3531 fprintf(stderr, "%d", piece+1);
3532 for (kind=0; kind<4; kind++) {
3533 fprintf(stderr, ".");
3534 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3535 ExpandPathName(appData.pixmapDirectory),
3536 piece <= (int) WhiteKing ? "" : "w",
3537 pieceBitmapNames[piece],
3539 ximPieceBitmap[kind][piece] =
3540 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3541 0, 0, ss, ss, AllPlanes, XYPixmap);
3542 if (appData.debugMode)
3543 fprintf(stderr, _("(File:%s:) "), buf);
3544 loadXIM(ximPieceBitmap[kind][piece],
3546 &(xpmPieceBitmap2[kind][piece]),
3547 &(ximMaskPm2[piece]));
3548 if(piece <= (int)WhiteKing)
3549 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3551 fprintf(stderr," ");
3553 /* Load light and dark squares */
3554 /* If the LSQ and DSQ pieces don't exist, we will
3555 draw them with solid squares. */
3556 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3557 if (access(buf, 0) != 0) {
3561 fprintf(stderr, _("light square "));
3563 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3564 0, 0, ss, ss, AllPlanes, XYPixmap);
3565 if (appData.debugMode)
3566 fprintf(stderr, _("(File:%s:) "), buf);
3568 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3569 fprintf(stderr, _("dark square "));
3570 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3571 ExpandPathName(appData.pixmapDirectory), ss);
3572 if (appData.debugMode)
3573 fprintf(stderr, _("(File:%s:) "), buf);
3575 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3576 0, 0, ss, ss, AllPlanes, XYPixmap);
3577 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3578 xpmJailSquare = xpmLightSquare;
3580 fprintf(stderr, _("Done.\n"));
3582 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3585 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3588 void CreateXPMBoard(char *s, int kind)
3592 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3593 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3594 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3598 void FreeXPMPieces()
3599 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3600 // thisroutine has to be called t free the old piece pixmaps
3602 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3603 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3605 XFreePixmap(xDisplay, xpmLightSquare);
3606 XFreePixmap(xDisplay, xpmDarkSquare);
3610 void CreateXPMPieces()
3614 u_int ss = squareSize;
3616 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3617 XpmColorSymbol symbols[4];
3618 static int redo = False;
3620 if(redo) FreeXPMPieces(); else redo = 1;
3622 /* The XSynchronize calls were copied from CreatePieces.
3623 Not sure if needed, but can't hurt */
3624 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3626 /* Setup translations so piece colors match square colors */
3627 symbols[0].name = "light_piece";
3628 symbols[0].value = appData.whitePieceColor;
3629 symbols[1].name = "dark_piece";
3630 symbols[1].value = appData.blackPieceColor;
3631 symbols[2].name = "light_square";
3632 symbols[2].value = appData.lightSquareColor;
3633 symbols[3].name = "dark_square";
3634 symbols[3].value = appData.darkSquareColor;
3636 attr.valuemask = XpmColorSymbols;
3637 attr.colorsymbols = symbols;
3638 attr.numsymbols = 4;
3640 if (appData.monoMode) {
3641 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3645 if (strlen(appData.pixmapDirectory) == 0) {
3646 XpmPieces* pieces = builtInXpms;
3649 while (pieces->size != squareSize && pieces->size) pieces++;
3650 if (!pieces->size) {
3651 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3654 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3655 for (kind=0; kind<4; kind++) {
3657 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3658 pieces->xpm[piece][kind],
3659 &(xpmPieceBitmap2[kind][piece]),
3660 NULL, &attr)) != 0) {
3661 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3665 if(piece <= (int) WhiteKing)
3666 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3670 xpmJailSquare = xpmLightSquare;
3674 fprintf(stderr, _("\nLoading XPMs...\n"));
3677 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3678 fprintf(stderr, "%d ", piece+1);
3679 for (kind=0; kind<4; kind++) {
3680 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3681 ExpandPathName(appData.pixmapDirectory),
3682 piece > (int) WhiteKing ? "w" : "",
3683 pieceBitmapNames[piece],
3685 if (appData.debugMode) {
3686 fprintf(stderr, _("(File:%s:) "), buf);
3688 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3689 &(xpmPieceBitmap2[kind][piece]),
3690 NULL, &attr)) != 0) {
3691 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3692 // [HGM] missing: read of unorthodox piece failed; substitute King.
3693 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3694 ExpandPathName(appData.pixmapDirectory),
3696 if (appData.debugMode) {
3697 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3699 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3700 &(xpmPieceBitmap2[kind][piece]),
3704 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3709 if(piece <= (int) WhiteKing)
3710 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3713 /* Load light and dark squares */
3714 /* If the LSQ and DSQ pieces don't exist, we will
3715 draw them with solid squares. */
3716 fprintf(stderr, _("light square "));
3717 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3718 if (access(buf, 0) != 0) {
3722 if (appData.debugMode)
3723 fprintf(stderr, _("(File:%s:) "), buf);
3725 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3726 &xpmLightSquare, NULL, &attr)) != 0) {
3727 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3730 fprintf(stderr, _("dark square "));
3731 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3732 ExpandPathName(appData.pixmapDirectory), ss);
3733 if (appData.debugMode) {
3734 fprintf(stderr, _("(File:%s:) "), buf);
3736 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3737 &xpmDarkSquare, NULL, &attr)) != 0) {
3738 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3742 xpmJailSquare = xpmLightSquare;
3743 fprintf(stderr, _("Done.\n"));
3745 oldVariant = -1; // kludge to force re-makig of animation masks
3746 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3749 #endif /* HAVE_LIBXPM */
3752 /* No built-in bitmaps */
3757 u_int ss = squareSize;
3759 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3762 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3763 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3764 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3765 pieceBitmapNames[piece],
3766 ss, kind == SOLID ? 's' : 'o');
3767 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3768 if(piece <= (int)WhiteKing)
3769 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3773 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3777 /* With built-in bitmaps */
3780 BuiltInBits* bib = builtInBits;
3783 u_int ss = squareSize;
3785 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3788 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3790 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3791 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3792 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3793 pieceBitmapNames[piece],
3794 ss, kind == SOLID ? 's' : 'o');
3795 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3796 bib->bits[kind][piece], ss, ss);
3797 if(piece <= (int)WhiteKing)
3798 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3802 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3807 void ReadBitmap(pm, name, bits, wreq, hreq)
3810 unsigned char bits[];
3816 char msg[MSG_SIZ], fullname[MSG_SIZ];
3818 if (*appData.bitmapDirectory != NULLCHAR) {
3819 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3820 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3821 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3822 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3823 &w, &h, pm, &x_hot, &y_hot);
3824 fprintf(stderr, "load %s\n", name);
3825 if (errcode != BitmapSuccess) {
3827 case BitmapOpenFailed:
3828 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3830 case BitmapFileInvalid:
3831 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3833 case BitmapNoMemory:
3834 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3838 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3842 fprintf(stderr, _("%s: %s...using built-in\n"),
3844 } else if (w != wreq || h != hreq) {
3846 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3847 programName, fullname, w, h, wreq, hreq);
3853 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3862 if (lineGap == 0) return;
3864 /* [HR] Split this into 2 loops for non-square boards. */
3866 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3867 gridSegments[i].x1 = 0;
3868 gridSegments[i].x2 =
3869 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3870 gridSegments[i].y1 = gridSegments[i].y2
3871 = lineGap / 2 + (i * (squareSize + lineGap));
3874 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3875 gridSegments[j + i].y1 = 0;
3876 gridSegments[j + i].y2 =
3877 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3878 gridSegments[j + i].x1 = gridSegments[j + i].x2
3879 = lineGap / 2 + (j * (squareSize + lineGap));
3883 static void MenuBarSelect(w, addr, index)
3888 XtActionProc proc = (XtActionProc) addr;
3890 (proc)(NULL, NULL, NULL, NULL);
3893 void CreateMenuBarPopup(parent, name, mb)
3903 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3906 XtSetArg(args[j], XtNleftMargin, 20); j++;
3907 XtSetArg(args[j], XtNrightMargin, 20); j++;
3909 while (mi->string != NULL) {
3910 if (strcmp(mi->string, "----") == 0) {
3911 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3914 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3915 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3917 XtAddCallback(entry, XtNcallback,
3918 (XtCallbackProc) MenuBarSelect,
3919 (caddr_t) mi->proc);
3925 Widget CreateMenuBar(mb)
3929 Widget anchor, menuBar;
3931 char menuName[MSG_SIZ];
3934 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3935 XtSetArg(args[j], XtNvSpace, 0); j++;
3936 XtSetArg(args[j], XtNborderWidth, 0); j++;
3937 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3938 formWidget, args, j);
3940 while (mb->name != NULL) {
3941 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3942 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3944 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3947 shortName[0] = mb->name[0];
3948 shortName[1] = NULLCHAR;
3949 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3952 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3955 XtSetArg(args[j], XtNborderWidth, 0); j++;
3956 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3958 CreateMenuBarPopup(menuBar, menuName, mb);
3964 Widget CreateButtonBar(mi)
3968 Widget button, buttonBar;
3972 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3974 XtSetArg(args[j], XtNhSpace, 0); j++;
3976 XtSetArg(args[j], XtNborderWidth, 0); j++;
3977 XtSetArg(args[j], XtNvSpace, 0); j++;
3978 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3979 formWidget, args, j);
3981 while (mi->string != NULL) {
3984 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3985 XtSetArg(args[j], XtNborderWidth, 0); j++;
3987 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3988 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3989 buttonBar, args, j);
3990 XtAddCallback(button, XtNcallback,
3991 (XtCallbackProc) MenuBarSelect,
3992 (caddr_t) mi->proc);
3999 CreatePieceMenu(name, color)
4006 ChessSquare selection;
4008 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4009 boardWidget, args, 0);
4011 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4012 String item = pieceMenuStrings[color][i];
4014 if (strcmp(item, "----") == 0) {
4015 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4018 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4019 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4021 selection = pieceMenuTranslation[color][i];
4022 XtAddCallback(entry, XtNcallback,
4023 (XtCallbackProc) PieceMenuSelect,
4024 (caddr_t) selection);
4025 if (selection == WhitePawn || selection == BlackPawn) {
4026 XtSetArg(args[0], XtNpopupOnEntry, entry);
4027 XtSetValues(menu, args, 1);
4040 ChessSquare selection;
4042 whitePieceMenu = CreatePieceMenu("menuW", 0);
4043 blackPieceMenu = CreatePieceMenu("menuB", 1);
4045 XtRegisterGrabAction(PieceMenuPopup, True,
4046 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4047 GrabModeAsync, GrabModeAsync);
4049 XtSetArg(args[0], XtNlabel, _("Drop"));
4050 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4051 boardWidget, args, 1);
4052 for (i = 0; i < DROP_MENU_SIZE; i++) {
4053 String item = dropMenuStrings[i];
4055 if (strcmp(item, "----") == 0) {
4056 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4059 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4060 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4062 selection = dropMenuTranslation[i];
4063 XtAddCallback(entry, XtNcallback,
4064 (XtCallbackProc) DropMenuSelect,
4065 (caddr_t) selection);
4070 void SetupDropMenu()
4078 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4079 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4080 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4081 dmEnables[i].piece);
4082 XtSetSensitive(entry, p != NULL || !appData.testLegality
4083 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4084 && !appData.icsActive));
4086 while (p && *p++ == dmEnables[i].piece) count++;
4087 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4089 XtSetArg(args[j], XtNlabel, label); j++;
4090 XtSetValues(entry, args, j);
4094 void PieceMenuPopup(w, event, params, num_params)
4098 Cardinal *num_params;
4100 String whichMenu; int menuNr = -2;
4101 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4102 if (event->type == ButtonRelease)
4103 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4104 else if (event->type == ButtonPress)
4105 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4107 case 0: whichMenu = params[0]; break;
4108 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4110 case -1: if (errorUp) ErrorPopDown();
4113 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4116 static void PieceMenuSelect(w, piece, junk)
4121 if (pmFromX < 0 || pmFromY < 0) return;
4122 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4125 static void DropMenuSelect(w, piece, junk)
4130 if (pmFromX < 0 || pmFromY < 0) return;
4131 DropMenuEvent(piece, pmFromX, pmFromY);
4134 void WhiteClock(w, event, prms, nprms)
4143 void BlackClock(w, event, prms, nprms)
4154 * If the user selects on a border boundary, return -1; if off the board,
4155 * return -2. Otherwise map the event coordinate to the square.
4157 int EventToSquare(x, limit)
4165 if ((x % (squareSize + lineGap)) >= squareSize)
4167 x /= (squareSize + lineGap);
4173 static void do_flash_delay(msec)
4179 static void drawHighlight(file, rank, gc)
4185 if (lineGap == 0) return;
4188 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4189 (squareSize + lineGap);
4190 y = lineGap/2 + rank * (squareSize + lineGap);
4192 x = lineGap/2 + file * (squareSize + lineGap);
4193 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4194 (squareSize + lineGap);
4197 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4198 squareSize+lineGap, squareSize+lineGap);
4201 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4202 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4205 SetHighlights(fromX, fromY, toX, toY)
4206 int fromX, fromY, toX, toY;
4208 if (hi1X != fromX || hi1Y != fromY) {
4209 if (hi1X >= 0 && hi1Y >= 0) {
4210 drawHighlight(hi1X, hi1Y, lineGC);
4212 } // [HGM] first erase both, then draw new!
4213 if (hi2X != toX || hi2Y != toY) {
4214 if (hi2X >= 0 && hi2Y >= 0) {
4215 drawHighlight(hi2X, hi2Y, lineGC);
4218 if (hi1X != fromX || hi1Y != fromY) {
4219 if (fromX >= 0 && fromY >= 0) {
4220 drawHighlight(fromX, fromY, highlineGC);
4223 if (hi2X != toX || hi2Y != toY) {
4224 if (toX >= 0 && toY >= 0) {
4225 drawHighlight(toX, toY, highlineGC);
4237 SetHighlights(-1, -1, -1, -1);
4242 SetPremoveHighlights(fromX, fromY, toX, toY)
4243 int fromX, fromY, toX, toY;
4245 if (pm1X != fromX || pm1Y != fromY) {
4246 if (pm1X >= 0 && pm1Y >= 0) {
4247 drawHighlight(pm1X, pm1Y, lineGC);
4249 if (fromX >= 0 && fromY >= 0) {
4250 drawHighlight(fromX, fromY, prelineGC);
4253 if (pm2X != toX || pm2Y != toY) {
4254 if (pm2X >= 0 && pm2Y >= 0) {
4255 drawHighlight(pm2X, pm2Y, lineGC);
4257 if (toX >= 0 && toY >= 0) {
4258 drawHighlight(toX, toY, prelineGC);
4268 ClearPremoveHighlights()
4270 SetPremoveHighlights(-1, -1, -1, -1);
4273 static int CutOutSquare(x, y, x0, y0, kind)
4274 int x, y, *x0, *y0, kind;
4276 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4277 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4279 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4280 if(textureW[kind] < W*squareSize)
4281 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4283 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4284 if(textureH[kind] < H*squareSize)
4285 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4287 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4291 static void BlankSquare(x, y, color, piece, dest, fac)
4292 int x, y, color, fac;
4295 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4297 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4298 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4299 squareSize, squareSize, x*fac, y*fac);
4301 if (useImages && useImageSqs) {
4305 pm = xpmLightSquare;
4310 case 2: /* neutral */
4315 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4316 squareSize, squareSize, x*fac, y*fac);
4326 case 2: /* neutral */
4331 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4336 I split out the routines to draw a piece so that I could
4337 make a generic flash routine.
4339 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4341 int square_color, x, y;
4344 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4345 switch (square_color) {
4347 case 2: /* neutral */
4349 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4350 ? *pieceToOutline(piece)
4351 : *pieceToSolid(piece),
4352 dest, bwPieceGC, 0, 0,
4353 squareSize, squareSize, x, y);
4356 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4357 ? *pieceToSolid(piece)
4358 : *pieceToOutline(piece),
4359 dest, wbPieceGC, 0, 0,
4360 squareSize, squareSize, x, y);
4365 static void monoDrawPiece(piece, square_color, x, y, dest)
4367 int square_color, x, y;
4370 switch (square_color) {
4372 case 2: /* neutral */
4374 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4375 ? *pieceToOutline(piece)
4376 : *pieceToSolid(piece),
4377 dest, bwPieceGC, 0, 0,
4378 squareSize, squareSize, x, y, 1);
4381 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4382 ? *pieceToSolid(piece)
4383 : *pieceToOutline(piece),
4384 dest, wbPieceGC, 0, 0,
4385 squareSize, squareSize, x, y, 1);
4390 static void colorDrawPiece(piece, square_color, x, y, dest)
4392 int square_color, x, y;
4395 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4396 switch (square_color) {
4398 XCopyPlane(xDisplay, *pieceToSolid(piece),
4399 dest, (int) piece < (int) BlackPawn
4400 ? wlPieceGC : blPieceGC, 0, 0,
4401 squareSize, squareSize, x, y, 1);
4404 XCopyPlane(xDisplay, *pieceToSolid(piece),
4405 dest, (int) piece < (int) BlackPawn
4406 ? wdPieceGC : bdPieceGC, 0, 0,
4407 squareSize, squareSize, x, y, 1);
4409 case 2: /* neutral */
4411 XCopyPlane(xDisplay, *pieceToSolid(piece),
4412 dest, (int) piece < (int) BlackPawn
4413 ? wjPieceGC : bjPieceGC, 0, 0,
4414 squareSize, squareSize, x, y, 1);
4419 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4421 int square_color, x, y;
4424 int kind, p = piece;
4426 switch (square_color) {
4428 case 2: /* neutral */
4430 if ((int)piece < (int) BlackPawn) {
4438 if ((int)piece < (int) BlackPawn) {
4446 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4447 if(useTexture & square_color+1) {
4448 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4449 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4450 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4451 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4452 XSetClipMask(xDisplay, wlPieceGC, None);
4453 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4455 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4456 dest, wlPieceGC, 0, 0,
4457 squareSize, squareSize, x, y);
4460 typedef void (*DrawFunc)();
4462 DrawFunc ChooseDrawFunc()
4464 if (appData.monoMode) {
4465 if (DefaultDepth(xDisplay, xScreen) == 1) {
4466 return monoDrawPiece_1bit;
4468 return monoDrawPiece;
4472 return colorDrawPieceImage;
4474 return colorDrawPiece;
4478 /* [HR] determine square color depending on chess variant. */
4479 static int SquareColor(row, column)
4484 if (gameInfo.variant == VariantXiangqi) {
4485 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4487 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4489 } else if (row <= 4) {
4495 square_color = ((column + row) % 2) == 1;
4498 /* [hgm] holdings: next line makes all holdings squares light */
4499 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4501 return square_color;
4504 void DrawSquare(row, column, piece, do_flash)
4505 int row, column, do_flash;
4508 int square_color, x, y, direction, font_ascent, font_descent;
4511 XCharStruct overall;
4515 /* Calculate delay in milliseconds (2-delays per complete flash) */
4516 flash_delay = 500 / appData.flashRate;
4519 x = lineGap + ((BOARD_WIDTH-1)-column) *
4520 (squareSize + lineGap);
4521 y = lineGap + row * (squareSize + lineGap);
4523 x = lineGap + column * (squareSize + lineGap);
4524 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4525 (squareSize + lineGap);
4528 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4530 square_color = SquareColor(row, column);
4532 if ( // [HGM] holdings: blank out area between board and holdings
4533 column == BOARD_LEFT-1 || column == BOARD_RGHT
4534 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4535 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4536 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4538 // [HGM] print piece counts next to holdings
4539 string[1] = NULLCHAR;
4540 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4541 string[0] = '0' + piece;
4542 XTextExtents(countFontStruct, string, 1, &direction,
4543 &font_ascent, &font_descent, &overall);
4544 if (appData.monoMode) {
4545 XDrawImageString(xDisplay, xBoardWindow, countGC,
4546 x + squareSize - overall.width - 2,
4547 y + font_ascent + 1, string, 1);
4549 XDrawString(xDisplay, xBoardWindow, countGC,
4550 x + squareSize - overall.width - 2,
4551 y + font_ascent + 1, string, 1);
4554 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && 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 + 2, y + font_ascent + 1, string, 1);
4562 XDrawString(xDisplay, xBoardWindow, countGC,
4563 x + 2, y + font_ascent + 1, string, 1);
4567 if (piece == EmptySquare || appData.blindfold) {
4568 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4570 drawfunc = ChooseDrawFunc();
4572 if (do_flash && appData.flashCount > 0) {
4573 for (i=0; i<appData.flashCount; ++i) {
4574 drawfunc(piece, square_color, x, y, xBoardWindow);
4575 XSync(xDisplay, False);
4576 do_flash_delay(flash_delay);
4578 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4579 XSync(xDisplay, False);
4580 do_flash_delay(flash_delay);
4583 drawfunc(piece, square_color, x, y, xBoardWindow);
4587 string[1] = NULLCHAR;
4588 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4589 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4590 string[0] = 'a' + column - BOARD_LEFT;
4591 XTextExtents(coordFontStruct, string, 1, &direction,
4592 &font_ascent, &font_descent, &overall);
4593 if (appData.monoMode) {
4594 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4595 x + squareSize - overall.width - 2,
4596 y + squareSize - font_descent - 1, string, 1);
4598 XDrawString(xDisplay, xBoardWindow, coordGC,
4599 x + squareSize - overall.width - 2,
4600 y + squareSize - font_descent - 1, string, 1);
4603 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4604 string[0] = ONE + row;
4605 XTextExtents(coordFontStruct, string, 1, &direction,
4606 &font_ascent, &font_descent, &overall);
4607 if (appData.monoMode) {
4608 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4609 x + 2, y + font_ascent + 1, string, 1);
4611 XDrawString(xDisplay, xBoardWindow, coordGC,
4612 x + 2, y + font_ascent + 1, string, 1);
4615 if(!partnerUp && marker[row][column]) {
4616 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4617 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4622 /* Why is this needed on some versions of X? */
4623 void EventProc(widget, unused, event)
4628 if (!XtIsRealized(widget))
4631 switch (event->type) {
4633 if (event->xexpose.count > 0) return; /* no clipping is done */
4634 XDrawPosition(widget, True, NULL);
4635 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4636 flipView = !flipView; partnerUp = !partnerUp;
4637 XDrawPosition(widget, True, NULL);
4638 flipView = !flipView; partnerUp = !partnerUp;
4642 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4649 void DrawPosition(fullRedraw, board)
4650 /*Boolean*/int fullRedraw;
4653 XDrawPosition(boardWidget, fullRedraw, board);
4656 /* Returns 1 if there are "too many" differences between b1 and b2
4657 (i.e. more than 1 move was made) */
4658 static int too_many_diffs(b1, b2)
4664 for (i=0; i<BOARD_HEIGHT; ++i) {
4665 for (j=0; j<BOARD_WIDTH; ++j) {
4666 if (b1[i][j] != b2[i][j]) {
4667 if (++c > 4) /* Castling causes 4 diffs */
4675 /* Matrix describing castling maneuvers */
4676 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4677 static int castling_matrix[4][5] = {
4678 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4679 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4680 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4681 { 7, 7, 4, 5, 6 } /* 0-0, black */
4684 /* Checks whether castling occurred. If it did, *rrow and *rcol
4685 are set to the destination (row,col) of the rook that moved.
4687 Returns 1 if castling occurred, 0 if not.
4689 Note: Only handles a max of 1 castling move, so be sure
4690 to call too_many_diffs() first.
4692 static int check_castle_draw(newb, oldb, rrow, rcol)
4699 /* For each type of castling... */
4700 for (i=0; i<4; ++i) {
4701 r = castling_matrix[i];
4703 /* Check the 4 squares involved in the castling move */
4705 for (j=1; j<=4; ++j) {
4706 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4713 /* All 4 changed, so it must be a castling move */
4722 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4723 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4725 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4728 void DrawSeekBackground( int left, int top, int right, int bottom )
4730 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4733 void DrawSeekText(char *buf, int x, int y)
4735 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4738 void DrawSeekDot(int x, int y, int colorNr)
4740 int square = colorNr & 0x80;
4743 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4745 XFillRectangle(xDisplay, xBoardWindow, color,
4746 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4748 XFillArc(xDisplay, xBoardWindow, color,
4749 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4752 static int damage[2][BOARD_RANKS][BOARD_FILES];
4755 * event handler for redrawing the board
4757 void XDrawPosition(w, repaint, board)
4759 /*Boolean*/int repaint;
4763 static int lastFlipView = 0;
4764 static int lastBoardValid[2] = {0, 0};
4765 static Board lastBoard[2];
4768 int nr = twoBoards*partnerUp;
4770 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4772 if (board == NULL) {
4773 if (!lastBoardValid[nr]) return;
4774 board = lastBoard[nr];
4776 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4777 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4778 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4783 * It would be simpler to clear the window with XClearWindow()
4784 * but this causes a very distracting flicker.
4787 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4789 if ( lineGap && IsDrawArrowEnabled())
4790 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4791 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4793 /* If too much changes (begin observing new game, etc.), don't
4795 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4797 /* Special check for castling so we don't flash both the king
4798 and the rook (just flash the king). */
4800 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4801 /* Draw rook with NO flashing. King will be drawn flashing later */
4802 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4803 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4807 /* First pass -- Draw (newly) empty squares and repair damage.
4808 This prevents you from having a piece show up twice while it
4809 is flashing on its new square */
4810 for (i = 0; i < BOARD_HEIGHT; i++)
4811 for (j = 0; j < BOARD_WIDTH; j++)
4812 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4813 || damage[nr][i][j]) {
4814 DrawSquare(i, j, board[i][j], 0);
4815 damage[nr][i][j] = False;
4818 /* Second pass -- Draw piece(s) in new position and flash them */
4819 for (i = 0; i < BOARD_HEIGHT; i++)
4820 for (j = 0; j < BOARD_WIDTH; j++)
4821 if (board[i][j] != lastBoard[nr][i][j]) {
4822 DrawSquare(i, j, board[i][j], do_flash);
4826 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4827 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4828 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4830 for (i = 0; i < BOARD_HEIGHT; i++)
4831 for (j = 0; j < BOARD_WIDTH; j++) {
4832 DrawSquare(i, j, board[i][j], 0);
4833 damage[nr][i][j] = False;
4837 CopyBoard(lastBoard[nr], board);
4838 lastBoardValid[nr] = 1;
4839 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4840 lastFlipView = flipView;
4842 /* Draw highlights */
4843 if (pm1X >= 0 && pm1Y >= 0) {
4844 drawHighlight(pm1X, pm1Y, prelineGC);
4846 if (pm2X >= 0 && pm2Y >= 0) {
4847 drawHighlight(pm2X, pm2Y, prelineGC);
4849 if (hi1X >= 0 && hi1Y >= 0) {
4850 drawHighlight(hi1X, hi1Y, highlineGC);
4852 if (hi2X >= 0 && hi2Y >= 0) {
4853 drawHighlight(hi2X, hi2Y, highlineGC);
4855 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4857 /* If piece being dragged around board, must redraw that too */
4860 XSync(xDisplay, False);
4865 * event handler for redrawing the board
4867 void DrawPositionProc(w, event, prms, nprms)
4873 XDrawPosition(w, True, NULL);
4878 * event handler for parsing user moves
4880 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4881 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4882 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4883 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4884 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4885 // and at the end FinishMove() to perform the move after optional promotion popups.
4886 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4887 void HandleUserMove(w, event, prms, nprms)
4893 if (w != boardWidget || errorExitStatus != -1) return;
4894 if(nprms) shiftKey = !strcmp(prms[0], "1");
4897 if (event->type == ButtonPress) {
4898 XtPopdown(promotionShell);
4899 XtDestroyWidget(promotionShell);
4900 promotionUp = False;
4908 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4909 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4910 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4913 void AnimateUserMove (Widget w, XEvent * event,
4914 String * params, Cardinal * nParams)
4916 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4917 DragPieceMove(event->xmotion.x, event->xmotion.y);
4920 void HandlePV (Widget w, XEvent * event,
4921 String * params, Cardinal * nParams)
4922 { // [HGM] pv: walk PV
4923 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4926 static int savedIndex; /* gross that this is global */
4928 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4931 XawTextPosition index, dummy;
4934 XawTextGetSelectionPos(w, &index, &dummy);
4935 XtSetArg(arg, XtNstring, &val);
4936 XtGetValues(w, &arg, 1);
4937 ReplaceComment(savedIndex, val);
4938 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4939 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4942 void EditCommentPopUp(index, title, text)
4947 if (text == NULL) text = "";
4948 NewCommentPopup(title, text, index);
4951 void ICSInputBoxPopUp()
4956 extern Option boxOptions[];
4958 void ICSInputSendText()
4965 edit = boxOptions[0].handle;
4967 XtSetArg(args[j], XtNstring, &val); j++;
4968 XtGetValues(edit, args, j);
4970 SendMultiLineToICS(val);
4971 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4972 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4975 void ICSInputBoxPopDown()
4980 void CommentPopUp(title, text)
4983 savedIndex = currentMove; // [HGM] vari
4984 NewCommentPopup(title, text, currentMove);
4987 void CommentPopDown()
4992 void FileNamePopUp(label, def, filter, proc, openMode)
4999 fileProc = proc; /* I can't see a way not */
5000 fileOpenMode = openMode; /* to use globals here */
5001 { // [HGM] use file-selector dialog stolen from Ghostview
5003 int index; // this is not supported yet
5005 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5006 (def[0] ? def : NULL), filter, openMode, NULL, &name))
5007 (void) (*fileProc)(f, index=0, name);
5011 void FileNamePopDown()
5013 if (!filenameUp) return;
5014 XtPopdown(fileNameShell);
5015 XtDestroyWidget(fileNameShell);
5020 void FileNameCallback(w, client_data, call_data)
5022 XtPointer client_data, call_data;
5027 XtSetArg(args[0], XtNlabel, &name);
5028 XtGetValues(w, args, 1);
5030 if (strcmp(name, _("cancel")) == 0) {
5035 FileNameAction(w, NULL, NULL, NULL);
5038 void FileNameAction(w, event, prms, nprms)
5050 name = XawDialogGetValueString(w = XtParent(w));
5052 if ((name != NULL) && (*name != NULLCHAR)) {
5053 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5054 XtPopdown(w = XtParent(XtParent(w)));
5058 p = strrchr(buf, ' ');
5065 fullname = ExpandPathName(buf);
5067 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5070 f = fopen(fullname, fileOpenMode);
5072 DisplayError(_("Failed to open file"), errno);
5074 (void) (*fileProc)(f, index, buf);
5081 XtPopdown(w = XtParent(XtParent(w)));
5087 void PromotionPopUp()
5090 Widget dialog, layout;
5092 Dimension bw_width, pw_width;
5096 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5097 XtGetValues(boardWidget, args, j);
5100 XtSetArg(args[j], XtNresizable, True); j++;
5101 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5103 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5104 shellWidget, args, j);
5106 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5107 layoutArgs, XtNumber(layoutArgs));
5110 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5111 XtSetArg(args[j], XtNborderWidth, 0); j++;
5112 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5115 if(gameInfo.variant != VariantShogi) {
5116 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5117 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5118 (XtPointer) dialog);
5119 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5120 (XtPointer) dialog);
5121 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5122 (XtPointer) dialog);
5123 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5124 (XtPointer) dialog);
5126 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5127 (XtPointer) dialog);
5128 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5129 (XtPointer) dialog);
5130 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5131 (XtPointer) dialog);
5132 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5133 (XtPointer) dialog);
5135 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5136 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5137 gameInfo.variant == VariantGiveaway) {
5138 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5139 (XtPointer) dialog);
5141 if(gameInfo.variant == VariantCapablanca ||
5142 gameInfo.variant == VariantGothic ||
5143 gameInfo.variant == VariantCapaRandom) {
5144 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5145 (XtPointer) dialog);
5146 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5147 (XtPointer) dialog);
5149 } else // [HGM] shogi
5151 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5152 (XtPointer) dialog);
5153 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5154 (XtPointer) dialog);
5156 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5157 (XtPointer) dialog);
5159 XtRealizeWidget(promotionShell);
5160 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5163 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5164 XtGetValues(promotionShell, args, j);
5166 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5167 lineGap + squareSize/3 +
5168 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5169 0 : 6*(squareSize + lineGap)), &x, &y);
5172 XtSetArg(args[j], XtNx, x); j++;
5173 XtSetArg(args[j], XtNy, y); j++;
5174 XtSetValues(promotionShell, args, j);
5176 XtPopup(promotionShell, XtGrabNone);
5181 void PromotionPopDown()
5183 if (!promotionUp) return;
5184 XtPopdown(promotionShell);
5185 XtDestroyWidget(promotionShell);
5186 promotionUp = False;
5189 void PromotionCallback(w, client_data, call_data)
5191 XtPointer client_data, call_data;
5197 XtSetArg(args[0], XtNlabel, &name);
5198 XtGetValues(w, args, 1);
5202 if (fromX == -1) return;
5204 if (strcmp(name, _("cancel")) == 0) {
5208 } else if (strcmp(name, _("Knight")) == 0) {
5210 } else if (strcmp(name, _("Promote")) == 0) {
5212 } else if (strcmp(name, _("Defer")) == 0) {
5215 promoChar = ToLower(name[0]);
5218 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5220 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5221 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5226 void ErrorCallback(w, client_data, call_data)
5228 XtPointer client_data, call_data;
5231 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5233 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5239 if (!errorUp) return;
5241 XtPopdown(errorShell);
5242 XtDestroyWidget(errorShell);
5243 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5246 void ErrorPopUp(title, label, modal)
5247 char *title, *label;
5251 Widget dialog, layout;
5255 Dimension bw_width, pw_width;
5256 Dimension pw_height;
5260 XtSetArg(args[i], XtNresizable, True); i++;
5261 XtSetArg(args[i], XtNtitle, title); i++;
5263 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5264 shellWidget, args, i);
5266 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5267 layoutArgs, XtNumber(layoutArgs));
5270 XtSetArg(args[i], XtNlabel, label); i++;
5271 XtSetArg(args[i], XtNborderWidth, 0); i++;
5272 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5275 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5277 XtRealizeWidget(errorShell);
5278 CatchDeleteWindow(errorShell, "ErrorPopDown");
5281 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5282 XtGetValues(boardWidget, args, i);
5284 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5285 XtSetArg(args[i], XtNheight, &pw_height); i++;
5286 XtGetValues(errorShell, args, i);
5289 /* This code seems to tickle an X bug if it is executed too soon
5290 after xboard starts up. The coordinates get transformed as if
5291 the main window was positioned at (0, 0).
5293 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5294 0 - pw_height + squareSize / 3, &x, &y);
5296 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5297 RootWindowOfScreen(XtScreen(boardWidget)),
5298 (bw_width - pw_width) / 2,
5299 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5303 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5306 XtSetArg(args[i], XtNx, x); i++;
5307 XtSetArg(args[i], XtNy, y); i++;
5308 XtSetValues(errorShell, args, i);
5311 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5314 /* Disable all user input other than deleting the window */
5315 static int frozen = 0;
5319 /* Grab by a widget that doesn't accept input */
5320 XtAddGrab(messageWidget, TRUE, FALSE);
5324 /* Undo a FreezeUI */
5327 if (!frozen) return;
5328 XtRemoveGrab(messageWidget);
5332 char *ModeToWidgetName(mode)
5336 case BeginningOfGame:
5337 if (appData.icsActive)
5338 return "menuMode.ICS Client";
5339 else if (appData.noChessProgram ||
5340 *appData.cmailGameName != NULLCHAR)
5341 return "menuMode.Edit Game";
5343 return "menuMode.Machine Black";
5344 case MachinePlaysBlack:
5345 return "menuMode.Machine Black";
5346 case MachinePlaysWhite:
5347 return "menuMode.Machine White";
5349 return "menuMode.Analysis Mode";
5351 return "menuMode.Analyze File";
5352 case TwoMachinesPlay:
5353 return "menuMode.Two Machines";
5355 return "menuMode.Edit Game";
5356 case PlayFromGameFile:
5357 return "menuFile.Load Game";
5359 return "menuMode.Edit Position";
5361 return "menuMode.Training";
5362 case IcsPlayingWhite:
5363 case IcsPlayingBlack:
5367 return "menuMode.ICS Client";
5374 void ModeHighlight()
5377 static int oldPausing = FALSE;
5378 static GameMode oldmode = (GameMode) -1;
5381 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5383 if (pausing != oldPausing) {
5384 oldPausing = pausing;
5386 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5388 XtSetArg(args[0], XtNleftBitmap, None);
5390 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5393 if (appData.showButtonBar) {
5394 /* Always toggle, don't set. Previous code messes up when
5395 invoked while the button is pressed, as releasing it
5396 toggles the state again. */
5399 XtSetArg(args[0], XtNbackground, &oldbg);
5400 XtSetArg(args[1], XtNforeground, &oldfg);
5401 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5403 XtSetArg(args[0], XtNbackground, oldfg);
5404 XtSetArg(args[1], XtNforeground, oldbg);
5406 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5410 wname = ModeToWidgetName(oldmode);
5411 if (wname != NULL) {
5412 XtSetArg(args[0], XtNleftBitmap, None);
5413 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5415 wname = ModeToWidgetName(gameMode);
5416 if (wname != NULL) {
5417 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5418 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5421 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5422 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5424 /* Maybe all the enables should be handled here, not just this one */
5425 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5426 gameMode == Training || gameMode == PlayFromGameFile);
5431 * Button/menu procedures
5433 void ResetProc(w, event, prms, nprms)
5442 int LoadGamePopUp(f, gameNumber, title)
5447 cmailMsgLoaded = FALSE;
5448 if (gameNumber == 0) {
5449 int error = GameListBuild(f);
5451 DisplayError(_("Cannot build game list"), error);
5452 } else if (!ListEmpty(&gameList) &&
5453 ((ListGame *) gameList.tailPred)->number > 1) {
5454 GameListPopUp(f, title);
5460 return LoadGame(f, gameNumber, title, FALSE);
5463 void LoadGameProc(w, event, prms, nprms)
5469 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5472 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5475 void LoadNextGameProc(w, event, prms, nprms)
5484 void LoadPrevGameProc(w, event, prms, nprms)
5493 void ReloadGameProc(w, event, prms, nprms)
5502 void LoadNextPositionProc(w, event, prms, nprms)
5511 void LoadPrevPositionProc(w, event, prms, nprms)
5520 void ReloadPositionProc(w, event, prms, nprms)
5529 void LoadPositionProc(w, event, prms, nprms)
5535 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5538 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5541 void SaveGameProc(w, event, prms, nprms)
5547 FileNamePopUp(_("Save game file name?"),
5548 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5549 appData.oldSaveStyle ? ".game" : ".pgn",
5553 void SavePositionProc(w, event, prms, nprms)
5559 FileNamePopUp(_("Save position file name?"),
5560 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5561 appData.oldSaveStyle ? ".pos" : ".fen",
5565 void ReloadCmailMsgProc(w, event, prms, nprms)
5571 ReloadCmailMsgEvent(FALSE);
5574 void MailMoveProc(w, event, prms, nprms)
5583 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5584 char *selected_fen_position=NULL;
5587 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5588 Atom *type_return, XtPointer *value_return,
5589 unsigned long *length_return, int *format_return)
5591 char *selection_tmp;
5593 if (!selected_fen_position) return False; /* should never happen */
5594 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5595 /* note: since no XtSelectionDoneProc was registered, Xt will
5596 * automatically call XtFree on the value returned. So have to
5597 * make a copy of it allocated with XtMalloc */
5598 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5599 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5601 *value_return=selection_tmp;
5602 *length_return=strlen(selection_tmp);
5603 *type_return=*target;
5604 *format_return = 8; /* bits per byte */
5606 } else if (*target == XA_TARGETS(xDisplay)) {
5607 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5608 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5609 targets_tmp[1] = XA_STRING;
5610 *value_return = targets_tmp;
5611 *type_return = XA_ATOM;
5613 *format_return = 8 * sizeof(Atom);
5614 if (*format_return > 32) {
5615 *length_return *= *format_return / 32;
5616 *format_return = 32;
5624 /* note: when called from menu all parameters are NULL, so no clue what the
5625 * Widget which was clicked on was, or what the click event was
5627 void CopyPositionProc(w, event, prms, nprms)
5634 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5635 * have a notion of a position that is selected but not copied.
5636 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5638 if(gameMode == EditPosition) EditPositionDone(TRUE);
5639 if (selected_fen_position) free(selected_fen_position);
5640 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5641 if (!selected_fen_position) return;
5642 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5644 SendPositionSelection,
5645 NULL/* lose_ownership_proc */ ,
5646 NULL/* transfer_done_proc */);
5647 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5649 SendPositionSelection,
5650 NULL/* lose_ownership_proc */ ,
5651 NULL/* transfer_done_proc */);
5654 /* function called when the data to Paste is ready */
5656 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5657 Atom *type, XtPointer value, unsigned long *len, int *format)
5660 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5661 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5662 EditPositionPasteFEN(fenstr);
5666 /* called when Paste Position button is pressed,
5667 * all parameters will be NULL */
5668 void PastePositionProc(w, event, prms, nprms)
5674 XtGetSelectionValue(menuBarWidget,
5675 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5676 /* (XtSelectionCallbackProc) */ PastePositionCB,
5677 NULL, /* client_data passed to PastePositionCB */
5679 /* better to use the time field from the event that triggered the
5680 * call to this function, but that isn't trivial to get
5688 SendGameSelection(Widget w, Atom *selection, Atom *target,
5689 Atom *type_return, XtPointer *value_return,
5690 unsigned long *length_return, int *format_return)
5692 char *selection_tmp;
5694 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5695 FILE* f = fopen(gameCopyFilename, "r");
5698 if (f == NULL) return False;
5702 selection_tmp = XtMalloc(len + 1);
5703 count = fread(selection_tmp, 1, len, f);
5706 XtFree(selection_tmp);
5709 selection_tmp[len] = NULLCHAR;
5710 *value_return = selection_tmp;
5711 *length_return = len;
5712 *type_return = *target;
5713 *format_return = 8; /* bits per byte */
5715 } else if (*target == XA_TARGETS(xDisplay)) {
5716 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5717 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5718 targets_tmp[1] = XA_STRING;
5719 *value_return = targets_tmp;
5720 *type_return = XA_ATOM;
5722 *format_return = 8 * sizeof(Atom);
5723 if (*format_return > 32) {
5724 *length_return *= *format_return / 32;
5725 *format_return = 32;
5733 void CopySomething()
5738 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5739 * have a notion of a game that is selected but not copied.
5740 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5742 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5745 NULL/* lose_ownership_proc */ ,
5746 NULL/* transfer_done_proc */);
5747 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5750 NULL/* lose_ownership_proc */ ,
5751 NULL/* transfer_done_proc */);
5754 /* note: when called from menu all parameters are NULL, so no clue what the
5755 * Widget which was clicked on was, or what the click event was
5757 void CopyGameProc(w, event, prms, nprms)
5765 ret = SaveGameToFile(gameCopyFilename, FALSE);
5771 void CopyGameListProc(w, event, prms, nprms)
5777 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5781 /* function called when the data to Paste is ready */
5783 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5784 Atom *type, XtPointer value, unsigned long *len, int *format)
5787 if (value == NULL || *len == 0) {
5788 return; /* nothing had been selected to copy */
5790 f = fopen(gamePasteFilename, "w");
5792 DisplayError(_("Can't open temp file"), errno);
5795 fwrite(value, 1, *len, f);
5798 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5801 /* called when Paste Game button is pressed,
5802 * all parameters will be NULL */
5803 void PasteGameProc(w, event, prms, nprms)
5809 XtGetSelectionValue(menuBarWidget,
5810 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5811 /* (XtSelectionCallbackProc) */ PasteGameCB,
5812 NULL, /* client_data passed to PasteGameCB */
5814 /* better to use the time field from the event that triggered the
5815 * call to this function, but that isn't trivial to get
5825 SaveGameProc(NULL, NULL, NULL, NULL);
5829 void QuitProc(w, event, prms, nprms)
5838 void PauseProc(w, event, prms, nprms)
5848 void MachineBlackProc(w, event, prms, nprms)
5854 MachineBlackEvent();
5857 void MachineWhiteProc(w, event, prms, nprms)
5863 MachineWhiteEvent();
5866 void AnalyzeModeProc(w, event, prms, nprms)
5874 if (!first.analysisSupport) {
5875 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5876 DisplayError(buf, 0);
5879 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5880 if (appData.icsActive) {
5881 if (gameMode != IcsObserving) {
5882 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5883 DisplayError(buf, 0);
5885 if (appData.icsEngineAnalyze) {
5886 if (appData.debugMode)
5887 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5893 /* if enable, use want disable icsEngineAnalyze */
5894 if (appData.icsEngineAnalyze) {
5899 appData.icsEngineAnalyze = TRUE;
5900 if (appData.debugMode)
5901 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5903 #ifndef OPTIONSDIALOG
5904 if (!appData.showThinking)
5905 ShowThinkingProc(w,event,prms,nprms);
5911 void AnalyzeFileProc(w, event, prms, nprms)
5917 if (!first.analysisSupport) {
5919 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5920 DisplayError(buf, 0);
5924 #ifndef OPTIONSDIALOG
5925 if (!appData.showThinking)
5926 ShowThinkingProc(w,event,prms,nprms);
5929 FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5930 AnalysisPeriodicEvent(1);
5933 void TwoMachinesProc(w, event, prms, nprms)
5942 void MatchProc(w, event, prms, nprms)
5951 void IcsClientProc(w, event, prms, nprms)
5960 void EditGameProc(w, event, prms, nprms)
5969 void EditPositionProc(w, event, prms, nprms)
5975 EditPositionEvent();
5978 void TrainingProc(w, event, prms, nprms)
5987 void EditCommentProc(w, event, prms, nprms)
5995 if (PopDown(1)) { // popdown succesful
5997 XtSetArg(args[j], XtNleftBitmap, None); j++;
5998 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5999 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6000 } else // was not up
6004 void IcsInputBoxProc(w, event, prms, nprms)
6010 if (!PopDown(4)) ICSInputBoxPopUp();
6013 void AcceptProc(w, event, prms, nprms)
6022 void DeclineProc(w, event, prms, nprms)
6031 void RematchProc(w, event, prms, nprms)
6040 void CallFlagProc(w, event, prms, nprms)
6049 void DrawProc(w, event, prms, nprms)
6058 void AbortProc(w, event, prms, nprms)
6067 void AdjournProc(w, event, prms, nprms)
6076 void ResignProc(w, event, prms, nprms)
6085 void AdjuWhiteProc(w, event, prms, nprms)
6091 UserAdjudicationEvent(+1);
6094 void AdjuBlackProc(w, event, prms, nprms)
6100 UserAdjudicationEvent(-1);
6103 void AdjuDrawProc(w, event, prms, nprms)
6109 UserAdjudicationEvent(0);
6112 void EnterKeyProc(w, event, prms, nprms)
6118 if (shellUp[4] == True)
6122 void UpKeyProc(w, event, prms, nprms)
6127 { // [HGM] input: let up-arrow recall previous line from history
6134 if (!shellUp[4]) return;
6135 edit = boxOptions[0].handle;
6137 XtSetArg(args[j], XtNstring, &val); j++;
6138 XtGetValues(edit, args, j);
6139 val = PrevInHistory(val);
6140 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6141 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6143 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6144 XawTextReplace(edit, 0, 0, &t);
6145 XawTextSetInsertionPoint(edit, 9999);
6149 void DownKeyProc(w, event, prms, nprms)
6154 { // [HGM] input: let down-arrow recall next line from history
6159 if (!shellUp[4]) return;
6160 edit = boxOptions[0].handle;
6161 val = NextInHistory();
6162 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6163 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6165 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6166 XawTextReplace(edit, 0, 0, &t);
6167 XawTextSetInsertionPoint(edit, 9999);
6171 void StopObservingProc(w, event, prms, nprms)
6177 StopObservingEvent();
6180 void StopExaminingProc(w, event, prms, nprms)
6186 StopExaminingEvent();
6189 void UploadProc(w, event, prms, nprms)
6199 void ForwardProc(w, event, prms, nprms)
6209 void BackwardProc(w, event, prms, nprms)
6218 void ToStartProc(w, event, prms, nprms)
6227 void ToEndProc(w, event, prms, nprms)
6236 void RevertProc(w, event, prms, nprms)
6245 void AnnotateProc(w, event, prms, nprms)
6254 void TruncateGameProc(w, event, prms, nprms)
6260 TruncateGameEvent();
6262 void RetractMoveProc(w, event, prms, nprms)
6271 void MoveNowProc(w, event, prms, nprms)
6280 void FlipViewProc(w, event, prms, nprms)
6286 flipView = !flipView;
6287 DrawPosition(True, NULL);
6290 void PonderNextMoveProc(w, event, prms, nprms)
6298 PonderNextMoveEvent(!appData.ponderNextMove);
6299 #ifndef OPTIONSDIALOG
6300 if (appData.ponderNextMove) {
6301 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6303 XtSetArg(args[0], XtNleftBitmap, None);
6305 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6310 #ifndef OPTIONSDIALOG
6311 void AlwaysQueenProc(w, event, prms, nprms)
6319 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6321 if (appData.alwaysPromoteToQueen) {
6322 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6324 XtSetArg(args[0], XtNleftBitmap, None);
6326 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6330 void AnimateDraggingProc(w, event, prms, nprms)
6338 appData.animateDragging = !appData.animateDragging;
6340 if (appData.animateDragging) {
6341 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6344 XtSetArg(args[0], XtNleftBitmap, None);
6346 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6350 void AnimateMovingProc(w, event, prms, nprms)
6358 appData.animate = !appData.animate;
6360 if (appData.animate) {
6361 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6364 XtSetArg(args[0], XtNleftBitmap, None);
6366 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6370 void AutoflagProc(w, event, prms, nprms)
6378 appData.autoCallFlag = !appData.autoCallFlag;
6380 if (appData.autoCallFlag) {
6381 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6383 XtSetArg(args[0], XtNleftBitmap, None);
6385 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6389 void AutoflipProc(w, event, prms, nprms)
6397 appData.autoFlipView = !appData.autoFlipView;
6399 if (appData.autoFlipView) {
6400 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6402 XtSetArg(args[0], XtNleftBitmap, None);
6404 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6408 void BlindfoldProc(w, event, prms, nprms)
6416 appData.blindfold = !appData.blindfold;
6418 if (appData.blindfold) {
6419 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6421 XtSetArg(args[0], XtNleftBitmap, None);
6423 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6426 DrawPosition(True, NULL);
6429 void TestLegalityProc(w, event, prms, nprms)
6437 appData.testLegality = !appData.testLegality;
6439 if (appData.testLegality) {
6440 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6442 XtSetArg(args[0], XtNleftBitmap, None);
6444 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6449 void FlashMovesProc(w, event, prms, nprms)
6457 if (appData.flashCount == 0) {
6458 appData.flashCount = 3;
6460 appData.flashCount = -appData.flashCount;
6463 if (appData.flashCount > 0) {
6464 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6466 XtSetArg(args[0], XtNleftBitmap, None);
6468 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6473 void HighlightDraggingProc(w, event, prms, nprms)
6481 appData.highlightDragging = !appData.highlightDragging;
6483 if (appData.highlightDragging) {
6484 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6486 XtSetArg(args[0], XtNleftBitmap, None);
6488 XtSetValues(XtNameToWidget(menuBarWidget,
6489 "menuOptions.Highlight Dragging"), args, 1);
6493 void HighlightLastMoveProc(w, event, prms, nprms)
6501 appData.highlightLastMove = !appData.highlightLastMove;
6503 if (appData.highlightLastMove) {
6504 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6506 XtSetArg(args[0], XtNleftBitmap, None);
6508 XtSetValues(XtNameToWidget(menuBarWidget,
6509 "menuOptions.Highlight Last Move"), args, 1);
6512 void HighlightArrowProc(w, event, prms, nprms)
6520 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6522 if (appData.highlightMoveWithArrow) {
6523 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6525 XtSetArg(args[0], XtNleftBitmap, None);
6527 XtSetValues(XtNameToWidget(menuBarWidget,
6528 "menuOptions.Arrow"), args, 1);
6532 void IcsAlarmProc(w, event, prms, nprms)
6540 appData.icsAlarm = !appData.icsAlarm;
6542 if (appData.icsAlarm) {
6543 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6545 XtSetArg(args[0], XtNleftBitmap, None);
6547 XtSetValues(XtNameToWidget(menuBarWidget,
6548 "menuOptions.ICS Alarm"), args, 1);
6552 void MoveSoundProc(w, event, prms, nprms)
6560 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6562 if (appData.ringBellAfterMoves) {
6563 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6565 XtSetArg(args[0], XtNleftBitmap, None);
6567 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6571 void OneClickProc(w, event, prms, nprms)
6579 appData.oneClick = !appData.oneClick;
6581 if (appData.oneClick) {
6582 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6584 XtSetArg(args[0], XtNleftBitmap, None);
6586 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6590 void PeriodicUpdatesProc(w, event, prms, nprms)
6598 PeriodicUpdatesEvent(!appData.periodicUpdates);
6600 if (appData.periodicUpdates) {
6601 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6603 XtSetArg(args[0], XtNleftBitmap, None);
6605 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6609 void PopupExitMessageProc(w, event, prms, nprms)
6617 appData.popupExitMessage = !appData.popupExitMessage;
6619 if (appData.popupExitMessage) {
6620 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6622 XtSetArg(args[0], XtNleftBitmap, None);
6624 XtSetValues(XtNameToWidget(menuBarWidget,
6625 "menuOptions.Popup Exit Message"), args, 1);
6628 void PopupMoveErrorsProc(w, event, prms, nprms)
6636 appData.popupMoveErrors = !appData.popupMoveErrors;
6638 if (appData.popupMoveErrors) {
6639 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6641 XtSetArg(args[0], XtNleftBitmap, None);
6643 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6648 void PremoveProc(w, event, prms, nprms)
6656 appData.premove = !appData.premove;
6658 if (appData.premove) {
6659 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6661 XtSetArg(args[0], XtNleftBitmap, None);
6663 XtSetValues(XtNameToWidget(menuBarWidget,
6664 "menuOptions.Premove"), args, 1);
6668 void ShowCoordsProc(w, event, prms, nprms)
6676 appData.showCoords = !appData.showCoords;
6678 if (appData.showCoords) {
6679 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6681 XtSetArg(args[0], XtNleftBitmap, None);
6683 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6686 DrawPosition(True, NULL);
6689 void ShowThinkingProc(w, event, prms, nprms)
6695 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6696 ShowThinkingEvent();
6699 void HideThinkingProc(w, event, prms, nprms)
6707 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6708 ShowThinkingEvent();
6710 if (appData.hideThinkingFromHuman) {
6711 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6713 XtSetArg(args[0], XtNleftBitmap, None);
6715 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6720 void SaveOnExitProc(w, event, prms, nprms)
6728 saveSettingsOnExit = !saveSettingsOnExit;
6730 if (saveSettingsOnExit) {
6731 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6733 XtSetArg(args[0], XtNleftBitmap, None);
6735 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6739 void SaveSettingsProc(w, event, prms, nprms)
6745 SaveSettings(settingsFileName);
6748 void InfoProc(w, event, prms, nprms)
6755 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6760 void ManProc(w, event, prms, nprms)
6768 if (nprms && *nprms > 0)
6772 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6776 void HintProc(w, event, prms, nprms)
6785 void BookProc(w, event, prms, nprms)
6794 void AboutProc(w, event, prms, nprms)
6802 char *zippy = " (with Zippy code)";
6806 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6807 programVersion, zippy,
6808 "Copyright 1991 Digital Equipment Corporation",
6809 "Enhancements Copyright 1992-2009 Free Software Foundation",
6810 "Enhancements Copyright 2005 Alessandro Scotti",
6811 PACKAGE, " is free software and carries NO WARRANTY;",
6812 "see the file COPYING for more information.");
6813 ErrorPopUp(_("About XBoard"), buf, FALSE);
6816 void DebugProc(w, event, prms, nprms)
6822 appData.debugMode = !appData.debugMode;
6825 void AboutGameProc(w, event, prms, nprms)
6834 void NothingProc(w, event, prms, nprms)
6843 void Iconify(w, event, prms, nprms)
6852 XtSetArg(args[0], XtNiconic, True);
6853 XtSetValues(shellWidget, args, 1);
6856 void DisplayMessage(message, extMessage)
6857 char *message, *extMessage;
6859 /* display a message in the message widget */
6868 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6873 message = extMessage;
6877 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6879 /* need to test if messageWidget already exists, since this function
6880 can also be called during the startup, if for example a Xresource
6881 is not set up correctly */
6884 XtSetArg(arg, XtNlabel, message);
6885 XtSetValues(messageWidget, &arg, 1);
6891 void DisplayTitle(text)
6896 char title[MSG_SIZ];
6899 if (text == NULL) text = "";
6901 if (appData.titleInWindow) {
6903 XtSetArg(args[i], XtNlabel, text); i++;
6904 XtSetValues(titleWidget, args, i);
6907 if (*text != NULLCHAR) {
6908 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6909 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6910 } else if (appData.icsActive) {
6911 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6912 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6913 } else if (appData.cmailGameName[0] != NULLCHAR) {
6914 snprintf(icon, sizeof(icon), "%s", "CMail");
6915 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6917 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6918 } else if (gameInfo.variant == VariantGothic) {
6919 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6920 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6923 } else if (gameInfo.variant == VariantFalcon) {
6924 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6925 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6927 } else if (appData.noChessProgram) {
6928 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6929 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6931 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6932 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6935 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6936 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6937 XtSetValues(shellWidget, args, i);
6942 DisplayError(message, error)
6949 if (appData.debugMode || appData.matchMode) {
6950 fprintf(stderr, "%s: %s\n", programName, message);
6953 if (appData.debugMode || appData.matchMode) {
6954 fprintf(stderr, "%s: %s: %s\n",
6955 programName, message, strerror(error));
6957 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6960 ErrorPopUp(_("Error"), message, FALSE);
6964 void DisplayMoveError(message)
6969 DrawPosition(FALSE, NULL);
6970 if (appData.debugMode || appData.matchMode) {
6971 fprintf(stderr, "%s: %s\n", programName, message);
6973 if (appData.popupMoveErrors) {
6974 ErrorPopUp(_("Error"), message, FALSE);
6976 DisplayMessage(message, "");
6981 void DisplayFatalError(message, error, status)
6987 errorExitStatus = status;
6989 fprintf(stderr, "%s: %s\n", programName, message);
6991 fprintf(stderr, "%s: %s: %s\n",
6992 programName, message, strerror(error));
6993 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6996 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6997 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7003 void DisplayInformation(message)
7007 ErrorPopUp(_("Information"), message, TRUE);
7010 void DisplayNote(message)
7014 ErrorPopUp(_("Note"), message, FALSE);
7018 NullXErrorCheck(dpy, error_event)
7020 XErrorEvent *error_event;
7025 void DisplayIcsInteractionTitle(message)
7028 if (oldICSInteractionTitle == NULL) {
7029 /* Magic to find the old window title, adapted from vim */
7030 char *wina = getenv("WINDOWID");
7032 Window win = (Window) atoi(wina);
7033 Window root, parent, *children;
7034 unsigned int nchildren;
7035 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7037 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7038 if (!XQueryTree(xDisplay, win, &root, &parent,
7039 &children, &nchildren)) break;
7040 if (children) XFree((void *)children);
7041 if (parent == root || parent == 0) break;
7044 XSetErrorHandler(oldHandler);
7046 if (oldICSInteractionTitle == NULL) {
7047 oldICSInteractionTitle = "xterm";
7050 printf("\033]0;%s\007", message);
7054 char pendingReplyPrefix[MSG_SIZ];
7055 ProcRef pendingReplyPR;
7057 void AskQuestionProc(w, event, prms, nprms)
7064 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7068 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7071 void AskQuestionPopDown()
7073 if (!askQuestionUp) return;
7074 XtPopdown(askQuestionShell);
7075 XtDestroyWidget(askQuestionShell);
7076 askQuestionUp = False;
7079 void AskQuestionReplyAction(w, event, prms, nprms)
7089 reply = XawDialogGetValueString(w = XtParent(w));
7090 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7091 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7092 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7093 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7094 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7095 AskQuestionPopDown();
7097 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7100 void AskQuestionCallback(w, client_data, call_data)
7102 XtPointer client_data, call_data;
7107 XtSetArg(args[0], XtNlabel, &name);
7108 XtGetValues(w, args, 1);
7110 if (strcmp(name, _("cancel")) == 0) {
7111 AskQuestionPopDown();
7113 AskQuestionReplyAction(w, NULL, NULL, NULL);
7117 void AskQuestion(title, question, replyPrefix, pr)
7118 char *title, *question, *replyPrefix;
7122 Widget popup, layout, dialog, edit;
7128 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7129 pendingReplyPR = pr;
7132 XtSetArg(args[i], XtNresizable, True); i++;
7133 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7134 askQuestionShell = popup =
7135 XtCreatePopupShell(title, transientShellWidgetClass,
7136 shellWidget, args, i);
7139 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7140 layoutArgs, XtNumber(layoutArgs));
7143 XtSetArg(args[i], XtNlabel, question); i++;
7144 XtSetArg(args[i], XtNvalue, ""); i++;
7145 XtSetArg(args[i], XtNborderWidth, 0); i++;
7146 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7149 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7150 (XtPointer) dialog);
7151 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7152 (XtPointer) dialog);
7154 XtRealizeWidget(popup);
7155 CatchDeleteWindow(popup, "AskQuestionPopDown");
7157 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7158 &x, &y, &win_x, &win_y, &mask);
7160 XtSetArg(args[0], XtNx, x - 10);
7161 XtSetArg(args[1], XtNy, y - 30);
7162 XtSetValues(popup, args, 2);
7164 XtPopup(popup, XtGrabExclusive);
7165 askQuestionUp = True;
7167 edit = XtNameToWidget(dialog, "*value");
7168 XtSetKeyboardFocus(popup, edit);
7176 if (*name == NULLCHAR) {
7178 } else if (strcmp(name, "$") == 0) {
7179 putc(BELLCHAR, stderr);
7182 char *prefix = "", *sep = "";
7183 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7184 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7192 PlaySound(appData.soundMove);
7198 PlaySound(appData.soundIcsWin);
7204 PlaySound(appData.soundIcsLoss);
7210 PlaySound(appData.soundIcsDraw);
7214 PlayIcsUnfinishedSound()
7216 PlaySound(appData.soundIcsUnfinished);
7222 PlaySound(appData.soundIcsAlarm);
7228 system("stty echo");
7234 system("stty -echo");
7238 Colorize(cc, continuation)
7243 int count, outCount, error;
7245 if (textColors[(int)cc].bg > 0) {
7246 if (textColors[(int)cc].fg > 0) {
7247 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7248 textColors[(int)cc].fg, textColors[(int)cc].bg);
7250 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7251 textColors[(int)cc].bg);
7254 if (textColors[(int)cc].fg > 0) {
7255 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7256 textColors[(int)cc].fg);
7258 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7261 count = strlen(buf);
7262 outCount = OutputToProcess(NoProc, buf, count, &error);
7263 if (outCount < count) {
7264 DisplayFatalError(_("Error writing to display"), error, 1);
7267 if (continuation) return;
7270 PlaySound(appData.soundShout);
7273 PlaySound(appData.soundSShout);
7276 PlaySound(appData.soundChannel1);
7279 PlaySound(appData.soundChannel);
7282 PlaySound(appData.soundKibitz);
7285 PlaySound(appData.soundTell);
7287 case ColorChallenge:
7288 PlaySound(appData.soundChallenge);
7291 PlaySound(appData.soundRequest);
7294 PlaySound(appData.soundSeek);
7305 return getpwuid(getuid())->pw_name;
7309 ExpandPathName(path)
7312 static char static_buf[4*MSG_SIZ];
7313 char *d, *s, buf[4*MSG_SIZ];
7319 while (*s && isspace(*s))
7328 if (*(s+1) == '/') {
7329 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7333 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7334 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7335 pwd = getpwnam(buf);
7338 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7342 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7343 strcat(d, strchr(s+1, '/'));
7347 safeStrCpy(d, s, 4*MSG_SIZ );
7354 static char host_name[MSG_SIZ];
7356 #if HAVE_GETHOSTNAME
7357 gethostname(host_name, MSG_SIZ);
7359 #else /* not HAVE_GETHOSTNAME */
7360 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7361 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7363 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7365 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7366 #endif /* not HAVE_GETHOSTNAME */
7369 XtIntervalId delayedEventTimerXID = 0;
7370 DelayedEventCallback delayedEventCallback = 0;
7375 delayedEventTimerXID = 0;
7376 delayedEventCallback();
7380 ScheduleDelayedEvent(cb, millisec)
7381 DelayedEventCallback cb; long millisec;
7383 if(delayedEventTimerXID && delayedEventCallback == cb)
7384 // [HGM] alive: replace, rather than add or flush identical event
7385 XtRemoveTimeOut(delayedEventTimerXID);
7386 delayedEventCallback = cb;
7387 delayedEventTimerXID =
7388 XtAppAddTimeOut(appContext, millisec,
7389 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7392 DelayedEventCallback
7395 if (delayedEventTimerXID) {
7396 return delayedEventCallback;
7403 CancelDelayedEvent()
7405 if (delayedEventTimerXID) {
7406 XtRemoveTimeOut(delayedEventTimerXID);
7407 delayedEventTimerXID = 0;
7411 XtIntervalId loadGameTimerXID = 0;
7413 int LoadGameTimerRunning()
7415 return loadGameTimerXID != 0;
7418 int StopLoadGameTimer()
7420 if (loadGameTimerXID != 0) {
7421 XtRemoveTimeOut(loadGameTimerXID);
7422 loadGameTimerXID = 0;
7430 LoadGameTimerCallback(arg, id)
7434 loadGameTimerXID = 0;
7439 StartLoadGameTimer(millisec)
7443 XtAppAddTimeOut(appContext, millisec,
7444 (XtTimerCallbackProc) LoadGameTimerCallback,
7448 XtIntervalId analysisClockXID = 0;
7451 AnalysisClockCallback(arg, id)
7455 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7456 || appData.icsEngineAnalyze) { // [DM]
7457 AnalysisPeriodicEvent(0);
7458 StartAnalysisClock();
7463 StartAnalysisClock()
7466 XtAppAddTimeOut(appContext, 2000,
7467 (XtTimerCallbackProc) AnalysisClockCallback,
7471 XtIntervalId clockTimerXID = 0;
7473 int ClockTimerRunning()
7475 return clockTimerXID != 0;
7478 int StopClockTimer()
7480 if (clockTimerXID != 0) {
7481 XtRemoveTimeOut(clockTimerXID);
7490 ClockTimerCallback(arg, id)
7499 StartClockTimer(millisec)
7503 XtAppAddTimeOut(appContext, millisec,
7504 (XtTimerCallbackProc) ClockTimerCallback,
7509 DisplayTimerLabel(w, color, timer, highlight)
7518 /* check for low time warning */
7519 Pixel foregroundOrWarningColor = timerForegroundPixel;
7522 appData.lowTimeWarning &&
7523 (timer / 1000) < appData.icsAlarmTime)
7524 foregroundOrWarningColor = lowTimeWarningColor;
7526 if (appData.clockMode) {
7527 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7528 XtSetArg(args[0], XtNlabel, buf);
7530 snprintf(buf, MSG_SIZ, "%s ", color);
7531 XtSetArg(args[0], XtNlabel, buf);
7536 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7537 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7539 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7540 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7543 XtSetValues(w, args, 3);
7547 DisplayWhiteClock(timeRemaining, highlight)
7553 if(appData.noGUI) return;
7554 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7555 if (highlight && iconPixmap == bIconPixmap) {
7556 iconPixmap = wIconPixmap;
7557 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7558 XtSetValues(shellWidget, args, 1);
7563 DisplayBlackClock(timeRemaining, highlight)
7569 if(appData.noGUI) return;
7570 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7571 if (highlight && iconPixmap == wIconPixmap) {
7572 iconPixmap = bIconPixmap;
7573 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7574 XtSetValues(shellWidget, args, 1);
7592 int StartChildProcess(cmdLine, dir, pr)
7599 int to_prog[2], from_prog[2];
7603 if (appData.debugMode) {
7604 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7607 /* We do NOT feed the cmdLine to the shell; we just
7608 parse it into blank-separated arguments in the
7609 most simple-minded way possible.
7612 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7615 while(*p == ' ') p++;
7617 if(*p == '"' || *p == '\'')
7618 p = strchr(++argv[i-1], *p);
7619 else p = strchr(p, ' ');
7620 if (p == NULL) break;
7625 SetUpChildIO(to_prog, from_prog);
7627 if ((pid = fork()) == 0) {
7629 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7630 close(to_prog[1]); // first close the unused pipe ends
7631 close(from_prog[0]);
7632 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7633 dup2(from_prog[1], 1);
7634 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7635 close(from_prog[1]); // and closing again loses one of the pipes!
7636 if(fileno(stderr) >= 2) // better safe than sorry...
7637 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7639 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7644 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7646 execvp(argv[0], argv);
7648 /* If we get here, exec failed */
7653 /* Parent process */
7655 close(from_prog[1]);
7657 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7660 cp->fdFrom = from_prog[0];
7661 cp->fdTo = to_prog[1];
7666 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7667 static RETSIGTYPE AlarmCallBack(int n)
7673 DestroyChildProcess(pr, signalType)
7677 ChildProc *cp = (ChildProc *) pr;
7679 if (cp->kind != CPReal) return;
7681 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7682 signal(SIGALRM, AlarmCallBack);
7684 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7685 kill(cp->pid, SIGKILL); // kill it forcefully
7686 wait((int *) 0); // and wait again
7690 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7692 /* Process is exiting either because of the kill or because of
7693 a quit command sent by the backend; either way, wait for it to die.
7702 InterruptChildProcess(pr)
7705 ChildProc *cp = (ChildProc *) pr;
7707 if (cp->kind != CPReal) return;
7708 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7711 int OpenTelnet(host, port, pr)
7716 char cmdLine[MSG_SIZ];
7718 if (port[0] == NULLCHAR) {
7719 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7721 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7723 return StartChildProcess(cmdLine, "", pr);
7726 int OpenTCP(host, port, pr)
7732 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7733 #else /* !OMIT_SOCKETS */
7734 struct addrinfo hints;
7735 struct addrinfo *ais, *ai;
7740 memset(&hints, 0, sizeof(hints));
7741 hints.ai_family = AF_UNSPEC;
7742 hints.ai_socktype = SOCK_STREAM;
7744 error = getaddrinfo(host, port, &hints, &ais);
7746 /* a getaddrinfo error is not an errno, so can't return it */
7747 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7748 host, port, gai_strerror(error));
7752 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7753 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7757 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7770 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7776 #endif /* !OMIT_SOCKETS */
7781 int OpenCommPort(name, pr)
7788 fd = open(name, 2, 0);
7789 if (fd < 0) return errno;
7791 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7801 int OpenLoopback(pr)
7807 SetUpChildIO(to, from);
7809 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7812 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7819 int OpenRcmd(host, user, cmd, pr)
7820 char *host, *user, *cmd;
7823 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7827 #define INPUT_SOURCE_BUF_SIZE 8192
7836 char buf[INPUT_SOURCE_BUF_SIZE];
7841 DoInputCallback(closure, source, xid)
7846 InputSource *is = (InputSource *) closure;
7851 if (is->lineByLine) {
7852 count = read(is->fd, is->unused,
7853 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7855 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7858 is->unused += count;
7860 while (p < is->unused) {
7861 q = memchr(p, '\n', is->unused - p);
7862 if (q == NULL) break;
7864 (is->func)(is, is->closure, p, q - p, 0);
7868 while (p < is->unused) {
7873 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7878 (is->func)(is, is->closure, is->buf, count, error);
7882 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7889 ChildProc *cp = (ChildProc *) pr;
7891 is = (InputSource *) calloc(1, sizeof(InputSource));
7892 is->lineByLine = lineByLine;
7896 is->fd = fileno(stdin);
7898 is->kind = cp->kind;
7899 is->fd = cp->fdFrom;
7902 is->unused = is->buf;
7905 is->xid = XtAppAddInput(appContext, is->fd,
7906 (XtPointer) (XtInputReadMask),
7907 (XtInputCallbackProc) DoInputCallback,
7909 is->closure = closure;
7910 return (InputSourceRef) is;
7914 RemoveInputSource(isr)
7917 InputSource *is = (InputSource *) isr;
7919 if (is->xid == 0) return;
7920 XtRemoveInput(is->xid);
7924 int OutputToProcess(pr, message, count, outError)
7930 static int line = 0;
7931 ChildProc *cp = (ChildProc *) pr;
7936 if (appData.noJoin || !appData.useInternalWrap)
7937 outCount = fwrite(message, 1, count, stdout);
7940 int width = get_term_width();
7941 int len = wrap(NULL, message, count, width, &line);
7942 char *msg = malloc(len);
7946 outCount = fwrite(message, 1, count, stdout);
7949 dbgchk = wrap(msg, message, count, width, &line);
7950 if (dbgchk != len && appData.debugMode)
7951 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7952 outCount = fwrite(msg, 1, dbgchk, stdout);
7958 outCount = write(cp->fdTo, message, count);
7968 /* Output message to process, with "ms" milliseconds of delay
7969 between each character. This is needed when sending the logon
7970 script to ICC, which for some reason doesn't like the
7971 instantaneous send. */
7972 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7979 ChildProc *cp = (ChildProc *) pr;
7984 r = write(cp->fdTo, message++, 1);
7997 /**** Animation code by Hugh Fisher, DCS, ANU.
7999 Known problem: if a window overlapping the board is
8000 moved away while a piece is being animated underneath,
8001 the newly exposed area won't be updated properly.
8002 I can live with this.
8004 Known problem: if you look carefully at the animation
8005 of pieces in mono mode, they are being drawn as solid
8006 shapes without interior detail while moving. Fixing
8007 this would be a major complication for minimal return.
8010 /* Masks for XPM pieces. Black and white pieces can have
8011 different shapes, but in the interest of retaining my
8012 sanity pieces must have the same outline on both light
8013 and dark squares, and all pieces must use the same
8014 background square colors/images. */
8016 static int xpmDone = 0;
8019 CreateAnimMasks (pieceDepth)
8026 unsigned long plane;
8029 /* Need a bitmap just to get a GC with right depth */
8030 buf = XCreatePixmap(xDisplay, xBoardWindow,
8032 values.foreground = 1;
8033 values.background = 0;
8034 /* Don't use XtGetGC, not read only */
8035 maskGC = XCreateGC(xDisplay, buf,
8036 GCForeground | GCBackground, &values);
8037 XFreePixmap(xDisplay, buf);
8039 buf = XCreatePixmap(xDisplay, xBoardWindow,
8040 squareSize, squareSize, pieceDepth);
8041 values.foreground = XBlackPixel(xDisplay, xScreen);
8042 values.background = XWhitePixel(xDisplay, xScreen);
8043 bufGC = XCreateGC(xDisplay, buf,
8044 GCForeground | GCBackground, &values);
8046 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8047 /* Begin with empty mask */
8048 if(!xpmDone) // [HGM] pieces: keep using existing
8049 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8050 squareSize, squareSize, 1);
8051 XSetFunction(xDisplay, maskGC, GXclear);
8052 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8053 0, 0, squareSize, squareSize);
8055 /* Take a copy of the piece */
8060 XSetFunction(xDisplay, bufGC, GXcopy);
8061 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8063 0, 0, squareSize, squareSize, 0, 0);
8065 /* XOR the background (light) over the piece */
8066 XSetFunction(xDisplay, bufGC, GXxor);
8068 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8069 0, 0, squareSize, squareSize, 0, 0);
8071 XSetForeground(xDisplay, bufGC, lightSquareColor);
8072 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8075 /* We now have an inverted piece image with the background
8076 erased. Construct mask by just selecting all the non-zero
8077 pixels - no need to reconstruct the original image. */
8078 XSetFunction(xDisplay, maskGC, GXor);
8080 /* Might be quicker to download an XImage and create bitmap
8081 data from it rather than this N copies per piece, but it
8082 only takes a fraction of a second and there is a much
8083 longer delay for loading the pieces. */
8084 for (n = 0; n < pieceDepth; n ++) {
8085 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8086 0, 0, squareSize, squareSize,
8092 XFreePixmap(xDisplay, buf);
8093 XFreeGC(xDisplay, bufGC);
8094 XFreeGC(xDisplay, maskGC);
8098 InitAnimState (anim, info)
8100 XWindowAttributes * info;
8105 /* Each buffer is square size, same depth as window */
8106 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8107 squareSize, squareSize, info->depth);
8108 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8109 squareSize, squareSize, info->depth);
8111 /* Create a plain GC for blitting */
8112 mask = GCForeground | GCBackground | GCFunction |
8113 GCPlaneMask | GCGraphicsExposures;
8114 values.foreground = XBlackPixel(xDisplay, xScreen);
8115 values.background = XWhitePixel(xDisplay, xScreen);
8116 values.function = GXcopy;
8117 values.plane_mask = AllPlanes;
8118 values.graphics_exposures = False;
8119 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8121 /* Piece will be copied from an existing context at
8122 the start of each new animation/drag. */
8123 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8125 /* Outline will be a read-only copy of an existing */
8126 anim->outlineGC = None;
8132 XWindowAttributes info;
8134 if (xpmDone && gameInfo.variant == oldVariant) return;
8135 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8136 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8138 InitAnimState(&game, &info);
8139 InitAnimState(&player, &info);
8141 /* For XPM pieces, we need bitmaps to use as masks. */
8143 CreateAnimMasks(info.depth), xpmDone = 1;
8148 static Boolean frameWaiting;
8150 static RETSIGTYPE FrameAlarm (sig)
8153 frameWaiting = False;
8154 /* In case System-V style signals. Needed?? */
8155 signal(SIGALRM, FrameAlarm);
8162 struct itimerval delay;
8164 XSync(xDisplay, False);
8167 frameWaiting = True;
8168 signal(SIGALRM, FrameAlarm);
8169 delay.it_interval.tv_sec =
8170 delay.it_value.tv_sec = time / 1000;
8171 delay.it_interval.tv_usec =
8172 delay.it_value.tv_usec = (time % 1000) * 1000;
8173 setitimer(ITIMER_REAL, &delay, NULL);
8174 while (frameWaiting) pause();
8175 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8176 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8177 setitimer(ITIMER_REAL, &delay, NULL);
8187 XSync(xDisplay, False);
8189 usleep(time * 1000);
8194 /* Convert board position to corner of screen rect and color */
8197 ScreenSquare(column, row, pt, color)
8198 int column; int row; XPoint * pt; int * color;
8201 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8202 pt->y = lineGap + row * (squareSize + lineGap);
8204 pt->x = lineGap + column * (squareSize + lineGap);
8205 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8207 *color = SquareColor(row, column);
8210 /* Convert window coords to square */
8213 BoardSquare(x, y, column, row)
8214 int x; int y; int * column; int * row;
8216 *column = EventToSquare(x, BOARD_WIDTH);
8217 if (flipView && *column >= 0)
8218 *column = BOARD_WIDTH - 1 - *column;
8219 *row = EventToSquare(y, BOARD_HEIGHT);
8220 if (!flipView && *row >= 0)
8221 *row = BOARD_HEIGHT - 1 - *row;
8226 #undef Max /* just in case */
8228 #define Max(a, b) ((a) > (b) ? (a) : (b))
8229 #define Min(a, b) ((a) < (b) ? (a) : (b))
8232 SetRect(rect, x, y, width, height)
8233 XRectangle * rect; int x; int y; int width; int height;
8237 rect->width = width;
8238 rect->height = height;
8241 /* Test if two frames overlap. If they do, return
8242 intersection rect within old and location of
8243 that rect within new. */
8246 Intersect(old, new, size, area, pt)
8247 XPoint * old; XPoint * new;
8248 int size; XRectangle * area; XPoint * pt;
8250 if (old->x > new->x + size || new->x > old->x + size ||
8251 old->y > new->y + size || new->y > old->y + size) {
8254 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8255 size - abs(old->x - new->x), size - abs(old->y - new->y));
8256 pt->x = Max(old->x - new->x, 0);
8257 pt->y = Max(old->y - new->y, 0);
8262 /* For two overlapping frames, return the rect(s)
8263 in the old that do not intersect with the new. */
8266 CalcUpdateRects(old, new, size, update, nUpdates)
8267 XPoint * old; XPoint * new; int size;
8268 XRectangle update[]; int * nUpdates;
8272 /* If old = new (shouldn't happen) then nothing to draw */
8273 if (old->x == new->x && old->y == new->y) {
8277 /* Work out what bits overlap. Since we know the rects
8278 are the same size we don't need a full intersect calc. */
8280 /* Top or bottom edge? */
8281 if (new->y > old->y) {
8282 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8284 } else if (old->y > new->y) {
8285 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8286 size, old->y - new->y);
8289 /* Left or right edge - don't overlap any update calculated above. */
8290 if (new->x > old->x) {
8291 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8292 new->x - old->x, size - abs(new->y - old->y));
8294 } else if (old->x > new->x) {
8295 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8296 old->x - new->x, size - abs(new->y - old->y));
8303 /* Generate a series of frame coords from start->mid->finish.
8304 The movement rate doubles until the half way point is
8305 reached, then halves back down to the final destination,
8306 which gives a nice slow in/out effect. The algorithmn
8307 may seem to generate too many intermediates for short
8308 moves, but remember that the purpose is to attract the
8309 viewers attention to the piece about to be moved and
8310 then to where it ends up. Too few frames would be less
8314 Tween(start, mid, finish, factor, frames, nFrames)
8315 XPoint * start; XPoint * mid;
8316 XPoint * finish; int factor;
8317 XPoint frames[]; int * nFrames;
8319 int fraction, n, count;
8323 /* Slow in, stepping 1/16th, then 1/8th, ... */
8325 for (n = 0; n < factor; n++)
8327 for (n = 0; n < factor; n++) {
8328 frames[count].x = start->x + (mid->x - start->x) / fraction;
8329 frames[count].y = start->y + (mid->y - start->y) / fraction;
8331 fraction = fraction / 2;
8335 frames[count] = *mid;
8338 /* Slow out, stepping 1/2, then 1/4, ... */
8340 for (n = 0; n < factor; n++) {
8341 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8342 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8344 fraction = fraction * 2;
8349 /* Draw a piece on the screen without disturbing what's there */
8352 SelectGCMask(piece, clip, outline, mask)
8353 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8357 /* Bitmap for piece being moved. */
8358 if (appData.monoMode) {
8359 *mask = *pieceToSolid(piece);
8360 } else if (useImages) {
8362 *mask = xpmMask[piece];
8364 *mask = ximMaskPm[piece];
8367 *mask = *pieceToSolid(piece);
8370 /* GC for piece being moved. Square color doesn't matter, but
8371 since it gets modified we make a copy of the original. */
8373 if (appData.monoMode)
8378 if (appData.monoMode)
8383 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8385 /* Outline only used in mono mode and is not modified */
8387 *outline = bwPieceGC;
8389 *outline = wbPieceGC;
8393 OverlayPiece(piece, clip, outline, dest)
8394 ChessSquare piece; GC clip; GC outline; Drawable dest;
8399 /* Draw solid rectangle which will be clipped to shape of piece */
8400 XFillRectangle(xDisplay, dest, clip,
8401 0, 0, squareSize, squareSize);
8402 if (appData.monoMode)
8403 /* Also draw outline in contrasting color for black
8404 on black / white on white cases */
8405 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8406 0, 0, squareSize, squareSize, 0, 0, 1);
8408 /* Copy the piece */
8413 if(appData.upsideDown && flipView) kind ^= 2;
8414 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8416 0, 0, squareSize, squareSize,
8421 /* Animate the movement of a single piece */
8424 BeginAnimation(anim, piece, startColor, start)
8432 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8433 /* The old buffer is initialised with the start square (empty) */
8434 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8435 anim->prevFrame = *start;
8437 /* The piece will be drawn using its own bitmap as a matte */
8438 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8439 XSetClipMask(xDisplay, anim->pieceGC, mask);
8443 AnimationFrame(anim, frame, piece)
8448 XRectangle updates[4];
8453 /* Save what we are about to draw into the new buffer */
8454 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8455 frame->x, frame->y, squareSize, squareSize,
8458 /* Erase bits of the previous frame */
8459 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8460 /* Where the new frame overlapped the previous,
8461 the contents in newBuf are wrong. */
8462 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8463 overlap.x, overlap.y,
8464 overlap.width, overlap.height,
8466 /* Repaint the areas in the old that don't overlap new */
8467 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8468 for (i = 0; i < count; i++)
8469 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8470 updates[i].x - anim->prevFrame.x,
8471 updates[i].y - anim->prevFrame.y,
8472 updates[i].width, updates[i].height,
8473 updates[i].x, updates[i].y);
8475 /* Easy when no overlap */
8476 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8477 0, 0, squareSize, squareSize,
8478 anim->prevFrame.x, anim->prevFrame.y);
8481 /* Save this frame for next time round */
8482 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8483 0, 0, squareSize, squareSize,
8485 anim->prevFrame = *frame;
8487 /* Draw piece over original screen contents, not current,
8488 and copy entire rect. Wipes out overlapping piece images. */
8489 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8490 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8491 0, 0, squareSize, squareSize,
8492 frame->x, frame->y);
8496 EndAnimation (anim, finish)
8500 XRectangle updates[4];
8505 /* The main code will redraw the final square, so we
8506 only need to erase the bits that don't overlap. */
8507 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8508 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8509 for (i = 0; i < count; i++)
8510 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8511 updates[i].x - anim->prevFrame.x,
8512 updates[i].y - anim->prevFrame.y,
8513 updates[i].width, updates[i].height,
8514 updates[i].x, updates[i].y);
8516 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8517 0, 0, squareSize, squareSize,
8518 anim->prevFrame.x, anim->prevFrame.y);
8523 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8525 ChessSquare piece; int startColor;
8526 XPoint * start; XPoint * finish;
8527 XPoint frames[]; int nFrames;
8531 BeginAnimation(anim, piece, startColor, start);
8532 for (n = 0; n < nFrames; n++) {
8533 AnimationFrame(anim, &(frames[n]), piece);
8534 FrameDelay(appData.animSpeed);
8536 EndAnimation(anim, finish);
8540 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8543 ChessSquare piece = board[fromY][toY];
8544 board[fromY][toY] = EmptySquare;
8545 DrawPosition(FALSE, board);
8547 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8548 y = lineGap + toY * (squareSize + lineGap);
8550 x = lineGap + toX * (squareSize + lineGap);
8551 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8553 for(i=1; i<4*kFactor; i++) {
8554 int r = squareSize * 9 * i/(20*kFactor - 5);
8555 XFillArc(xDisplay, xBoardWindow, highlineGC,
8556 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8557 FrameDelay(appData.animSpeed);
8559 board[fromY][toY] = piece;
8562 /* Main control logic for deciding what to animate and how */
8565 AnimateMove(board, fromX, fromY, toX, toY)
8574 XPoint start, finish, mid;
8575 XPoint frames[kFactor * 2 + 1];
8576 int nFrames, startColor, endColor;
8578 /* Are we animating? */
8579 if (!appData.animate || appData.blindfold)
8582 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8583 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8584 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8586 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8587 piece = board[fromY][fromX];
8588 if (piece >= EmptySquare) return;
8593 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8596 if (appData.debugMode) {
8597 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8598 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8599 piece, fromX, fromY, toX, toY); }
8601 ScreenSquare(fromX, fromY, &start, &startColor);
8602 ScreenSquare(toX, toY, &finish, &endColor);
8605 /* Knight: make straight movement then diagonal */
8606 if (abs(toY - fromY) < abs(toX - fromX)) {
8607 mid.x = start.x + (finish.x - start.x) / 2;
8611 mid.y = start.y + (finish.y - start.y) / 2;
8614 mid.x = start.x + (finish.x - start.x) / 2;
8615 mid.y = start.y + (finish.y - start.y) / 2;
8618 /* Don't use as many frames for very short moves */
8619 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8620 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8622 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8623 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8624 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8626 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8627 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8630 /* Be sure end square is redrawn */
8631 damage[0][toY][toX] = True;
8635 DragPieceBegin(x, y)
8638 int boardX, boardY, color;
8641 /* Are we animating? */
8642 if (!appData.animateDragging || appData.blindfold)
8645 /* Figure out which square we start in and the
8646 mouse position relative to top left corner. */
8647 BoardSquare(x, y, &boardX, &boardY);
8648 player.startBoardX = boardX;
8649 player.startBoardY = boardY;
8650 ScreenSquare(boardX, boardY, &corner, &color);
8651 player.startSquare = corner;
8652 player.startColor = color;
8653 /* As soon as we start dragging, the piece will jump slightly to
8654 be centered over the mouse pointer. */
8655 player.mouseDelta.x = squareSize/2;
8656 player.mouseDelta.y = squareSize/2;
8657 /* Initialise animation */
8658 player.dragPiece = PieceForSquare(boardX, boardY);
8660 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8661 player.dragActive = True;
8662 BeginAnimation(&player, player.dragPiece, color, &corner);
8663 /* Mark this square as needing to be redrawn. Note that
8664 we don't remove the piece though, since logically (ie
8665 as seen by opponent) the move hasn't been made yet. */
8666 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8667 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8668 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8669 corner.x, corner.y, squareSize, squareSize,
8670 0, 0); // [HGM] zh: unstack in stead of grab
8671 if(gatingPiece != EmptySquare) {
8672 /* Kludge alert: When gating we want the introduced
8673 piece to appear on the from square. To generate an
8674 image of it, we draw it on the board, copy the image,
8675 and draw the original piece again. */
8676 ChessSquare piece = boards[currentMove][boardY][boardX];
8677 DrawSquare(boardY, boardX, gatingPiece, 0);
8678 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8679 corner.x, corner.y, squareSize, squareSize, 0, 0);
8680 DrawSquare(boardY, boardX, piece, 0);
8682 damage[0][boardY][boardX] = True;
8684 player.dragActive = False;
8689 ChangeDragPiece(ChessSquare piece)
8692 player.dragPiece = piece;
8693 /* The piece will be drawn using its own bitmap as a matte */
8694 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8695 XSetClipMask(xDisplay, player.pieceGC, mask);
8704 /* Are we animating? */
8705 if (!appData.animateDragging || appData.blindfold)
8709 if (! player.dragActive)
8711 /* Move piece, maintaining same relative position
8712 of mouse within square */
8713 corner.x = x - player.mouseDelta.x;
8714 corner.y = y - player.mouseDelta.y;
8715 AnimationFrame(&player, &corner, player.dragPiece);
8717 if (appData.highlightDragging) {
8719 BoardSquare(x, y, &boardX, &boardY);
8720 SetHighlights(fromX, fromY, boardX, boardY);
8729 int boardX, boardY, color;
8732 /* Are we animating? */
8733 if (!appData.animateDragging || appData.blindfold)
8737 if (! player.dragActive)
8739 /* Last frame in sequence is square piece is
8740 placed on, which may not match mouse exactly. */
8741 BoardSquare(x, y, &boardX, &boardY);
8742 ScreenSquare(boardX, boardY, &corner, &color);
8743 EndAnimation(&player, &corner);
8745 /* Be sure end square is redrawn */
8746 damage[0][boardY][boardX] = True;
8748 /* This prevents weird things happening with fast successive
8749 clicks which on my Sun at least can cause motion events
8750 without corresponding press/release. */
8751 player.dragActive = False;
8754 /* Handle expose event while piece being dragged */
8759 if (!player.dragActive || appData.blindfold)
8762 /* What we're doing: logically, the move hasn't been made yet,
8763 so the piece is still in it's original square. But visually
8764 it's being dragged around the board. So we erase the square
8765 that the piece is on and draw it at the last known drag point. */
8766 BlankSquare(player.startSquare.x, player.startSquare.y,
8767 player.startColor, EmptySquare, xBoardWindow, 1);
8768 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8769 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8772 #include <sys/ioctl.h>
8773 int get_term_width()
8775 int fd, default_width;
8778 default_width = 79; // this is FICS default anyway...
8780 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8782 if (!ioctl(fd, TIOCGSIZE, &win))
8783 default_width = win.ts_cols;
8784 #elif defined(TIOCGWINSZ)
8786 if (!ioctl(fd, TIOCGWINSZ, &win))
8787 default_width = win.ws_col;
8789 return default_width;
8795 static int old_width = 0;
8796 int new_width = get_term_width();
8798 if (old_width != new_width)
8799 ics_printf("set width %d\n", new_width);
8800 old_width = new_width;
8803 void NotifyFrontendLogin()
8808 /* [AS] Arrow highlighting support */
8810 static double A_WIDTH = 5; /* Width of arrow body */
8812 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8813 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8815 static double Sqr( double x )
8820 static int Round( double x )
8822 return (int) (x + 0.5);
8825 void SquareToPos(int rank, int file, int *x, int *y)
8828 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8829 *y = lineGap + rank * (squareSize + lineGap);
8831 *x = lineGap + file * (squareSize + lineGap);
8832 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8836 /* Draw an arrow between two points using current settings */
8837 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8840 double dx, dy, j, k, x, y;
8843 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8845 arrow[0].x = s_x + A_WIDTH + 0.5;
8848 arrow[1].x = s_x + A_WIDTH + 0.5;
8849 arrow[1].y = d_y - h;
8851 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8852 arrow[2].y = d_y - h;
8857 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8858 arrow[5].y = d_y - h;
8860 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8861 arrow[4].y = d_y - h;
8863 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8866 else if( d_y == s_y ) {
8867 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8870 arrow[0].y = s_y + A_WIDTH + 0.5;
8872 arrow[1].x = d_x - w;
8873 arrow[1].y = s_y + A_WIDTH + 0.5;
8875 arrow[2].x = d_x - w;
8876 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8881 arrow[5].x = d_x - w;
8882 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8884 arrow[4].x = d_x - w;
8885 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8888 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8891 /* [AS] Needed a lot of paper for this! :-) */
8892 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8893 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8895 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8897 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8902 arrow[0].x = Round(x - j);
8903 arrow[0].y = Round(y + j*dx);
8905 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8906 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8909 x = (double) d_x - k;
8910 y = (double) d_y - k*dy;
8913 x = (double) d_x + k;
8914 y = (double) d_y + k*dy;
8917 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8919 arrow[6].x = Round(x - j);
8920 arrow[6].y = Round(y + j*dx);
8922 arrow[2].x = Round(arrow[6].x + 2*j);
8923 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8925 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8926 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8931 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8932 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8935 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8936 // Polygon( hdc, arrow, 7 );
8939 /* [AS] Draw an arrow between two squares */
8940 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8942 int s_x, s_y, d_x, d_y, hor, vert, i;
8944 if( s_col == d_col && s_row == d_row ) {
8948 /* Get source and destination points */
8949 SquareToPos( s_row, s_col, &s_x, &s_y);
8950 SquareToPos( d_row, d_col, &d_x, &d_y);
8953 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8955 else if( d_y < s_y ) {
8956 d_y += squareSize / 2 + squareSize / 4;
8959 d_y += squareSize / 2;
8963 d_x += squareSize / 2 - squareSize / 4;
8965 else if( d_x < s_x ) {
8966 d_x += squareSize / 2 + squareSize / 4;
8969 d_x += squareSize / 2;
8972 s_x += squareSize / 2;
8973 s_y += squareSize / 2;
8976 A_WIDTH = squareSize / 14.; //[HGM] make float
8978 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8980 hor = 64*s_col + 32; vert = 64*s_row + 32;
8981 for(i=0; i<= 64; i++) {
8982 damage[0][vert+6>>6][hor+6>>6] = True;
8983 damage[0][vert-6>>6][hor+6>>6] = True;
8984 damage[0][vert+6>>6][hor-6>>6] = True;
8985 damage[0][vert-6>>6][hor-6>>6] = True;
8986 hor += d_col - s_col; vert += d_row - s_row;
8990 Boolean IsDrawArrowEnabled()
8992 return appData.highlightMoveWithArrow && squareSize >= 32;
8995 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8997 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8998 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9001 void UpdateLogos(int displ)
9003 return; // no logos in XBoard yet