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 DisplayMove P((int moveNumber));
448 void DisplayTitle P((char *title));
449 void ICSInitScript P((void));
450 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
451 void ErrorPopUp P((char *title, char *text, int modal));
452 void ErrorPopDown P((void));
453 static char *ExpandPathName P((char *path));
454 static void CreateAnimVars P((void));
455 static void DragPieceMove P((int x, int y));
456 static void DrawDragPiece P((void));
457 char *ModeToWidgetName P((GameMode mode));
458 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
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 Game Ctrl+G"), "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 {N_(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 { "ResetProc", ResetProc },
924 { "NewVariantProc", NewVariantProc },
925 { "LoadGameProc", LoadGameProc },
926 { "LoadNextGameProc", LoadNextGameProc },
927 { "LoadPrevGameProc", LoadPrevGameProc },
928 { "LoadSelectedProc", LoadSelectedProc },
929 { "SetFilterProc", SetFilterProc },
930 { "ReloadGameProc", ReloadGameProc },
931 { "LoadPositionProc", LoadPositionProc },
932 { "LoadNextPositionProc", LoadNextPositionProc },
933 { "LoadPrevPositionProc", LoadPrevPositionProc },
934 { "ReloadPositionProc", ReloadPositionProc },
935 { "CopyPositionProc", CopyPositionProc },
936 { "PastePositionProc", PastePositionProc },
937 { "CopyGameProc", CopyGameProc },
938 { "CopyGameListProc", CopyGameListProc },
939 { "PasteGameProc", PasteGameProc },
940 { "SaveGameProc", SaveGameProc },
941 { "SavePositionProc", SavePositionProc },
942 { "MailMoveProc", MailMoveProc },
943 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
944 { "QuitProc", QuitProc },
945 { "MachineWhiteProc", MachineWhiteProc },
946 { "MachineBlackProc", MachineBlackProc },
947 { "AnalysisModeProc", AnalyzeModeProc },
948 { "AnalyzeFileProc", AnalyzeFileProc },
949 { "TwoMachinesProc", TwoMachinesProc },
950 { "IcsClientProc", IcsClientProc },
951 { "EditGameProc", EditGameProc },
952 { "EditPositionProc", EditPositionProc },
953 { "TrainingProc", EditPositionProc },
954 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
955 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
956 { "ShowGameListProc", ShowGameListProc },
957 { "ShowMoveListProc", HistoryShowProc},
958 { "EditTagsProc", EditCommentProc },
959 { "EditBookProc", EditBookProc },
960 { "EditCommentProc", EditCommentProc },
961 { "IcsInputBoxProc", IcsInputBoxProc },
962 { "PauseProc", PauseProc },
963 { "AcceptProc", AcceptProc },
964 { "DeclineProc", DeclineProc },
965 { "RematchProc", RematchProc },
966 { "CallFlagProc", CallFlagProc },
967 { "DrawProc", DrawProc },
968 { "AdjournProc", AdjournProc },
969 { "AbortProc", AbortProc },
970 { "ResignProc", ResignProc },
971 { "AdjuWhiteProc", AdjuWhiteProc },
972 { "AdjuBlackProc", AdjuBlackProc },
973 { "AdjuDrawProc", AdjuDrawProc },
974 { "TypeInProc", TypeInProc },
975 { "EnterKeyProc", EnterKeyProc },
976 { "UpKeyProc", UpKeyProc },
977 { "DownKeyProc", DownKeyProc },
978 { "StopObservingProc", StopObservingProc },
979 { "StopExaminingProc", StopExaminingProc },
980 { "UploadProc", UploadProc },
981 { "BackwardProc", BackwardProc },
982 { "ForwardProc", ForwardProc },
983 { "ToStartProc", ToStartProc },
984 { "ToEndProc", ToEndProc },
985 { "RevertProc", RevertProc },
986 { "AnnotateProc", AnnotateProc },
987 { "TruncateGameProc", TruncateGameProc },
988 { "MoveNowProc", MoveNowProc },
989 { "RetractMoveProc", RetractMoveProc },
990 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
991 { "UciMenuProc", (XtActionProc) UciMenuProc },
992 { "TimeControlProc", (XtActionProc) TimeControlProc },
993 { "FlipViewProc", FlipViewProc },
994 { "PonderNextMoveProc", PonderNextMoveProc },
995 #ifndef OPTIONSDIALOG
996 { "AlwaysQueenProc", AlwaysQueenProc },
997 { "AnimateDraggingProc", AnimateDraggingProc },
998 { "AnimateMovingProc", AnimateMovingProc },
999 { "AutoflagProc", AutoflagProc },
1000 { "AutoflipProc", AutoflipProc },
1001 { "BlindfoldProc", BlindfoldProc },
1002 { "FlashMovesProc", FlashMovesProc },
1004 { "HighlightDraggingProc", HighlightDraggingProc },
1006 { "HighlightLastMoveProc", HighlightLastMoveProc },
1007 // { "IcsAlarmProc", IcsAlarmProc },
1008 { "MoveSoundProc", MoveSoundProc },
1009 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1010 { "PopupExitMessageProc", PopupExitMessageProc },
1011 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1012 // { "PremoveProc", PremoveProc },
1013 { "ShowCoordsProc", ShowCoordsProc },
1014 { "ShowThinkingProc", ShowThinkingProc },
1015 { "HideThinkingProc", HideThinkingProc },
1016 { "TestLegalityProc", TestLegalityProc },
1018 { "SaveSettingsProc", SaveSettingsProc },
1019 { "SaveOnExitProc", SaveOnExitProc },
1020 { "InfoProc", InfoProc },
1021 { "ManProc", ManProc },
1022 { "HintProc", HintProc },
1023 { "BookProc", BookProc },
1024 { "AboutGameProc", AboutGameProc },
1025 { "AboutProc", AboutProc },
1026 { "DebugProc", DebugProc },
1027 { "NothingProc", NothingProc },
1028 { "CommentClick", (XtActionProc) CommentClick },
1029 { "CommentPopDown", (XtActionProc) CommentPopDown },
1030 { "TagsPopDown", (XtActionProc) TagsPopDown },
1031 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1032 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1033 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1034 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1035 { "GameListPopDown", (XtActionProc) GameListPopDown },
1036 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1037 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1038 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1039 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1040 { "GenericPopDown", (XtActionProc) GenericPopDown },
1041 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1042 { "SelectMove", (XtActionProc) SelectMove },
1045 char globalTranslations[] =
1046 ":<Key>F9: ResignProc() \n \
1047 :Ctrl<Key>n: ResetProc() \n \
1048 :Meta<Key>V: NewVariantProc() \n \
1049 :Ctrl<Key>o: LoadGameProc() \n \
1050 :Meta<Key>Next: LoadNextGameProc() \n \
1051 :Meta<Key>Prior: LoadPrevGameProc() \n \
1052 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1053 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1054 :Ctrl<Key>s: SaveGameProc() \n \
1055 :Ctrl<Key>c: CopyGameProc() \n \
1056 :Ctrl<Key>v: PasteGameProc() \n \
1057 :Ctrl<Key>O: LoadPositionProc() \n \
1058 :Shift<Key>Next: LoadNextPositionProc() \n \
1059 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1060 :Ctrl<Key>S: SavePositionProc() \n \
1061 :Ctrl<Key>C: CopyPositionProc() \n \
1062 :Ctrl<Key>V: PastePositionProc() \n \
1063 :Ctrl<Key>q: QuitProc() \n \
1064 :Ctrl<Key>w: MachineWhiteProc() \n \
1065 :Ctrl<Key>b: MachineBlackProc() \n \
1066 :Ctrl<Key>t: TwoMachinesProc() \n \
1067 :Ctrl<Key>a: AnalysisModeProc() \n \
1068 :Ctrl<Key>g: AnalyzeFileProc() \n \
1069 :Ctrl<Key>e: EditGameProc() \n \
1070 :Ctrl<Key>E: EditPositionProc() \n \
1071 :Meta<Key>O: EngineOutputProc() \n \
1072 :Meta<Key>E: EvalGraphProc() \n \
1073 :Meta<Key>G: ShowGameListProc() \n \
1074 :Meta<Key>H: ShowMoveListProc() \n \
1075 :<Key>Pause: PauseProc() \n \
1076 :<Key>F3: AcceptProc() \n \
1077 :<Key>F4: DeclineProc() \n \
1078 :<Key>F12: RematchProc() \n \
1079 :<Key>F5: CallFlagProc() \n \
1080 :<Key>F6: DrawProc() \n \
1081 :<Key>F7: AdjournProc() \n \
1082 :<Key>F8: AbortProc() \n \
1083 :<Key>F10: StopObservingProc() \n \
1084 :<Key>F11: StopExaminingProc() \n \
1085 :Meta Ctrl<Key>F12: DebugProc() \n \
1086 :Meta<Key>End: ToEndProc() \n \
1087 :Meta<Key>Right: ForwardProc() \n \
1088 :Meta<Key>Home: ToStartProc() \n \
1089 :Meta<Key>Left: BackwardProc() \n \
1090 :<Key>Left: BackwardProc() \n \
1091 :<Key>Right: ForwardProc() \n \
1092 :<Key>Home: RevertProc() \n \
1093 :<Key>End: TruncateGameProc() \n \
1094 :Ctrl<Key>m: MoveNowProc() \n \
1095 :Ctrl<Key>x: RetractMoveProc() \n \
1096 :Meta<Key>J: EngineMenuProc() \n \
1097 :Meta<Key>U: UciMenuProc() \n \
1098 :Meta<Key>T: TimeControlProc() \n \
1099 :Ctrl<Key>P: PonderNextMoveProc() \n "
1100 #ifndef OPTIONSDIALOG
1102 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1103 :Ctrl<Key>F: AutoflagProc() \n \
1104 :Ctrl<Key>A: AnimateMovingProc() \n \
1105 :Ctrl<Key>L: TestLegalityProc() \n \
1106 :Ctrl<Key>H: HideThinkingProc() \n "
1109 :<Key>F1: ManProc() \n \
1110 :<Key>F2: FlipViewProc() \n \
1111 :Ctrl<KeyDown>.: BackwardProc() \n \
1112 :Ctrl<KeyUp>.: ForwardProc() \n \
1113 :Ctrl<Key>1: AskQuestionProc(\"Direct command\",\
1114 \"Send to chess program:\",,1) \n \
1115 :Ctrl<Key>2: AskQuestionProc(\"Direct command\",\
1116 \"Send to second chess program:\",,2) \n";
1118 char boardTranslations[] =
1119 "<Btn1Down>: HandleUserMove(0) \n \
1120 Shift<Btn1Up>: HandleUserMove(1) \n \
1121 <Btn1Up>: HandleUserMove(0) \n \
1122 <Btn1Motion>: AnimateUserMove() \n \
1123 <Btn3Motion>: HandlePV() \n \
1124 <Btn3Up>: PieceMenuPopup(menuB) \n \
1125 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1126 PieceMenuPopup(menuB) \n \
1127 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1128 PieceMenuPopup(menuW) \n \
1129 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1130 PieceMenuPopup(menuW) \n \
1131 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1132 PieceMenuPopup(menuB) \n";
1134 char whiteTranslations[] =
1135 "Shift<BtnDown>: WhiteClock(1)\n \
1136 <BtnDown>: WhiteClock(0)\n";
1137 char blackTranslations[] =
1138 "Shift<BtnDown>: BlackClock(1)\n \
1139 <BtnDown>: BlackClock(0)\n";
1141 char ICSInputTranslations[] =
1142 "<Key>Up: UpKeyProc() \n "
1143 "<Key>Down: DownKeyProc() \n "
1144 "<Key>Return: EnterKeyProc() \n";
1146 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1147 // as the widget is destroyed before the up-click can call extend-end
1148 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1150 String xboardResources[] = {
1151 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1152 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1153 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1158 /* Max possible square size */
1159 #define MAXSQSIZE 256
1161 static int xpm_avail[MAXSQSIZE];
1163 #ifdef HAVE_DIR_STRUCT
1165 /* Extract piece size from filename */
1167 xpm_getsize(name, len, ext)
1178 if ((p=strchr(name, '.')) == NULL ||
1179 StrCaseCmp(p+1, ext) != 0)
1185 while (*p && isdigit(*p))
1192 /* Setup xpm_avail */
1194 xpm_getavail(dirname, ext)
1202 for (i=0; i<MAXSQSIZE; ++i)
1205 if (appData.debugMode)
1206 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1208 dir = opendir(dirname);
1211 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1212 programName, dirname);
1216 while ((ent=readdir(dir)) != NULL) {
1217 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1218 if (i > 0 && i < MAXSQSIZE)
1228 xpm_print_avail(fp, ext)
1234 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1235 for (i=1; i<MAXSQSIZE; ++i) {
1241 /* Return XPM piecesize closest to size */
1243 xpm_closest_to(dirname, size, ext)
1249 int sm_diff = MAXSQSIZE;
1253 xpm_getavail(dirname, ext);
1255 if (appData.debugMode)
1256 xpm_print_avail(stderr, ext);
1258 for (i=1; i<MAXSQSIZE; ++i) {
1261 diff = (diff<0) ? -diff : diff;
1262 if (diff < sm_diff) {
1270 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1276 #else /* !HAVE_DIR_STRUCT */
1277 /* If we are on a system without a DIR struct, we can't
1278 read the directory, so we can't collect a list of
1279 filenames, etc., so we can't do any size-fitting. */
1281 xpm_closest_to(dirname, size, ext)
1286 fprintf(stderr, _("\
1287 Warning: No DIR structure found on this system --\n\
1288 Unable to autosize for XPM/XIM pieces.\n\
1289 Please report this error to %s.\n\
1290 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1293 #endif /* HAVE_DIR_STRUCT */
1295 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1296 "magenta", "cyan", "white" };
1300 TextColors textColors[(int)NColorClasses];
1302 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1304 parse_color(str, which)
1308 char *p, buf[100], *d;
1311 if (strlen(str) > 99) /* watch bounds on buf */
1316 for (i=0; i<which; ++i) {
1323 /* Could be looking at something like:
1325 .. in which case we want to stop on a comma also */
1326 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1330 return -1; /* Use default for empty field */
1333 if (which == 2 || isdigit(*p))
1336 while (*p && isalpha(*p))
1341 for (i=0; i<8; ++i) {
1342 if (!StrCaseCmp(buf, cnames[i]))
1343 return which? (i+40) : (i+30);
1345 if (!StrCaseCmp(buf, "default")) return -1;
1347 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1352 parse_cpair(cc, str)
1356 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1357 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1362 /* bg and attr are optional */
1363 textColors[(int)cc].bg = parse_color(str, 1);
1364 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1365 textColors[(int)cc].attr = 0;
1371 /* Arrange to catch delete-window events */
1372 Atom wm_delete_window;
1374 CatchDeleteWindow(Widget w, String procname)
1377 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1378 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1379 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1386 XtSetArg(args[0], XtNiconic, False);
1387 XtSetValues(shellWidget, args, 1);
1389 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1392 //---------------------------------------------------------------------------------------------------------
1393 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1396 #define CW_USEDEFAULT (1<<31)
1397 #define ICS_TEXT_MENU_SIZE 90
1398 #define DEBUG_FILE "xboard.debug"
1399 #define SetCurrentDirectory chdir
1400 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1404 // these two must some day move to frontend.h, when they are implemented
1405 Boolean GameListIsUp();
1407 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1410 // front-end part of option handling
1412 // [HGM] This platform-dependent table provides the location for storing the color info
1413 extern char *crWhite, * crBlack;
1417 &appData.whitePieceColor,
1418 &appData.blackPieceColor,
1419 &appData.lightSquareColor,
1420 &appData.darkSquareColor,
1421 &appData.highlightSquareColor,
1422 &appData.premoveHighlightColor,
1423 &appData.lowTimeWarningColor,
1434 // [HGM] font: keep a font for each square size, even non-stndard ones
1435 #define NUM_SIZES 18
1436 #define MAX_SIZE 130
1437 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1438 char *fontTable[NUM_FONTS][MAX_SIZE];
1441 ParseFont(char *name, int number)
1442 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1444 if(sscanf(name, "size%d:", &size)) {
1445 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1446 // defer processing it until we know if it matches our board size
1447 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1448 fontTable[number][size] = strdup(strchr(name, ':')+1);
1449 fontValid[number][size] = True;
1454 case 0: // CLOCK_FONT
1455 appData.clockFont = strdup(name);
1457 case 1: // MESSAGE_FONT
1458 appData.font = strdup(name);
1460 case 2: // COORD_FONT
1461 appData.coordFont = strdup(name);
1466 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1471 { // only 2 fonts currently
1472 appData.clockFont = CLOCK_FONT_NAME;
1473 appData.coordFont = COORD_FONT_NAME;
1474 appData.font = DEFAULT_FONT_NAME;
1479 { // no-op, until we identify the code for this already in XBoard and move it here
1483 ParseColor(int n, char *name)
1484 { // in XBoard, just copy the color-name string
1485 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1489 ParseTextAttribs(ColorClass cc, char *s)
1491 (&appData.colorShout)[cc] = strdup(s);
1495 ParseBoardSize(void *addr, char *name)
1497 appData.boardSize = strdup(name);
1502 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1506 SetCommPortDefaults()
1507 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1510 // [HGM] args: these three cases taken out to stay in front-end
1512 SaveFontArg(FILE *f, ArgDescriptor *ad)
1515 int i, n = (int)(intptr_t)ad->argLoc;
1517 case 0: // CLOCK_FONT
1518 name = appData.clockFont;
1520 case 1: // MESSAGE_FONT
1521 name = appData.font;
1523 case 2: // COORD_FONT
1524 name = appData.coordFont;
1529 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1530 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1531 fontTable[n][squareSize] = strdup(name);
1532 fontValid[n][squareSize] = True;
1535 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1536 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1541 { // nothing to do, as the sounds are at all times represented by their text-string names already
1545 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1546 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1547 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1551 SaveColor(FILE *f, ArgDescriptor *ad)
1552 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1553 if(colorVariable[(int)(intptr_t)ad->argLoc])
1554 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1558 SaveBoardSize(FILE *f, char *name, void *addr)
1559 { // wrapper to shield back-end from BoardSize & sizeInfo
1560 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1564 ParseCommPortSettings(char *s)
1565 { // no such option in XBoard (yet)
1568 extern Widget engineOutputShell;
1571 GetActualPlacement(Widget wg, WindowPlacement *wp)
1581 XtSetArg(args[i], XtNx, &x); i++;
1582 XtSetArg(args[i], XtNy, &y); i++;
1583 XtSetArg(args[i], XtNwidth, &w); i++;
1584 XtSetArg(args[i], XtNheight, &h); i++;
1585 XtGetValues(wg, args, i);
1594 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1595 // In XBoard this will have to wait until awareness of window parameters is implemented
1596 GetActualPlacement(shellWidget, &wpMain);
1597 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1598 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1599 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1600 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1601 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1602 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1606 PrintCommPortSettings(FILE *f, char *name)
1607 { // This option does not exist in XBoard
1611 MySearchPath(char *installDir, char *name, char *fullname)
1612 { // just append installDir and name. Perhaps ExpandPath should be used here?
1613 name = ExpandPathName(name);
1614 if(name && name[0] == '/')
1615 safeStrCpy(fullname, name, MSG_SIZ );
1617 sprintf(fullname, "%s%c%s", installDir, '/', name);
1623 MyGetFullPathName(char *name, char *fullname)
1624 { // should use ExpandPath?
1625 name = ExpandPathName(name);
1626 safeStrCpy(fullname, name, MSG_SIZ );
1631 EnsureOnScreen(int *x, int *y, int minX, int minY)
1638 { // [HGM] args: allows testing if main window is realized from back-end
1639 return xBoardWindow != 0;
1643 PopUpStartupDialog()
1644 { // start menu not implemented in XBoard
1648 ConvertToLine(int argc, char **argv)
1650 static char line[128*1024], buf[1024];
1654 for(i=1; i<argc; i++)
1656 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1657 && argv[i][0] != '{' )
1658 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1660 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1661 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1664 line[strlen(line)-1] = NULLCHAR;
1668 //--------------------------------------------------------------------------------------------
1670 extern Boolean twoBoards, partnerUp;
1673 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1675 #define BoardSize int
1676 void InitDrawingSizes(BoardSize boardSize, int flags)
1677 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1678 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1680 XtGeometryResult gres;
1682 static Dimension oldWidth, oldHeight;
1683 static VariantClass oldVariant;
1684 static int oldDual = -1;
1686 if(!formWidget) return;
1688 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1689 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1690 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1692 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1694 * Enable shell resizing.
1696 shellArgs[0].value = (XtArgVal) &w;
1697 shellArgs[1].value = (XtArgVal) &h;
1698 XtGetValues(shellWidget, shellArgs, 2);
1700 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1701 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1702 XtSetValues(shellWidget, &shellArgs[2], 4);
1704 XtSetArg(args[0], XtNdefaultDistance, &sep);
1705 XtGetValues(formWidget, args, 1);
1707 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1709 hOffset = boardWidth + 10;
1710 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1711 secondSegments[i] = gridSegments[i];
1712 secondSegments[i].x1 += hOffset;
1713 secondSegments[i].x2 += hOffset;
1716 XtSetArg(args[0], XtNwidth, boardWidth);
1717 XtSetArg(args[1], XtNheight, boardHeight);
1718 XtSetValues(boardWidget, args, 2);
1720 timerWidth = (boardWidth - sep) / 2;
1721 XtSetArg(args[0], XtNwidth, timerWidth);
1722 XtSetValues(whiteTimerWidget, args, 1);
1723 XtSetValues(blackTimerWidget, args, 1);
1725 XawFormDoLayout(formWidget, False);
1727 if (appData.titleInWindow) {
1729 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1730 XtSetArg(args[i], XtNheight, &h); i++;
1731 XtGetValues(titleWidget, args, i);
1733 w = boardWidth - 2*bor;
1735 XtSetArg(args[0], XtNwidth, &w);
1736 XtGetValues(menuBarWidget, args, 1);
1737 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1740 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1741 if (gres != XtGeometryYes && appData.debugMode) {
1743 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1744 programName, gres, w, h, wr, hr);
1748 XawFormDoLayout(formWidget, True);
1751 * Inhibit shell resizing.
1753 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1754 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1755 shellArgs[4].value = shellArgs[2].value = w;
1756 shellArgs[5].value = shellArgs[3].value = h;
1757 XtSetValues(shellWidget, &shellArgs[0], 6);
1760 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1763 if(gameInfo.variant == oldVariant) return; // and only if variant changed
1766 for(i=0; i<4; i++) {
1768 for(p=0; p<=(int)WhiteKing; p++)
1769 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1770 if(gameInfo.variant == VariantShogi) {
1771 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1772 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1773 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1774 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1775 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1778 if(gameInfo.variant == VariantGothic) {
1779 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1782 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1783 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1784 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1787 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1788 for(p=0; p<=(int)WhiteKing; p++)
1789 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1790 if(gameInfo.variant == VariantShogi) {
1791 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1792 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1793 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1794 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1795 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1798 if(gameInfo.variant == VariantGothic) {
1799 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1802 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1803 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1804 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1809 for(i=0; i<2; i++) {
1811 for(p=0; p<=(int)WhiteKing; p++)
1812 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1813 if(gameInfo.variant == VariantShogi) {
1814 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1815 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1816 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1817 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1818 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1821 if(gameInfo.variant == VariantGothic) {
1822 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1825 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1826 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1827 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1837 void ParseIcsTextColors()
1838 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1839 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1840 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1841 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1842 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1843 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1844 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1845 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1846 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1847 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1848 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1850 if (appData.colorize) {
1852 _("%s: can't parse color names; disabling colorization\n"),
1855 appData.colorize = FALSE;
1860 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1861 XrmValue vFrom, vTo;
1862 int forceMono = False;
1864 if (!appData.monoMode) {
1865 vFrom.addr = (caddr_t) appData.lightSquareColor;
1866 vFrom.size = strlen(appData.lightSquareColor);
1867 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1868 if (vTo.addr == NULL) {
1869 appData.monoMode = True;
1872 lightSquareColor = *(Pixel *) vTo.addr;
1875 if (!appData.monoMode) {
1876 vFrom.addr = (caddr_t) appData.darkSquareColor;
1877 vFrom.size = strlen(appData.darkSquareColor);
1878 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1879 if (vTo.addr == NULL) {
1880 appData.monoMode = True;
1883 darkSquareColor = *(Pixel *) vTo.addr;
1886 if (!appData.monoMode) {
1887 vFrom.addr = (caddr_t) appData.whitePieceColor;
1888 vFrom.size = strlen(appData.whitePieceColor);
1889 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1890 if (vTo.addr == NULL) {
1891 appData.monoMode = True;
1894 whitePieceColor = *(Pixel *) vTo.addr;
1897 if (!appData.monoMode) {
1898 vFrom.addr = (caddr_t) appData.blackPieceColor;
1899 vFrom.size = strlen(appData.blackPieceColor);
1900 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1901 if (vTo.addr == NULL) {
1902 appData.monoMode = True;
1905 blackPieceColor = *(Pixel *) vTo.addr;
1909 if (!appData.monoMode) {
1910 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1911 vFrom.size = strlen(appData.highlightSquareColor);
1912 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1913 if (vTo.addr == NULL) {
1914 appData.monoMode = True;
1917 highlightSquareColor = *(Pixel *) vTo.addr;
1921 if (!appData.monoMode) {
1922 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1923 vFrom.size = strlen(appData.premoveHighlightColor);
1924 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1925 if (vTo.addr == NULL) {
1926 appData.monoMode = True;
1929 premoveHighlightColor = *(Pixel *) vTo.addr;
1937 { // [HGM] taken out of main
1939 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1940 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1941 appData.bitmapDirectory = DEF_BITMAP_DIR;
1943 if (appData.bitmapDirectory[0] != NULLCHAR) {
1947 CreateXPMBoard(appData.liteBackTextureFile, 1);
1948 CreateXPMBoard(appData.darkBackTextureFile, 0);
1952 /* Create regular pieces */
1953 if (!useImages) CreatePieces();
1962 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1963 XSetWindowAttributes window_attributes;
1965 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1966 XrmValue vFrom, vTo;
1967 XtGeometryResult gres;
1970 int forceMono = False;
1972 srandom(time(0)); // [HGM] book: make random truly random
1974 setbuf(stdout, NULL);
1975 setbuf(stderr, NULL);
1978 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1979 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1983 programName = strrchr(argv[0], '/');
1984 if (programName == NULL)
1985 programName = argv[0];
1990 XtSetLanguageProc(NULL, NULL, NULL);
1991 bindtextdomain(PACKAGE, LOCALEDIR);
1992 textdomain(PACKAGE);
1996 XtAppInitialize(&appContext, "XBoard", shellOptions,
1997 XtNumber(shellOptions),
1998 &argc, argv, xboardResources, NULL, 0);
1999 appData.boardSize = "";
2000 InitAppData(ConvertToLine(argc, argv));
2002 if (p == NULL) p = "/tmp";
2003 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2004 gameCopyFilename = (char*) malloc(i);
2005 gamePasteFilename = (char*) malloc(i);
2006 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2007 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2009 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2010 clientResources, XtNumber(clientResources),
2013 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2014 static char buf[MSG_SIZ];
2015 EscapeExpand(buf, appData.firstInitString);
2016 appData.firstInitString = strdup(buf);
2017 EscapeExpand(buf, appData.secondInitString);
2018 appData.secondInitString = strdup(buf);
2019 EscapeExpand(buf, appData.firstComputerString);
2020 appData.firstComputerString = strdup(buf);
2021 EscapeExpand(buf, appData.secondComputerString);
2022 appData.secondComputerString = strdup(buf);
2025 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2028 if (chdir(chessDir) != 0) {
2029 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2035 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2036 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2037 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2038 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2041 setbuf(debugFP, NULL);
2045 if (appData.debugMode) {
2046 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2050 /* [HGM,HR] make sure board size is acceptable */
2051 if(appData.NrFiles > BOARD_FILES ||
2052 appData.NrRanks > BOARD_RANKS )
2053 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2056 /* This feature does not work; animation needs a rewrite */
2057 appData.highlightDragging = FALSE;
2061 xDisplay = XtDisplay(shellWidget);
2062 xScreen = DefaultScreen(xDisplay);
2063 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2065 gameInfo.variant = StringToVariant(appData.variant);
2066 InitPosition(FALSE);
2069 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2071 if (isdigit(appData.boardSize[0])) {
2072 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2073 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2074 &fontPxlSize, &smallLayout, &tinyLayout);
2076 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2077 programName, appData.boardSize);
2081 /* Find some defaults; use the nearest known size */
2082 SizeDefaults *szd, *nearest;
2083 int distance = 99999;
2084 nearest = szd = sizeDefaults;
2085 while (szd->name != NULL) {
2086 if (abs(szd->squareSize - squareSize) < distance) {
2088 distance = abs(szd->squareSize - squareSize);
2089 if (distance == 0) break;
2093 if (i < 2) lineGap = nearest->lineGap;
2094 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2095 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2096 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2097 if (i < 6) smallLayout = nearest->smallLayout;
2098 if (i < 7) tinyLayout = nearest->tinyLayout;
2101 SizeDefaults *szd = sizeDefaults;
2102 if (*appData.boardSize == NULLCHAR) {
2103 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2104 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2107 if (szd->name == NULL) szd--;
2108 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2110 while (szd->name != NULL &&
2111 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2112 if (szd->name == NULL) {
2113 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2114 programName, appData.boardSize);
2118 squareSize = szd->squareSize;
2119 lineGap = szd->lineGap;
2120 clockFontPxlSize = szd->clockFontPxlSize;
2121 coordFontPxlSize = szd->coordFontPxlSize;
2122 fontPxlSize = szd->fontPxlSize;
2123 smallLayout = szd->smallLayout;
2124 tinyLayout = szd->tinyLayout;
2125 // [HGM] font: use defaults from settings file if available and not overruled
2127 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2128 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2129 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2130 appData.font = fontTable[MESSAGE_FONT][squareSize];
2131 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2132 appData.coordFont = fontTable[COORD_FONT][squareSize];
2134 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2135 if (strlen(appData.pixmapDirectory) > 0) {
2136 p = ExpandPathName(appData.pixmapDirectory);
2138 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2139 appData.pixmapDirectory);
2142 if (appData.debugMode) {
2143 fprintf(stderr, _("\
2144 XBoard square size (hint): %d\n\
2145 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2147 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2148 if (appData.debugMode) {
2149 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2152 defaultLineGap = lineGap;
2153 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2155 /* [HR] height treated separately (hacked) */
2156 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2157 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2158 if (appData.showJail == 1) {
2159 /* Jail on top and bottom */
2160 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2161 XtSetArg(boardArgs[2], XtNheight,
2162 boardHeight + 2*(lineGap + squareSize));
2163 } else if (appData.showJail == 2) {
2165 XtSetArg(boardArgs[1], XtNwidth,
2166 boardWidth + 2*(lineGap + squareSize));
2167 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2170 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2171 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2175 * Determine what fonts to use.
2178 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2179 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2180 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2181 fontSet = CreateFontSet(appData.font);
2182 clockFontSet = CreateFontSet(appData.clockFont);
2184 /* For the coordFont, use the 0th font of the fontset. */
2185 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2186 XFontStruct **font_struct_list;
2187 char **font_name_list;
2188 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2189 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2190 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2193 appData.font = FindFont(appData.font, fontPxlSize);
2194 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2195 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2196 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2197 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2198 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2199 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2201 countFontID = coordFontID; // [HGM] holdings
2202 countFontStruct = coordFontStruct;
2204 xdb = XtDatabase(xDisplay);
2206 XrmPutLineResource(&xdb, "*international: True");
2207 vTo.size = sizeof(XFontSet);
2208 vTo.addr = (XtPointer) &fontSet;
2209 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2211 XrmPutStringResource(&xdb, "*font", appData.font);
2215 * Detect if there are not enough colors available and adapt.
2217 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2218 appData.monoMode = True;
2221 forceMono = MakeColors();
2224 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2226 appData.monoMode = True;
2229 if (appData.lowTimeWarning && !appData.monoMode) {
2230 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2231 vFrom.size = strlen(appData.lowTimeWarningColor);
2232 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2233 if (vTo.addr == NULL)
2234 appData.monoMode = True;
2236 lowTimeWarningColor = *(Pixel *) vTo.addr;
2239 if (appData.monoMode && appData.debugMode) {
2240 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2241 (unsigned long) XWhitePixel(xDisplay, xScreen),
2242 (unsigned long) XBlackPixel(xDisplay, xScreen));
2245 ParseIcsTextColors();
2246 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2247 textColors[ColorNone].attr = 0;
2249 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2255 layoutName = "tinyLayout";
2256 } else if (smallLayout) {
2257 layoutName = "smallLayout";
2259 layoutName = "normalLayout";
2261 /* Outer layoutWidget is there only to provide a name for use in
2262 resources that depend on the layout style */
2264 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2265 layoutArgs, XtNumber(layoutArgs));
2267 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2268 formArgs, XtNumber(formArgs));
2269 XtSetArg(args[0], XtNdefaultDistance, &sep);
2270 XtGetValues(formWidget, args, 1);
2273 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2274 XtSetArg(args[0], XtNtop, XtChainTop);
2275 XtSetArg(args[1], XtNbottom, XtChainTop);
2276 XtSetArg(args[2], XtNright, XtChainLeft);
2277 XtSetValues(menuBarWidget, args, 3);
2279 widgetList[j++] = whiteTimerWidget =
2280 XtCreateWidget("whiteTime", labelWidgetClass,
2281 formWidget, timerArgs, XtNumber(timerArgs));
2283 XtSetArg(args[0], XtNfontSet, clockFontSet);
2285 XtSetArg(args[0], XtNfont, clockFontStruct);
2287 XtSetArg(args[1], XtNtop, XtChainTop);
2288 XtSetArg(args[2], XtNbottom, XtChainTop);
2289 XtSetValues(whiteTimerWidget, args, 3);
2291 widgetList[j++] = blackTimerWidget =
2292 XtCreateWidget("blackTime", labelWidgetClass,
2293 formWidget, timerArgs, XtNumber(timerArgs));
2295 XtSetArg(args[0], XtNfontSet, clockFontSet);
2297 XtSetArg(args[0], XtNfont, clockFontStruct);
2299 XtSetArg(args[1], XtNtop, XtChainTop);
2300 XtSetArg(args[2], XtNbottom, XtChainTop);
2301 XtSetValues(blackTimerWidget, args, 3);
2303 if (appData.titleInWindow) {
2304 widgetList[j++] = titleWidget =
2305 XtCreateWidget("title", labelWidgetClass, formWidget,
2306 titleArgs, XtNumber(titleArgs));
2307 XtSetArg(args[0], XtNtop, XtChainTop);
2308 XtSetArg(args[1], XtNbottom, XtChainTop);
2309 XtSetValues(titleWidget, args, 2);
2312 if (appData.showButtonBar) {
2313 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2314 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2315 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2316 XtSetArg(args[2], XtNtop, XtChainTop);
2317 XtSetArg(args[3], XtNbottom, XtChainTop);
2318 XtSetValues(buttonBarWidget, args, 4);
2321 widgetList[j++] = messageWidget =
2322 XtCreateWidget("message", labelWidgetClass, formWidget,
2323 messageArgs, XtNumber(messageArgs));
2324 XtSetArg(args[0], XtNtop, XtChainTop);
2325 XtSetArg(args[1], XtNbottom, XtChainTop);
2326 XtSetValues(messageWidget, args, 2);
2328 widgetList[j++] = boardWidget =
2329 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2330 XtNumber(boardArgs));
2332 XtManageChildren(widgetList, j);
2334 timerWidth = (boardWidth - sep) / 2;
2335 XtSetArg(args[0], XtNwidth, timerWidth);
2336 XtSetValues(whiteTimerWidget, args, 1);
2337 XtSetValues(blackTimerWidget, args, 1);
2339 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2340 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2341 XtGetValues(whiteTimerWidget, args, 2);
2343 if (appData.showButtonBar) {
2344 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2345 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2346 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2350 * formWidget uses these constraints but they are stored
2354 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2355 XtSetValues(menuBarWidget, args, i);
2356 if (appData.titleInWindow) {
2359 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2360 XtSetValues(whiteTimerWidget, args, i);
2362 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2363 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2364 XtSetValues(blackTimerWidget, args, i);
2366 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2367 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2368 XtSetValues(titleWidget, args, i);
2370 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2371 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2372 XtSetValues(messageWidget, args, i);
2373 if (appData.showButtonBar) {
2375 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2376 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2377 XtSetValues(buttonBarWidget, args, i);
2381 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2382 XtSetValues(whiteTimerWidget, args, i);
2384 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2385 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2386 XtSetValues(blackTimerWidget, args, i);
2388 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2389 XtSetValues(titleWidget, args, i);
2391 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2392 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2393 XtSetValues(messageWidget, args, i);
2394 if (appData.showButtonBar) {
2396 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2397 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2398 XtSetValues(buttonBarWidget, args, i);
2403 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2404 XtSetValues(whiteTimerWidget, args, i);
2406 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2407 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2408 XtSetValues(blackTimerWidget, args, i);
2410 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2411 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2412 XtSetValues(messageWidget, args, i);
2413 if (appData.showButtonBar) {
2415 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2416 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2417 XtSetValues(buttonBarWidget, args, i);
2421 XtSetArg(args[0], XtNfromVert, messageWidget);
2422 XtSetArg(args[1], XtNtop, XtChainTop);
2423 XtSetArg(args[2], XtNbottom, XtChainBottom);
2424 XtSetArg(args[3], XtNleft, XtChainLeft);
2425 XtSetArg(args[4], XtNright, XtChainRight);
2426 XtSetValues(boardWidget, args, 5);
2428 XtRealizeWidget(shellWidget);
2431 XtSetArg(args[0], XtNx, wpMain.x);
2432 XtSetArg(args[1], XtNy, wpMain.y);
2433 XtSetValues(shellWidget, args, 2);
2437 * Correct the width of the message and title widgets.
2438 * It is not known why some systems need the extra fudge term.
2439 * The value "2" is probably larger than needed.
2441 XawFormDoLayout(formWidget, False);
2443 #define WIDTH_FUDGE 2
2445 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2446 XtSetArg(args[i], XtNheight, &h); i++;
2447 XtGetValues(messageWidget, args, i);
2448 if (appData.showButtonBar) {
2450 XtSetArg(args[i], XtNwidth, &w); i++;
2451 XtGetValues(buttonBarWidget, args, i);
2452 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2454 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2457 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2458 if (gres != XtGeometryYes && appData.debugMode) {
2459 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2460 programName, gres, w, h, wr, hr);
2463 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2464 /* The size used for the child widget in layout lags one resize behind
2465 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2467 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2468 if (gres != XtGeometryYes && appData.debugMode) {
2469 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2470 programName, gres, w, h, wr, hr);
2473 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2474 XtSetArg(args[1], XtNright, XtChainRight);
2475 XtSetValues(messageWidget, args, 2);
2477 if (appData.titleInWindow) {
2479 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2480 XtSetArg(args[i], XtNheight, &h); i++;
2481 XtGetValues(titleWidget, args, i);
2483 w = boardWidth - 2*bor;
2485 XtSetArg(args[0], XtNwidth, &w);
2486 XtGetValues(menuBarWidget, args, 1);
2487 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2490 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2491 if (gres != XtGeometryYes && appData.debugMode) {
2493 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2494 programName, gres, w, h, wr, hr);
2497 XawFormDoLayout(formWidget, True);
2499 xBoardWindow = XtWindow(boardWidget);
2501 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2502 // not need to go into InitDrawingSizes().
2506 * Create X checkmark bitmap and initialize option menu checks.
2508 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2509 checkmark_bits, checkmark_width, checkmark_height);
2510 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2511 #ifndef OPTIONSDIALOG
2512 if (appData.alwaysPromoteToQueen) {
2513 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2516 if (appData.animateDragging) {
2517 XtSetValues(XtNameToWidget(menuBarWidget,
2518 "menuOptions.Animate Dragging"),
2521 if (appData.animate) {
2522 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2525 if (appData.autoCallFlag) {
2526 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2529 if (appData.autoFlipView) {
2530 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2533 if (appData.blindfold) {
2534 XtSetValues(XtNameToWidget(menuBarWidget,
2535 "menuOptions.Blindfold"), args, 1);
2537 if (appData.flashCount > 0) {
2538 XtSetValues(XtNameToWidget(menuBarWidget,
2539 "menuOptions.Flash Moves"),
2543 if (appData.highlightDragging) {
2544 XtSetValues(XtNameToWidget(menuBarWidget,
2545 "menuOptions.Highlight Dragging"),
2549 if (appData.highlightLastMove) {
2550 XtSetValues(XtNameToWidget(menuBarWidget,
2551 "menuOptions.Highlight Last Move"),
2554 if (appData.highlightMoveWithArrow) {
2555 XtSetValues(XtNameToWidget(menuBarWidget,
2556 "menuOptions.Arrow"),
2559 // if (appData.icsAlarm) {
2560 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2563 if (appData.ringBellAfterMoves) {
2564 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2567 if (appData.oneClick) {
2568 XtSetValues(XtNameToWidget(menuBarWidget,
2569 "menuOptions.OneClick"), args, 1);
2571 if (appData.periodicUpdates) {
2572 XtSetValues(XtNameToWidget(menuBarWidget,
2573 "menuOptions.Periodic Updates"), args, 1);
2575 if (appData.ponderNextMove) {
2576 XtSetValues(XtNameToWidget(menuBarWidget,
2577 "menuOptions.Ponder Next Move"), args, 1);
2579 if (appData.popupExitMessage) {
2580 XtSetValues(XtNameToWidget(menuBarWidget,
2581 "menuOptions.Popup Exit Message"), args, 1);
2583 if (appData.popupMoveErrors) {
2584 XtSetValues(XtNameToWidget(menuBarWidget,
2585 "menuOptions.Popup Move Errors"), args, 1);
2587 // if (appData.premove) {
2588 // XtSetValues(XtNameToWidget(menuBarWidget,
2589 // "menuOptions.Premove"), args, 1);
2591 if (appData.showCoords) {
2592 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2595 if (appData.hideThinkingFromHuman) {
2596 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2599 if (appData.testLegality) {
2600 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2604 if (saveSettingsOnExit) {
2605 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2612 ReadBitmap(&wIconPixmap, "icon_white.bm",
2613 icon_white_bits, icon_white_width, icon_white_height);
2614 ReadBitmap(&bIconPixmap, "icon_black.bm",
2615 icon_black_bits, icon_black_width, icon_black_height);
2616 iconPixmap = wIconPixmap;
2618 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2619 XtSetValues(shellWidget, args, i);
2622 * Create a cursor for the board widget.
2624 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2625 XChangeWindowAttributes(xDisplay, xBoardWindow,
2626 CWCursor, &window_attributes);
2629 * Inhibit shell resizing.
2631 shellArgs[0].value = (XtArgVal) &w;
2632 shellArgs[1].value = (XtArgVal) &h;
2633 XtGetValues(shellWidget, shellArgs, 2);
2634 shellArgs[4].value = shellArgs[2].value = w;
2635 shellArgs[5].value = shellArgs[3].value = h;
2636 XtSetValues(shellWidget, &shellArgs[2], 4);
2637 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2638 marginH = h - boardHeight;
2640 CatchDeleteWindow(shellWidget, "QuitProc");
2648 if (appData.animate || appData.animateDragging)
2651 XtAugmentTranslations(formWidget,
2652 XtParseTranslationTable(globalTranslations));
2653 XtAugmentTranslations(boardWidget,
2654 XtParseTranslationTable(boardTranslations));
2655 XtAugmentTranslations(whiteTimerWidget,
2656 XtParseTranslationTable(whiteTranslations));
2657 XtAugmentTranslations(blackTimerWidget,
2658 XtParseTranslationTable(blackTranslations));
2660 /* Why is the following needed on some versions of X instead
2661 * of a translation? */
2662 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2663 (XtEventHandler) EventProc, NULL);
2665 XtAddEventHandler(formWidget, KeyPressMask, False,
2666 (XtEventHandler) MoveTypeInProc, NULL);
2668 /* [AS] Restore layout */
2669 if( wpMoveHistory.visible ) {
2673 if( wpEvalGraph.visible )
2678 if( wpEngineOutput.visible ) {
2679 EngineOutputPopUp();
2684 if (errorExitStatus == -1) {
2685 if (appData.icsActive) {
2686 /* We now wait until we see "login:" from the ICS before
2687 sending the logon script (problems with timestamp otherwise) */
2688 /*ICSInitScript();*/
2689 if (appData.icsInputBox) ICSInputBoxPopUp();
2693 signal(SIGWINCH, TermSizeSigHandler);
2695 signal(SIGINT, IntSigHandler);
2696 signal(SIGTERM, IntSigHandler);
2697 if (*appData.cmailGameName != NULLCHAR) {
2698 signal(SIGUSR1, CmailSigHandler);
2701 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2703 // XtSetKeyboardFocus(shellWidget, formWidget);
2704 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2706 XtAppMainLoop(appContext);
2707 if (appData.debugMode) fclose(debugFP); // [DM] debug
2711 static Boolean noEcho;
2716 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2717 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2719 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2720 unlink(gameCopyFilename);
2721 unlink(gamePasteFilename);
2722 if(noEcho) EchoOn();
2725 RETSIGTYPE TermSizeSigHandler(int sig)
2738 CmailSigHandler(sig)
2744 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2746 /* Activate call-back function CmailSigHandlerCallBack() */
2747 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2749 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2753 CmailSigHandlerCallBack(isr, closure, message, count, error)
2761 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2763 /**** end signal code ****/
2769 /* try to open the icsLogon script, either in the location given
2770 * or in the users HOME directory
2777 f = fopen(appData.icsLogon, "r");
2780 homedir = getenv("HOME");
2781 if (homedir != NULL)
2783 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2784 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2785 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2786 f = fopen(buf, "r");
2791 ProcessICSInitScript(f);
2793 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2816 if (!menuBarWidget) return;
2817 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2819 DisplayError("menuEdit.Revert", 0);
2821 XtSetSensitive(w, !grey);
2823 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2825 DisplayError("menuEdit.Annotate", 0);
2827 XtSetSensitive(w, !grey);
2832 SetMenuEnables(enab)
2836 if (!menuBarWidget) return;
2837 while (enab->name != NULL) {
2838 w = XtNameToWidget(menuBarWidget, enab->name);
2840 DisplayError(enab->name, 0);
2842 XtSetSensitive(w, enab->value);
2848 Enables icsEnables[] = {
2849 { "menuFile.Mail Move", False },
2850 { "menuFile.Reload CMail Message", False },
2851 { "menuMode.Machine Black", False },
2852 { "menuMode.Machine White", False },
2853 { "menuMode.Analysis Mode", False },
2854 { "menuMode.Analyze File", False },
2855 { "menuMode.Two Machines", False },
2856 { "menuMode.Machine Match", False },
2858 { "menuEngine.Hint", False },
2859 { "menuEngine.Book", False },
2860 { "menuEngine.Move Now", False },
2861 #ifndef OPTIONSDIALOG
2862 { "menuOptions.Periodic Updates", False },
2863 { "menuOptions.Hide Thinking", False },
2864 { "menuOptions.Ponder Next Move", False },
2867 { "menuEngine.Engine #1 Settings", False },
2868 { "menuEngine.Engine #2 Settings", False },
2869 { "menuEngine.Load Engine", False },
2870 { "menuEdit.Annotate", False },
2871 { "menuOptions.Match", False },
2875 Enables ncpEnables[] = {
2876 { "menuFile.Mail Move", False },
2877 { "menuFile.Reload CMail Message", False },
2878 { "menuMode.Machine White", False },
2879 { "menuMode.Machine Black", False },
2880 { "menuMode.Analysis Mode", False },
2881 { "menuMode.Analyze File", False },
2882 { "menuMode.Two Machines", False },
2883 { "menuMode.Machine Match", False },
2884 { "menuMode.ICS Client", False },
2885 { "menuView.ICStex", False },
2886 { "menuView.ICS Input Box", False },
2887 { "Action", False },
2888 { "menuEdit.Revert", False },
2889 { "menuEdit.Annotate", False },
2890 { "menuEngine.Engine #1 Settings", False },
2891 { "menuEngine.Engine #2 Settings", False },
2892 { "menuEngine.Move Now", False },
2893 { "menuEngine.Retract Move", False },
2894 { "menuOptions.ICS", False },
2895 #ifndef OPTIONSDIALOG
2896 { "menuOptions.Auto Flag", False },
2897 { "menuOptions.Auto Flip View", False },
2898 // { "menuOptions.ICS Alarm", False },
2899 { "menuOptions.Move Sound", False },
2900 { "menuOptions.Hide Thinking", False },
2901 { "menuOptions.Periodic Updates", False },
2902 { "menuOptions.Ponder Next Move", False },
2904 { "menuEngine.Hint", False },
2905 { "menuEngine.Book", False },
2909 Enables gnuEnables[] = {
2910 { "menuMode.ICS Client", False },
2911 { "menuView.ICStex", False },
2912 { "menuView.ICS Input Box", False },
2913 { "menuAction.Accept", False },
2914 { "menuAction.Decline", False },
2915 { "menuAction.Rematch", False },
2916 { "menuAction.Adjourn", False },
2917 { "menuAction.Stop Examining", False },
2918 { "menuAction.Stop Observing", False },
2919 { "menuAction.Upload to Examine", False },
2920 { "menuEdit.Revert", False },
2921 { "menuEdit.Annotate", False },
2922 { "menuOptions.ICS", False },
2924 /* The next two options rely on SetCmailMode being called *after* */
2925 /* SetGNUMode so that when GNU is being used to give hints these */
2926 /* menu options are still available */
2928 { "menuFile.Mail Move", False },
2929 { "menuFile.Reload CMail Message", False },
2930 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2931 { "menuMode.Machine White", True },
2932 { "menuMode.Machine Black", True },
2933 { "menuMode.Analysis Mode", True },
2934 { "menuMode.Analyze File", True },
2935 { "menuMode.Two Machines", True },
2936 { "menuMode.Machine Match", True },
2937 { "menuEngine.Engine #1 Settings", True },
2938 { "menuEngine.Engine #2 Settings", True },
2939 { "menuEngine.Hint", True },
2940 { "menuEngine.Book", True },
2941 { "menuEngine.Move Now", True },
2942 { "menuEngine.Retract Move", True },
2947 Enables cmailEnables[] = {
2949 { "menuAction.Call Flag", False },
2950 { "menuAction.Draw", True },
2951 { "menuAction.Adjourn", False },
2952 { "menuAction.Abort", False },
2953 { "menuAction.Stop Observing", False },
2954 { "menuAction.Stop Examining", False },
2955 { "menuFile.Mail Move", True },
2956 { "menuFile.Reload CMail Message", True },
2960 Enables trainingOnEnables[] = {
2961 { "menuMode.Edit Comment", False },
2962 { "menuMode.Pause", False },
2963 { "menuEdit.Forward", False },
2964 { "menuEdit.Backward", False },
2965 { "menuEdit.Forward to End", False },
2966 { "menuEdit.Back to Start", False },
2967 { "menuEngine.Move Now", False },
2968 { "menuEdit.Truncate Game", False },
2972 Enables trainingOffEnables[] = {
2973 { "menuMode.Edit Comment", True },
2974 { "menuMode.Pause", True },
2975 { "menuEdit.Forward", True },
2976 { "menuEdit.Backward", True },
2977 { "menuEdit.Forward to End", True },
2978 { "menuEdit.Back to Start", True },
2979 { "menuEngine.Move Now", True },
2980 { "menuEdit.Truncate Game", True },
2984 Enables machineThinkingEnables[] = {
2985 { "menuFile.Load Game", False },
2986 // { "menuFile.Load Next Game", False },
2987 // { "menuFile.Load Previous Game", False },
2988 // { "menuFile.Reload Same Game", False },
2989 { "menuEdit.Paste Game", False },
2990 { "menuFile.Load Position", False },
2991 // { "menuFile.Load Next Position", False },
2992 // { "menuFile.Load Previous Position", False },
2993 // { "menuFile.Reload Same Position", False },
2994 { "menuEdit.Paste Position", False },
2995 { "menuMode.Machine White", False },
2996 { "menuMode.Machine Black", False },
2997 { "menuMode.Two Machines", False },
2998 // { "menuMode.Machine Match", False },
2999 { "menuEngine.Retract Move", False },
3003 Enables userThinkingEnables[] = {
3004 { "menuFile.Load Game", True },
3005 // { "menuFile.Load Next Game", True },
3006 // { "menuFile.Load Previous Game", True },
3007 // { "menuFile.Reload Same Game", True },
3008 { "menuEdit.Paste Game", True },
3009 { "menuFile.Load Position", True },
3010 // { "menuFile.Load Next Position", True },
3011 // { "menuFile.Load Previous Position", True },
3012 // { "menuFile.Reload Same Position", True },
3013 { "menuEdit.Paste Position", True },
3014 { "menuMode.Machine White", True },
3015 { "menuMode.Machine Black", True },
3016 { "menuMode.Two Machines", True },
3017 // { "menuMode.Machine Match", True },
3018 { "menuEngine.Retract Move", True },
3024 SetMenuEnables(icsEnables);
3027 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3028 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3029 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3037 SetMenuEnables(ncpEnables);
3043 SetMenuEnables(gnuEnables);
3049 SetMenuEnables(cmailEnables);
3055 SetMenuEnables(trainingOnEnables);
3056 if (appData.showButtonBar) {
3057 XtSetSensitive(buttonBarWidget, False);
3063 SetTrainingModeOff()
3065 SetMenuEnables(trainingOffEnables);
3066 if (appData.showButtonBar) {
3067 XtSetSensitive(buttonBarWidget, True);
3072 SetUserThinkingEnables()
3074 if (appData.noChessProgram) return;
3075 SetMenuEnables(userThinkingEnables);
3079 SetMachineThinkingEnables()
3081 if (appData.noChessProgram) return;
3082 SetMenuEnables(machineThinkingEnables);
3084 case MachinePlaysBlack:
3085 case MachinePlaysWhite:
3086 case TwoMachinesPlay:
3087 XtSetSensitive(XtNameToWidget(menuBarWidget,
3088 ModeToWidgetName(gameMode)), True);
3095 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3096 #define HISTORY_SIZE 64
3097 static char *history[HISTORY_SIZE];
3098 int histIn = 0, histP = 0;
3101 SaveInHistory(char *cmd)
3103 if (history[histIn] != NULL) {
3104 free(history[histIn]);
3105 history[histIn] = NULL;
3107 if (*cmd == NULLCHAR) return;
3108 history[histIn] = StrSave(cmd);
3109 histIn = (histIn + 1) % HISTORY_SIZE;
3110 if (history[histIn] != NULL) {
3111 free(history[histIn]);
3112 history[histIn] = NULL;
3118 PrevInHistory(char *cmd)
3121 if (histP == histIn) {
3122 if (history[histIn] != NULL) free(history[histIn]);
3123 history[histIn] = StrSave(cmd);
3125 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3126 if (newhp == histIn || history[newhp] == NULL) return NULL;
3128 return history[histP];
3134 if (histP == histIn) return NULL;
3135 histP = (histP + 1) % HISTORY_SIZE;
3136 return history[histP];
3138 // end of borrowed code
3140 #define Abs(n) ((n)<0 ? -(n) : (n))
3144 InsertPxlSize(pattern, targetPxlSize)
3148 char *base_fnt_lst, strInt[12], *p, *q;
3149 int alternatives, i, len, strIntLen;
3152 * Replace the "*" (if present) in the pixel-size slot of each
3153 * alternative with the targetPxlSize.
3157 while ((p = strchr(p, ',')) != NULL) {
3161 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3162 strIntLen = strlen(strInt);
3163 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3167 while (alternatives--) {
3168 char *comma = strchr(p, ',');
3169 for (i=0; i<14; i++) {
3170 char *hyphen = strchr(p, '-');
3172 if (comma && hyphen > comma) break;
3173 len = hyphen + 1 - p;
3174 if (i == 7 && *p == '*' && len == 2) {
3176 memcpy(q, strInt, strIntLen);
3186 len = comma + 1 - p;
3193 return base_fnt_lst;
3197 CreateFontSet(base_fnt_lst)
3201 char **missing_list;
3205 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3206 &missing_list, &missing_count, &def_string);
3207 if (appData.debugMode) {
3209 XFontStruct **font_struct_list;
3210 char **font_name_list;
3211 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3213 fprintf(debugFP, " got list %s, locale %s\n",
3214 XBaseFontNameListOfFontSet(fntSet),
3215 XLocaleOfFontSet(fntSet));
3216 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3217 for (i = 0; i < count; i++) {
3218 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3221 for (i = 0; i < missing_count; i++) {
3222 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3225 if (fntSet == NULL) {
3226 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3231 #else // not ENABLE_NLS
3233 * Find a font that matches "pattern" that is as close as
3234 * possible to the targetPxlSize. Prefer fonts that are k
3235 * pixels smaller to fonts that are k pixels larger. The
3236 * pattern must be in the X Consortium standard format,
3237 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3238 * The return value should be freed with XtFree when no
3242 FindFont(pattern, targetPxlSize)
3246 char **fonts, *p, *best, *scalable, *scalableTail;
3247 int i, j, nfonts, minerr, err, pxlSize;
3249 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3251 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3252 programName, pattern);
3259 for (i=0; i<nfonts; i++) {
3262 if (*p != '-') continue;
3264 if (*p == NULLCHAR) break;
3265 if (*p++ == '-') j++;
3267 if (j < 7) continue;
3270 scalable = fonts[i];
3273 err = pxlSize - targetPxlSize;
3274 if (Abs(err) < Abs(minerr) ||
3275 (minerr > 0 && err < 0 && -err == minerr)) {
3281 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3282 /* If the error is too big and there is a scalable font,
3283 use the scalable font. */
3284 int headlen = scalableTail - scalable;
3285 p = (char *) XtMalloc(strlen(scalable) + 10);
3286 while (isdigit(*scalableTail)) scalableTail++;
3287 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3289 p = (char *) XtMalloc(strlen(best) + 2);
3290 safeStrCpy(p, best, strlen(best)+1 );
3292 if (appData.debugMode) {
3293 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3294 pattern, targetPxlSize, p);
3296 XFreeFontNames(fonts);
3302 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3303 // must be called before all non-first callse to CreateGCs()
3304 XtReleaseGC(shellWidget, highlineGC);
3305 XtReleaseGC(shellWidget, lightSquareGC);
3306 XtReleaseGC(shellWidget, darkSquareGC);
3307 XtReleaseGC(shellWidget, lineGC);
3308 if (appData.monoMode) {
3309 if (DefaultDepth(xDisplay, xScreen) == 1) {
3310 XtReleaseGC(shellWidget, wbPieceGC);
3312 XtReleaseGC(shellWidget, bwPieceGC);
3315 XtReleaseGC(shellWidget, prelineGC);
3316 XtReleaseGC(shellWidget, jailSquareGC);
3317 XtReleaseGC(shellWidget, wdPieceGC);
3318 XtReleaseGC(shellWidget, wlPieceGC);
3319 XtReleaseGC(shellWidget, wjPieceGC);
3320 XtReleaseGC(shellWidget, bdPieceGC);
3321 XtReleaseGC(shellWidget, blPieceGC);
3322 XtReleaseGC(shellWidget, bjPieceGC);
3326 void CreateGCs(int redo)
3328 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3329 | GCBackground | GCFunction | GCPlaneMask;
3330 XGCValues gc_values;
3333 gc_values.plane_mask = AllPlanes;
3334 gc_values.line_width = lineGap;
3335 gc_values.line_style = LineSolid;
3336 gc_values.function = GXcopy;
3339 DeleteGCs(); // called a second time; clean up old GCs first
3340 } else { // [HGM] grid and font GCs created on first call only
3341 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3342 gc_values.background = XWhitePixel(xDisplay, xScreen);
3343 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3344 XSetFont(xDisplay, coordGC, coordFontID);
3346 // [HGM] make font for holdings counts (white on black)
3347 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3348 gc_values.background = XBlackPixel(xDisplay, xScreen);
3349 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3350 XSetFont(xDisplay, countGC, countFontID);
3352 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3353 gc_values.background = XBlackPixel(xDisplay, xScreen);
3354 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3356 if (appData.monoMode) {
3357 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3358 gc_values.background = XWhitePixel(xDisplay, xScreen);
3359 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3361 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3362 gc_values.background = XBlackPixel(xDisplay, xScreen);
3363 lightSquareGC = wbPieceGC
3364 = XtGetGC(shellWidget, value_mask, &gc_values);
3366 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3367 gc_values.background = XWhitePixel(xDisplay, xScreen);
3368 darkSquareGC = bwPieceGC
3369 = XtGetGC(shellWidget, value_mask, &gc_values);
3371 if (DefaultDepth(xDisplay, xScreen) == 1) {
3372 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3373 gc_values.function = GXcopyInverted;
3374 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3375 gc_values.function = GXcopy;
3376 if (XBlackPixel(xDisplay, xScreen) == 1) {
3377 bwPieceGC = darkSquareGC;
3378 wbPieceGC = copyInvertedGC;
3380 bwPieceGC = copyInvertedGC;
3381 wbPieceGC = lightSquareGC;
3385 gc_values.foreground = highlightSquareColor;
3386 gc_values.background = highlightSquareColor;
3387 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3389 gc_values.foreground = premoveHighlightColor;
3390 gc_values.background = premoveHighlightColor;
3391 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3393 gc_values.foreground = lightSquareColor;
3394 gc_values.background = darkSquareColor;
3395 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3397 gc_values.foreground = darkSquareColor;
3398 gc_values.background = lightSquareColor;
3399 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3401 gc_values.foreground = jailSquareColor;
3402 gc_values.background = jailSquareColor;
3403 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3405 gc_values.foreground = whitePieceColor;
3406 gc_values.background = darkSquareColor;
3407 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3409 gc_values.foreground = whitePieceColor;
3410 gc_values.background = lightSquareColor;
3411 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3413 gc_values.foreground = whitePieceColor;
3414 gc_values.background = jailSquareColor;
3415 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3417 gc_values.foreground = blackPieceColor;
3418 gc_values.background = darkSquareColor;
3419 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3421 gc_values.foreground = blackPieceColor;
3422 gc_values.background = lightSquareColor;
3423 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3425 gc_values.foreground = blackPieceColor;
3426 gc_values.background = jailSquareColor;
3427 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3431 void loadXIM(xim, xmask, filename, dest, mask)
3444 fp = fopen(filename, "rb");
3446 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3453 for (y=0; y<h; ++y) {
3454 for (x=0; x<h; ++x) {
3459 XPutPixel(xim, x, y, blackPieceColor);
3461 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3464 XPutPixel(xim, x, y, darkSquareColor);
3466 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3469 XPutPixel(xim, x, y, whitePieceColor);
3471 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3474 XPutPixel(xim, x, y, lightSquareColor);
3476 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3484 /* create Pixmap of piece */
3485 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3487 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3490 /* create Pixmap of clipmask
3491 Note: We assume the white/black pieces have the same
3492 outline, so we make only 6 masks. This is okay
3493 since the XPM clipmask routines do the same. */
3495 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3497 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3500 /* now create the 1-bit version */
3501 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3504 values.foreground = 1;
3505 values.background = 0;
3507 /* Don't use XtGetGC, not read only */
3508 maskGC = XCreateGC(xDisplay, *mask,
3509 GCForeground | GCBackground, &values);
3510 XCopyPlane(xDisplay, temp, *mask, maskGC,
3511 0, 0, squareSize, squareSize, 0, 0, 1);
3512 XFreePixmap(xDisplay, temp);
3517 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3519 void CreateXIMPieces()
3524 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3529 /* The XSynchronize calls were copied from CreatePieces.
3530 Not sure if needed, but can't hurt */
3531 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3534 /* temp needed by loadXIM() */
3535 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3536 0, 0, ss, ss, AllPlanes, XYPixmap);
3538 if (strlen(appData.pixmapDirectory) == 0) {
3542 if (appData.monoMode) {
3543 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3547 fprintf(stderr, _("\nLoading XIMs...\n"));
3549 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3550 fprintf(stderr, "%d", piece+1);
3551 for (kind=0; kind<4; kind++) {
3552 fprintf(stderr, ".");
3553 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3554 ExpandPathName(appData.pixmapDirectory),
3555 piece <= (int) WhiteKing ? "" : "w",
3556 pieceBitmapNames[piece],
3558 ximPieceBitmap[kind][piece] =
3559 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3560 0, 0, ss, ss, AllPlanes, XYPixmap);
3561 if (appData.debugMode)
3562 fprintf(stderr, _("(File:%s:) "), buf);
3563 loadXIM(ximPieceBitmap[kind][piece],
3565 &(xpmPieceBitmap2[kind][piece]),
3566 &(ximMaskPm2[piece]));
3567 if(piece <= (int)WhiteKing)
3568 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3570 fprintf(stderr," ");
3572 /* Load light and dark squares */
3573 /* If the LSQ and DSQ pieces don't exist, we will
3574 draw them with solid squares. */
3575 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3576 if (access(buf, 0) != 0) {
3580 fprintf(stderr, _("light square "));
3582 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3583 0, 0, ss, ss, AllPlanes, XYPixmap);
3584 if (appData.debugMode)
3585 fprintf(stderr, _("(File:%s:) "), buf);
3587 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3588 fprintf(stderr, _("dark square "));
3589 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3590 ExpandPathName(appData.pixmapDirectory), ss);
3591 if (appData.debugMode)
3592 fprintf(stderr, _("(File:%s:) "), buf);
3594 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3595 0, 0, ss, ss, AllPlanes, XYPixmap);
3596 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3597 xpmJailSquare = xpmLightSquare;
3599 fprintf(stderr, _("Done.\n"));
3601 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3604 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3607 void CreateXPMBoard(char *s, int kind)
3611 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3612 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3613 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3617 void FreeXPMPieces()
3618 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3619 // thisroutine has to be called t free the old piece pixmaps
3621 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3622 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3624 XFreePixmap(xDisplay, xpmLightSquare);
3625 XFreePixmap(xDisplay, xpmDarkSquare);
3629 void CreateXPMPieces()
3633 u_int ss = squareSize;
3635 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3636 XpmColorSymbol symbols[4];
3637 static int redo = False;
3639 if(redo) FreeXPMPieces(); else redo = 1;
3641 /* The XSynchronize calls were copied from CreatePieces.
3642 Not sure if needed, but can't hurt */
3643 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3645 /* Setup translations so piece colors match square colors */
3646 symbols[0].name = "light_piece";
3647 symbols[0].value = appData.whitePieceColor;
3648 symbols[1].name = "dark_piece";
3649 symbols[1].value = appData.blackPieceColor;
3650 symbols[2].name = "light_square";
3651 symbols[2].value = appData.lightSquareColor;
3652 symbols[3].name = "dark_square";
3653 symbols[3].value = appData.darkSquareColor;
3655 attr.valuemask = XpmColorSymbols;
3656 attr.colorsymbols = symbols;
3657 attr.numsymbols = 4;
3659 if (appData.monoMode) {
3660 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3664 if (strlen(appData.pixmapDirectory) == 0) {
3665 XpmPieces* pieces = builtInXpms;
3668 while (pieces->size != squareSize && pieces->size) pieces++;
3669 if (!pieces->size) {
3670 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3673 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3674 for (kind=0; kind<4; kind++) {
3676 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3677 pieces->xpm[piece][kind],
3678 &(xpmPieceBitmap2[kind][piece]),
3679 NULL, &attr)) != 0) {
3680 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3684 if(piece <= (int) WhiteKing)
3685 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3689 xpmJailSquare = xpmLightSquare;
3693 fprintf(stderr, _("\nLoading XPMs...\n"));
3696 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3697 fprintf(stderr, "%d ", piece+1);
3698 for (kind=0; kind<4; kind++) {
3699 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3700 ExpandPathName(appData.pixmapDirectory),
3701 piece > (int) WhiteKing ? "w" : "",
3702 pieceBitmapNames[piece],
3704 if (appData.debugMode) {
3705 fprintf(stderr, _("(File:%s:) "), buf);
3707 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3708 &(xpmPieceBitmap2[kind][piece]),
3709 NULL, &attr)) != 0) {
3710 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3711 // [HGM] missing: read of unorthodox piece failed; substitute King.
3712 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3713 ExpandPathName(appData.pixmapDirectory),
3715 if (appData.debugMode) {
3716 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3718 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3719 &(xpmPieceBitmap2[kind][piece]),
3723 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3728 if(piece <= (int) WhiteKing)
3729 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3732 /* Load light and dark squares */
3733 /* If the LSQ and DSQ pieces don't exist, we will
3734 draw them with solid squares. */
3735 fprintf(stderr, _("light square "));
3736 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3737 if (access(buf, 0) != 0) {
3741 if (appData.debugMode)
3742 fprintf(stderr, _("(File:%s:) "), buf);
3744 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3745 &xpmLightSquare, NULL, &attr)) != 0) {
3746 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3749 fprintf(stderr, _("dark square "));
3750 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3751 ExpandPathName(appData.pixmapDirectory), ss);
3752 if (appData.debugMode) {
3753 fprintf(stderr, _("(File:%s:) "), buf);
3755 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3756 &xpmDarkSquare, NULL, &attr)) != 0) {
3757 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3761 xpmJailSquare = xpmLightSquare;
3762 fprintf(stderr, _("Done.\n"));
3764 oldVariant = -1; // kludge to force re-makig of animation masks
3765 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3768 #endif /* HAVE_LIBXPM */
3771 /* No built-in bitmaps */
3776 u_int ss = squareSize;
3778 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3781 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3782 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3783 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3784 pieceBitmapNames[piece],
3785 ss, kind == SOLID ? 's' : 'o');
3786 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3787 if(piece <= (int)WhiteKing)
3788 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3792 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3796 /* With built-in bitmaps */
3799 BuiltInBits* bib = builtInBits;
3802 u_int ss = squareSize;
3804 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3807 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3809 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3810 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3811 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3812 pieceBitmapNames[piece],
3813 ss, kind == SOLID ? 's' : 'o');
3814 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3815 bib->bits[kind][piece], ss, ss);
3816 if(piece <= (int)WhiteKing)
3817 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3821 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3826 void ReadBitmap(pm, name, bits, wreq, hreq)
3829 unsigned char bits[];
3835 char msg[MSG_SIZ], fullname[MSG_SIZ];
3837 if (*appData.bitmapDirectory != NULLCHAR) {
3838 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3839 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3840 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3841 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3842 &w, &h, pm, &x_hot, &y_hot);
3843 fprintf(stderr, "load %s\n", name);
3844 if (errcode != BitmapSuccess) {
3846 case BitmapOpenFailed:
3847 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3849 case BitmapFileInvalid:
3850 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3852 case BitmapNoMemory:
3853 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3857 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3861 fprintf(stderr, _("%s: %s...using built-in\n"),
3863 } else if (w != wreq || h != hreq) {
3865 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3866 programName, fullname, w, h, wreq, hreq);
3872 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3881 if (lineGap == 0) return;
3883 /* [HR] Split this into 2 loops for non-square boards. */
3885 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3886 gridSegments[i].x1 = 0;
3887 gridSegments[i].x2 =
3888 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3889 gridSegments[i].y1 = gridSegments[i].y2
3890 = lineGap / 2 + (i * (squareSize + lineGap));
3893 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3894 gridSegments[j + i].y1 = 0;
3895 gridSegments[j + i].y2 =
3896 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3897 gridSegments[j + i].x1 = gridSegments[j + i].x2
3898 = lineGap / 2 + (j * (squareSize + lineGap));
3902 static void MenuBarSelect(w, addr, index)
3907 XtActionProc proc = (XtActionProc) addr;
3909 (proc)(NULL, NULL, NULL, NULL);
3912 void CreateMenuBarPopup(parent, name, mb)
3922 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3925 XtSetArg(args[j], XtNleftMargin, 20); j++;
3926 XtSetArg(args[j], XtNrightMargin, 20); j++;
3928 while (mi->string != NULL) {
3929 if (strcmp(mi->string, "----") == 0) {
3930 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3933 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3934 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3936 XtAddCallback(entry, XtNcallback,
3937 (XtCallbackProc) MenuBarSelect,
3938 (caddr_t) mi->proc);
3944 Widget CreateMenuBar(mb)
3948 Widget anchor, menuBar;
3950 char menuName[MSG_SIZ];
3953 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3954 XtSetArg(args[j], XtNvSpace, 0); j++;
3955 XtSetArg(args[j], XtNborderWidth, 0); j++;
3956 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3957 formWidget, args, j);
3959 while (mb->name != NULL) {
3960 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3961 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3963 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3966 shortName[0] = mb->name[0];
3967 shortName[1] = NULLCHAR;
3968 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3971 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3974 XtSetArg(args[j], XtNborderWidth, 0); j++;
3975 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3977 CreateMenuBarPopup(menuBar, menuName, mb);
3983 Widget CreateButtonBar(mi)
3987 Widget button, buttonBar;
3991 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3993 XtSetArg(args[j], XtNhSpace, 0); j++;
3995 XtSetArg(args[j], XtNborderWidth, 0); j++;
3996 XtSetArg(args[j], XtNvSpace, 0); j++;
3997 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3998 formWidget, args, j);
4000 while (mi->string != NULL) {
4003 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4004 XtSetArg(args[j], XtNborderWidth, 0); j++;
4006 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4007 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4008 buttonBar, args, j);
4009 XtAddCallback(button, XtNcallback,
4010 (XtCallbackProc) MenuBarSelect,
4011 (caddr_t) mi->proc);
4018 CreatePieceMenu(name, color)
4025 ChessSquare selection;
4027 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4028 boardWidget, args, 0);
4030 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4031 String item = pieceMenuStrings[color][i];
4033 if (strcmp(item, "----") == 0) {
4034 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4037 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4038 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4040 selection = pieceMenuTranslation[color][i];
4041 XtAddCallback(entry, XtNcallback,
4042 (XtCallbackProc) PieceMenuSelect,
4043 (caddr_t) selection);
4044 if (selection == WhitePawn || selection == BlackPawn) {
4045 XtSetArg(args[0], XtNpopupOnEntry, entry);
4046 XtSetValues(menu, args, 1);
4059 ChessSquare selection;
4061 whitePieceMenu = CreatePieceMenu("menuW", 0);
4062 blackPieceMenu = CreatePieceMenu("menuB", 1);
4064 XtRegisterGrabAction(PieceMenuPopup, True,
4065 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4066 GrabModeAsync, GrabModeAsync);
4068 XtSetArg(args[0], XtNlabel, _("Drop"));
4069 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4070 boardWidget, args, 1);
4071 for (i = 0; i < DROP_MENU_SIZE; i++) {
4072 String item = dropMenuStrings[i];
4074 if (strcmp(item, "----") == 0) {
4075 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4078 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4079 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4081 selection = dropMenuTranslation[i];
4082 XtAddCallback(entry, XtNcallback,
4083 (XtCallbackProc) DropMenuSelect,
4084 (caddr_t) selection);
4089 void SetupDropMenu()
4097 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4098 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4099 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4100 dmEnables[i].piece);
4101 XtSetSensitive(entry, p != NULL || !appData.testLegality
4102 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4103 && !appData.icsActive));
4105 while (p && *p++ == dmEnables[i].piece) count++;
4106 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4108 XtSetArg(args[j], XtNlabel, label); j++;
4109 XtSetValues(entry, args, j);
4113 void PieceMenuPopup(w, event, params, num_params)
4117 Cardinal *num_params;
4119 String whichMenu; int menuNr = -2;
4120 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4121 if (event->type == ButtonRelease)
4122 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4123 else if (event->type == ButtonPress)
4124 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4126 case 0: whichMenu = params[0]; break;
4127 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4129 case -1: if (errorUp) ErrorPopDown();
4132 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4135 static void PieceMenuSelect(w, piece, junk)
4140 if (pmFromX < 0 || pmFromY < 0) return;
4141 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4144 static void DropMenuSelect(w, piece, junk)
4149 if (pmFromX < 0 || pmFromY < 0) return;
4150 DropMenuEvent(piece, pmFromX, pmFromY);
4153 void WhiteClock(w, event, prms, nprms)
4159 shiftKey = prms[0][0] & 1;
4163 void BlackClock(w, event, prms, nprms)
4169 shiftKey = prms[0][0] & 1;
4175 * If the user selects on a border boundary, return -1; if off the board,
4176 * return -2. Otherwise map the event coordinate to the square.
4178 int EventToSquare(x, limit)
4186 if ((x % (squareSize + lineGap)) >= squareSize)
4188 x /= (squareSize + lineGap);
4194 static void do_flash_delay(msec)
4200 static void drawHighlight(file, rank, gc)
4206 if (lineGap == 0) return;
4209 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4210 (squareSize + lineGap);
4211 y = lineGap/2 + rank * (squareSize + lineGap);
4213 x = lineGap/2 + file * (squareSize + lineGap);
4214 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4215 (squareSize + lineGap);
4218 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4219 squareSize+lineGap, squareSize+lineGap);
4222 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4223 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4226 SetHighlights(fromX, fromY, toX, toY)
4227 int fromX, fromY, toX, toY;
4229 if (hi1X != fromX || hi1Y != fromY) {
4230 if (hi1X >= 0 && hi1Y >= 0) {
4231 drawHighlight(hi1X, hi1Y, lineGC);
4233 } // [HGM] first erase both, then draw new!
4234 if (hi2X != toX || hi2Y != toY) {
4235 if (hi2X >= 0 && hi2Y >= 0) {
4236 drawHighlight(hi2X, hi2Y, lineGC);
4239 if (hi1X != fromX || hi1Y != fromY) {
4240 if (fromX >= 0 && fromY >= 0) {
4241 drawHighlight(fromX, fromY, highlineGC);
4244 if (hi2X != toX || hi2Y != toY) {
4245 if (toX >= 0 && toY >= 0) {
4246 drawHighlight(toX, toY, highlineGC);
4258 SetHighlights(-1, -1, -1, -1);
4263 SetPremoveHighlights(fromX, fromY, toX, toY)
4264 int fromX, fromY, toX, toY;
4266 if (pm1X != fromX || pm1Y != fromY) {
4267 if (pm1X >= 0 && pm1Y >= 0) {
4268 drawHighlight(pm1X, pm1Y, lineGC);
4270 if (fromX >= 0 && fromY >= 0) {
4271 drawHighlight(fromX, fromY, prelineGC);
4274 if (pm2X != toX || pm2Y != toY) {
4275 if (pm2X >= 0 && pm2Y >= 0) {
4276 drawHighlight(pm2X, pm2Y, lineGC);
4278 if (toX >= 0 && toY >= 0) {
4279 drawHighlight(toX, toY, prelineGC);
4289 ClearPremoveHighlights()
4291 SetPremoveHighlights(-1, -1, -1, -1);
4294 static int CutOutSquare(x, y, x0, y0, kind)
4295 int x, y, *x0, *y0, kind;
4297 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4298 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4300 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4301 if(textureW[kind] < W*squareSize)
4302 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4304 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4305 if(textureH[kind] < H*squareSize)
4306 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4308 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4312 static void BlankSquare(x, y, color, piece, dest, fac)
4313 int x, y, color, fac;
4316 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4318 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4319 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4320 squareSize, squareSize, x*fac, y*fac);
4322 if (useImages && useImageSqs) {
4326 pm = xpmLightSquare;
4331 case 2: /* neutral */
4336 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4337 squareSize, squareSize, x*fac, y*fac);
4347 case 2: /* neutral */
4352 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4357 I split out the routines to draw a piece so that I could
4358 make a generic flash routine.
4360 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4362 int square_color, x, y;
4365 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4366 switch (square_color) {
4368 case 2: /* neutral */
4370 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4371 ? *pieceToOutline(piece)
4372 : *pieceToSolid(piece),
4373 dest, bwPieceGC, 0, 0,
4374 squareSize, squareSize, x, y);
4377 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4378 ? *pieceToSolid(piece)
4379 : *pieceToOutline(piece),
4380 dest, wbPieceGC, 0, 0,
4381 squareSize, squareSize, x, y);
4386 static void monoDrawPiece(piece, square_color, x, y, dest)
4388 int square_color, x, y;
4391 switch (square_color) {
4393 case 2: /* neutral */
4395 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4396 ? *pieceToOutline(piece)
4397 : *pieceToSolid(piece),
4398 dest, bwPieceGC, 0, 0,
4399 squareSize, squareSize, x, y, 1);
4402 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4403 ? *pieceToSolid(piece)
4404 : *pieceToOutline(piece),
4405 dest, wbPieceGC, 0, 0,
4406 squareSize, squareSize, x, y, 1);
4411 static void colorDrawPiece(piece, square_color, x, y, dest)
4413 int square_color, x, y;
4416 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4417 switch (square_color) {
4419 XCopyPlane(xDisplay, *pieceToSolid(piece),
4420 dest, (int) piece < (int) BlackPawn
4421 ? wlPieceGC : blPieceGC, 0, 0,
4422 squareSize, squareSize, x, y, 1);
4425 XCopyPlane(xDisplay, *pieceToSolid(piece),
4426 dest, (int) piece < (int) BlackPawn
4427 ? wdPieceGC : bdPieceGC, 0, 0,
4428 squareSize, squareSize, x, y, 1);
4430 case 2: /* neutral */
4432 XCopyPlane(xDisplay, *pieceToSolid(piece),
4433 dest, (int) piece < (int) BlackPawn
4434 ? wjPieceGC : bjPieceGC, 0, 0,
4435 squareSize, squareSize, x, y, 1);
4440 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4442 int square_color, x, y;
4445 int kind, p = piece;
4447 switch (square_color) {
4449 case 2: /* neutral */
4451 if ((int)piece < (int) BlackPawn) {
4459 if ((int)piece < (int) BlackPawn) {
4467 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4468 if(useTexture & square_color+1) {
4469 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4470 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4471 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4472 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4473 XSetClipMask(xDisplay, wlPieceGC, None);
4474 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4476 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4477 dest, wlPieceGC, 0, 0,
4478 squareSize, squareSize, x, y);
4481 typedef void (*DrawFunc)();
4483 DrawFunc ChooseDrawFunc()
4485 if (appData.monoMode) {
4486 if (DefaultDepth(xDisplay, xScreen) == 1) {
4487 return monoDrawPiece_1bit;
4489 return monoDrawPiece;
4493 return colorDrawPieceImage;
4495 return colorDrawPiece;
4499 /* [HR] determine square color depending on chess variant. */
4500 static int SquareColor(row, column)
4505 if (gameInfo.variant == VariantXiangqi) {
4506 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4508 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4510 } else if (row <= 4) {
4516 square_color = ((column + row) % 2) == 1;
4519 /* [hgm] holdings: next line makes all holdings squares light */
4520 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4522 return square_color;
4525 void DrawSquare(row, column, piece, do_flash)
4526 int row, column, do_flash;
4529 int square_color, x, y, direction, font_ascent, font_descent;
4532 XCharStruct overall;
4536 /* Calculate delay in milliseconds (2-delays per complete flash) */
4537 flash_delay = 500 / appData.flashRate;
4540 x = lineGap + ((BOARD_WIDTH-1)-column) *
4541 (squareSize + lineGap);
4542 y = lineGap + row * (squareSize + lineGap);
4544 x = lineGap + column * (squareSize + lineGap);
4545 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4546 (squareSize + lineGap);
4549 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4551 square_color = SquareColor(row, column);
4553 if ( // [HGM] holdings: blank out area between board and holdings
4554 column == BOARD_LEFT-1 || column == BOARD_RGHT
4555 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4556 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4557 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4559 // [HGM] print piece counts next to holdings
4560 string[1] = NULLCHAR;
4561 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4562 string[0] = '0' + piece;
4563 XTextExtents(countFontStruct, string, 1, &direction,
4564 &font_ascent, &font_descent, &overall);
4565 if (appData.monoMode) {
4566 XDrawImageString(xDisplay, xBoardWindow, countGC,
4567 x + squareSize - overall.width - 2,
4568 y + font_ascent + 1, string, 1);
4570 XDrawString(xDisplay, xBoardWindow, countGC,
4571 x + squareSize - overall.width - 2,
4572 y + font_ascent + 1, string, 1);
4575 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4576 string[0] = '0' + piece;
4577 XTextExtents(countFontStruct, string, 1, &direction,
4578 &font_ascent, &font_descent, &overall);
4579 if (appData.monoMode) {
4580 XDrawImageString(xDisplay, xBoardWindow, countGC,
4581 x + 2, y + font_ascent + 1, string, 1);
4583 XDrawString(xDisplay, xBoardWindow, countGC,
4584 x + 2, y + font_ascent + 1, string, 1);
4588 if (piece == EmptySquare || appData.blindfold) {
4589 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4591 drawfunc = ChooseDrawFunc();
4593 if (do_flash && appData.flashCount > 0) {
4594 for (i=0; i<appData.flashCount; ++i) {
4595 drawfunc(piece, square_color, x, y, xBoardWindow);
4596 XSync(xDisplay, False);
4597 do_flash_delay(flash_delay);
4599 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4600 XSync(xDisplay, False);
4601 do_flash_delay(flash_delay);
4604 drawfunc(piece, square_color, x, y, xBoardWindow);
4608 string[1] = NULLCHAR;
4609 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4610 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4611 string[0] = 'a' + column - BOARD_LEFT;
4612 XTextExtents(coordFontStruct, string, 1, &direction,
4613 &font_ascent, &font_descent, &overall);
4614 if (appData.monoMode) {
4615 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4616 x + squareSize - overall.width - 2,
4617 y + squareSize - font_descent - 1, string, 1);
4619 XDrawString(xDisplay, xBoardWindow, coordGC,
4620 x + squareSize - overall.width - 2,
4621 y + squareSize - font_descent - 1, string, 1);
4624 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4625 string[0] = ONE + row;
4626 XTextExtents(coordFontStruct, string, 1, &direction,
4627 &font_ascent, &font_descent, &overall);
4628 if (appData.monoMode) {
4629 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4630 x + 2, y + font_ascent + 1, string, 1);
4632 XDrawString(xDisplay, xBoardWindow, coordGC,
4633 x + 2, y + font_ascent + 1, string, 1);
4636 if(!partnerUp && marker[row][column]) {
4637 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4638 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4643 /* Why is this needed on some versions of X? */
4644 void EventProc(widget, unused, event)
4649 if (!XtIsRealized(widget))
4652 switch (event->type) {
4654 if (event->xexpose.count > 0) return; /* no clipping is done */
4655 XDrawPosition(widget, True, NULL);
4656 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4657 flipView = !flipView; partnerUp = !partnerUp;
4658 XDrawPosition(widget, True, NULL);
4659 flipView = !flipView; partnerUp = !partnerUp;
4663 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4670 void DrawPosition(fullRedraw, board)
4671 /*Boolean*/int fullRedraw;
4674 XDrawPosition(boardWidget, fullRedraw, board);
4677 /* Returns 1 if there are "too many" differences between b1 and b2
4678 (i.e. more than 1 move was made) */
4679 static int too_many_diffs(b1, b2)
4685 for (i=0; i<BOARD_HEIGHT; ++i) {
4686 for (j=0; j<BOARD_WIDTH; ++j) {
4687 if (b1[i][j] != b2[i][j]) {
4688 if (++c > 4) /* Castling causes 4 diffs */
4696 /* Matrix describing castling maneuvers */
4697 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4698 static int castling_matrix[4][5] = {
4699 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4700 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4701 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4702 { 7, 7, 4, 5, 6 } /* 0-0, black */
4705 /* Checks whether castling occurred. If it did, *rrow and *rcol
4706 are set to the destination (row,col) of the rook that moved.
4708 Returns 1 if castling occurred, 0 if not.
4710 Note: Only handles a max of 1 castling move, so be sure
4711 to call too_many_diffs() first.
4713 static int check_castle_draw(newb, oldb, rrow, rcol)
4720 /* For each type of castling... */
4721 for (i=0; i<4; ++i) {
4722 r = castling_matrix[i];
4724 /* Check the 4 squares involved in the castling move */
4726 for (j=1; j<=4; ++j) {
4727 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4734 /* All 4 changed, so it must be a castling move */
4743 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4744 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4746 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4749 void DrawSeekBackground( int left, int top, int right, int bottom )
4751 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4754 void DrawSeekText(char *buf, int x, int y)
4756 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4759 void DrawSeekDot(int x, int y, int colorNr)
4761 int square = colorNr & 0x80;
4764 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4766 XFillRectangle(xDisplay, xBoardWindow, color,
4767 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4769 XFillArc(xDisplay, xBoardWindow, color,
4770 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4773 static int damage[2][BOARD_RANKS][BOARD_FILES];
4776 * event handler for redrawing the board
4778 void XDrawPosition(w, repaint, board)
4780 /*Boolean*/int repaint;
4784 static int lastFlipView = 0;
4785 static int lastBoardValid[2] = {0, 0};
4786 static Board lastBoard[2];
4789 int nr = twoBoards*partnerUp;
4791 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4793 if (board == NULL) {
4794 if (!lastBoardValid[nr]) return;
4795 board = lastBoard[nr];
4797 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4798 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4799 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4804 * It would be simpler to clear the window with XClearWindow()
4805 * but this causes a very distracting flicker.
4808 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4810 if ( lineGap && IsDrawArrowEnabled())
4811 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4812 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4814 /* If too much changes (begin observing new game, etc.), don't
4816 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4818 /* Special check for castling so we don't flash both the king
4819 and the rook (just flash the king). */
4821 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4822 /* Draw rook with NO flashing. King will be drawn flashing later */
4823 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4824 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4828 /* First pass -- Draw (newly) empty squares and repair damage.
4829 This prevents you from having a piece show up twice while it
4830 is flashing on its new square */
4831 for (i = 0; i < BOARD_HEIGHT; i++)
4832 for (j = 0; j < BOARD_WIDTH; j++)
4833 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4834 || damage[nr][i][j]) {
4835 DrawSquare(i, j, board[i][j], 0);
4836 damage[nr][i][j] = False;
4839 /* Second pass -- Draw piece(s) in new position and flash them */
4840 for (i = 0; i < BOARD_HEIGHT; i++)
4841 for (j = 0; j < BOARD_WIDTH; j++)
4842 if (board[i][j] != lastBoard[nr][i][j]) {
4843 DrawSquare(i, j, board[i][j], do_flash);
4847 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4848 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4849 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4851 for (i = 0; i < BOARD_HEIGHT; i++)
4852 for (j = 0; j < BOARD_WIDTH; j++) {
4853 DrawSquare(i, j, board[i][j], 0);
4854 damage[nr][i][j] = False;
4858 CopyBoard(lastBoard[nr], board);
4859 lastBoardValid[nr] = 1;
4860 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4861 lastFlipView = flipView;
4863 /* Draw highlights */
4864 if (pm1X >= 0 && pm1Y >= 0) {
4865 drawHighlight(pm1X, pm1Y, prelineGC);
4867 if (pm2X >= 0 && pm2Y >= 0) {
4868 drawHighlight(pm2X, pm2Y, prelineGC);
4870 if (hi1X >= 0 && hi1Y >= 0) {
4871 drawHighlight(hi1X, hi1Y, highlineGC);
4873 if (hi2X >= 0 && hi2Y >= 0) {
4874 drawHighlight(hi2X, hi2Y, highlineGC);
4876 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4878 /* If piece being dragged around board, must redraw that too */
4881 XSync(xDisplay, False);
4886 * event handler for redrawing the board
4888 void DrawPositionProc(w, event, prms, nprms)
4894 XDrawPosition(w, True, NULL);
4899 * event handler for parsing user moves
4901 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4902 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4903 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4904 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4905 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4906 // and at the end FinishMove() to perform the move after optional promotion popups.
4907 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4908 void HandleUserMove(w, event, prms, nprms)
4914 if (w != boardWidget || errorExitStatus != -1) return;
4915 if(nprms) shiftKey = !strcmp(prms[0], "1");
4918 if (event->type == ButtonPress) {
4919 XtPopdown(promotionShell);
4920 XtDestroyWidget(promotionShell);
4921 promotionUp = False;
4929 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4930 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4931 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4934 void AnimateUserMove (Widget w, XEvent * event,
4935 String * params, Cardinal * nParams)
4937 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4938 DragPieceMove(event->xmotion.x, event->xmotion.y);
4941 void HandlePV (Widget w, XEvent * event,
4942 String * params, Cardinal * nParams)
4943 { // [HGM] pv: walk PV
4944 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4947 static int savedIndex; /* gross that this is global */
4949 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4952 XawTextPosition index, dummy;
4955 XawTextGetSelectionPos(w, &index, &dummy);
4956 XtSetArg(arg, XtNstring, &val);
4957 XtGetValues(w, &arg, 1);
4958 ReplaceComment(savedIndex, val);
4959 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4960 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4963 void EditCommentPopUp(index, title, text)
4968 if (text == NULL) text = "";
4969 NewCommentPopup(title, text, index);
4972 void ICSInputBoxPopUp()
4977 extern Option boxOptions[];
4979 void ICSInputSendText()
4986 edit = boxOptions[0].handle;
4988 XtSetArg(args[j], XtNstring, &val); j++;
4989 XtGetValues(edit, args, j);
4991 SendMultiLineToICS(val);
4992 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4993 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4996 void ICSInputBoxPopDown()
5001 void CommentPopUp(title, text)
5004 savedIndex = currentMove; // [HGM] vari
5005 NewCommentPopup(title, text, currentMove);
5008 void CommentPopDown()
5013 static char *openName;
5018 (void) (*fileProc)(openFP, 0, openName);
5021 void FileNamePopUp(label, def, filter, proc, openMode)
5028 fileProc = proc; /* I can't see a way not */
5029 fileOpenMode = openMode; /* to use globals here */
5030 { // [HGM] use file-selector dialog stolen from Ghostview
5031 int index; // this is not supported yet
5032 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5033 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5034 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5035 ScheduleDelayedEvent(&DelayedLoad, 50);
5039 void FileNamePopDown()
5041 if (!filenameUp) return;
5042 XtPopdown(fileNameShell);
5043 XtDestroyWidget(fileNameShell);
5048 void FileNameCallback(w, client_data, call_data)
5050 XtPointer client_data, call_data;
5055 XtSetArg(args[0], XtNlabel, &name);
5056 XtGetValues(w, args, 1);
5058 if (strcmp(name, _("cancel")) == 0) {
5063 FileNameAction(w, NULL, NULL, NULL);
5066 void FileNameAction(w, event, prms, nprms)
5078 name = XawDialogGetValueString(w = XtParent(w));
5080 if ((name != NULL) && (*name != NULLCHAR)) {
5081 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5082 XtPopdown(w = XtParent(XtParent(w)));
5086 p = strrchr(buf, ' ');
5093 fullname = ExpandPathName(buf);
5095 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5098 f = fopen(fullname, fileOpenMode);
5100 DisplayError(_("Failed to open file"), errno);
5102 (void) (*fileProc)(f, index, buf);
5109 XtPopdown(w = XtParent(XtParent(w)));
5115 void PromotionPopUp()
5118 Widget dialog, layout;
5120 Dimension bw_width, pw_width;
5124 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5125 XtGetValues(boardWidget, args, j);
5128 XtSetArg(args[j], XtNresizable, True); j++;
5129 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5131 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5132 shellWidget, args, j);
5134 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5135 layoutArgs, XtNumber(layoutArgs));
5138 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5139 XtSetArg(args[j], XtNborderWidth, 0); j++;
5140 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5143 if(gameInfo.variant != VariantShogi) {
5144 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5145 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5146 (XtPointer) dialog);
5147 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5148 (XtPointer) dialog);
5149 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5150 (XtPointer) dialog);
5151 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5152 (XtPointer) dialog);
5154 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5155 (XtPointer) dialog);
5156 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5157 (XtPointer) dialog);
5158 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5159 (XtPointer) dialog);
5160 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5161 (XtPointer) dialog);
5163 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5164 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5165 gameInfo.variant == VariantGiveaway) {
5166 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5167 (XtPointer) dialog);
5169 if(gameInfo.variant == VariantCapablanca ||
5170 gameInfo.variant == VariantGothic ||
5171 gameInfo.variant == VariantCapaRandom) {
5172 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5173 (XtPointer) dialog);
5174 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5175 (XtPointer) dialog);
5177 } else // [HGM] shogi
5179 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5180 (XtPointer) dialog);
5181 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5182 (XtPointer) dialog);
5184 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5185 (XtPointer) dialog);
5187 XtRealizeWidget(promotionShell);
5188 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5191 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5192 XtGetValues(promotionShell, args, j);
5194 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5195 lineGap + squareSize/3 +
5196 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5197 0 : 6*(squareSize + lineGap)), &x, &y);
5200 XtSetArg(args[j], XtNx, x); j++;
5201 XtSetArg(args[j], XtNy, y); j++;
5202 XtSetValues(promotionShell, args, j);
5204 XtPopup(promotionShell, XtGrabNone);
5209 void PromotionPopDown()
5211 if (!promotionUp) return;
5212 XtPopdown(promotionShell);
5213 XtDestroyWidget(promotionShell);
5214 promotionUp = False;
5217 void PromotionCallback(w, client_data, call_data)
5219 XtPointer client_data, call_data;
5225 XtSetArg(args[0], XtNlabel, &name);
5226 XtGetValues(w, args, 1);
5230 if (fromX == -1) return;
5232 if (strcmp(name, _("cancel")) == 0) {
5236 } else if (strcmp(name, _("Knight")) == 0) {
5238 } else if (strcmp(name, _("Promote")) == 0) {
5240 } else if (strcmp(name, _("Defer")) == 0) {
5243 promoChar = ToLower(name[0]);
5246 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5248 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5249 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5254 void ErrorCallback(w, client_data, call_data)
5256 XtPointer client_data, call_data;
5259 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5261 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5267 if (!errorUp) return;
5269 XtPopdown(errorShell);
5270 XtDestroyWidget(errorShell);
5271 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5274 void ErrorPopUp(title, label, modal)
5275 char *title, *label;
5279 Widget dialog, layout;
5283 Dimension bw_width, pw_width;
5284 Dimension pw_height;
5288 XtSetArg(args[i], XtNresizable, True); i++;
5289 XtSetArg(args[i], XtNtitle, title); i++;
5291 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5292 shellWidget, args, i);
5294 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5295 layoutArgs, XtNumber(layoutArgs));
5298 XtSetArg(args[i], XtNlabel, label); i++;
5299 XtSetArg(args[i], XtNborderWidth, 0); i++;
5300 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5303 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5305 XtRealizeWidget(errorShell);
5306 CatchDeleteWindow(errorShell, "ErrorPopDown");
5309 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5310 XtGetValues(boardWidget, args, i);
5312 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5313 XtSetArg(args[i], XtNheight, &pw_height); i++;
5314 XtGetValues(errorShell, args, i);
5317 /* This code seems to tickle an X bug if it is executed too soon
5318 after xboard starts up. The coordinates get transformed as if
5319 the main window was positioned at (0, 0).
5321 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5322 0 - pw_height + squareSize / 3, &x, &y);
5324 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5325 RootWindowOfScreen(XtScreen(boardWidget)),
5326 (bw_width - pw_width) / 2,
5327 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5331 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5334 XtSetArg(args[i], XtNx, x); i++;
5335 XtSetArg(args[i], XtNy, y); i++;
5336 XtSetValues(errorShell, args, i);
5339 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5342 /* Disable all user input other than deleting the window */
5343 static int frozen = 0;
5347 /* Grab by a widget that doesn't accept input */
5348 XtAddGrab(messageWidget, TRUE, FALSE);
5352 /* Undo a FreezeUI */
5355 if (!frozen) return;
5356 XtRemoveGrab(messageWidget);
5360 char *ModeToWidgetName(mode)
5364 case BeginningOfGame:
5365 if (appData.icsActive)
5366 return "menuMode.ICS Client";
5367 else if (appData.noChessProgram ||
5368 *appData.cmailGameName != NULLCHAR)
5369 return "menuMode.Edit Game";
5371 return "menuMode.Machine Black";
5372 case MachinePlaysBlack:
5373 return "menuMode.Machine Black";
5374 case MachinePlaysWhite:
5375 return "menuMode.Machine White";
5377 return "menuMode.Analysis Mode";
5379 return "menuMode.Analyze File";
5380 case TwoMachinesPlay:
5381 return "menuMode.Two Machines";
5383 return "menuMode.Edit Game";
5384 case PlayFromGameFile:
5385 return "menuFile.Load Game";
5387 return "menuMode.Edit Position";
5389 return "menuMode.Training";
5390 case IcsPlayingWhite:
5391 case IcsPlayingBlack:
5395 return "menuMode.ICS Client";
5402 void ModeHighlight()
5405 static int oldPausing = FALSE;
5406 static GameMode oldmode = (GameMode) -1;
5409 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5411 if (pausing != oldPausing) {
5412 oldPausing = pausing;
5414 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5416 XtSetArg(args[0], XtNleftBitmap, None);
5418 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5421 if (appData.showButtonBar) {
5422 /* Always toggle, don't set. Previous code messes up when
5423 invoked while the button is pressed, as releasing it
5424 toggles the state again. */
5427 XtSetArg(args[0], XtNbackground, &oldbg);
5428 XtSetArg(args[1], XtNforeground, &oldfg);
5429 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5431 XtSetArg(args[0], XtNbackground, oldfg);
5432 XtSetArg(args[1], XtNforeground, oldbg);
5434 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5438 wname = ModeToWidgetName(oldmode);
5439 if (wname != NULL) {
5440 XtSetArg(args[0], XtNleftBitmap, None);
5441 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5443 wname = ModeToWidgetName(gameMode);
5444 if (wname != NULL) {
5445 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5446 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5449 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5450 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5452 /* Maybe all the enables should be handled here, not just this one */
5453 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5454 gameMode == Training || gameMode == PlayFromGameFile);
5459 * Button/menu procedures
5461 void ResetProc(w, event, prms, nprms)
5470 int LoadGamePopUp(f, gameNumber, title)
5475 cmailMsgLoaded = FALSE;
5476 if (gameNumber == 0) {
5477 int error = GameListBuild(f);
5479 DisplayError(_("Cannot build game list"), error);
5480 } else if (!ListEmpty(&gameList) &&
5481 ((ListGame *) gameList.tailPred)->number > 1) {
5482 GameListPopUp(f, title);
5488 return LoadGame(f, gameNumber, title, FALSE);
5491 void LoadGameProc(w, event, prms, nprms)
5497 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5500 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5503 void LoadNextGameProc(w, event, prms, nprms)
5512 void LoadPrevGameProc(w, event, prms, nprms)
5521 void ReloadGameProc(w, event, prms, nprms)
5530 void LoadNextPositionProc(w, event, prms, nprms)
5539 void LoadPrevPositionProc(w, event, prms, nprms)
5548 void ReloadPositionProc(w, event, prms, nprms)
5557 void LoadPositionProc(w, event, prms, nprms)
5563 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5566 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5569 void SaveGameProc(w, event, prms, nprms)
5575 FileNamePopUp(_("Save game file name?"),
5576 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5577 appData.oldSaveStyle ? ".game" : ".pgn",
5581 void SavePositionProc(w, event, prms, nprms)
5587 FileNamePopUp(_("Save position file name?"),
5588 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5589 appData.oldSaveStyle ? ".pos" : ".fen",
5593 void ReloadCmailMsgProc(w, event, prms, nprms)
5599 ReloadCmailMsgEvent(FALSE);
5602 void MailMoveProc(w, event, prms, nprms)
5611 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5612 char *selected_fen_position=NULL;
5615 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5616 Atom *type_return, XtPointer *value_return,
5617 unsigned long *length_return, int *format_return)
5619 char *selection_tmp;
5621 if (!selected_fen_position) return False; /* should never happen */
5622 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5623 /* note: since no XtSelectionDoneProc was registered, Xt will
5624 * automatically call XtFree on the value returned. So have to
5625 * make a copy of it allocated with XtMalloc */
5626 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5627 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5629 *value_return=selection_tmp;
5630 *length_return=strlen(selection_tmp);
5631 *type_return=*target;
5632 *format_return = 8; /* bits per byte */
5634 } else if (*target == XA_TARGETS(xDisplay)) {
5635 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5636 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5637 targets_tmp[1] = XA_STRING;
5638 *value_return = targets_tmp;
5639 *type_return = XA_ATOM;
5641 *format_return = 8 * sizeof(Atom);
5642 if (*format_return > 32) {
5643 *length_return *= *format_return / 32;
5644 *format_return = 32;
5652 /* note: when called from menu all parameters are NULL, so no clue what the
5653 * Widget which was clicked on was, or what the click event was
5655 void CopyPositionProc(w, event, prms, nprms)
5662 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5663 * have a notion of a position that is selected but not copied.
5664 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5666 if(gameMode == EditPosition) EditPositionDone(TRUE);
5667 if (selected_fen_position) free(selected_fen_position);
5668 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5669 if (!selected_fen_position) return;
5670 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5672 SendPositionSelection,
5673 NULL/* lose_ownership_proc */ ,
5674 NULL/* transfer_done_proc */);
5675 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5677 SendPositionSelection,
5678 NULL/* lose_ownership_proc */ ,
5679 NULL/* transfer_done_proc */);
5682 /* function called when the data to Paste is ready */
5684 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5685 Atom *type, XtPointer value, unsigned long *len, int *format)
5688 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5689 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5690 EditPositionPasteFEN(fenstr);
5694 /* called when Paste Position button is pressed,
5695 * all parameters will be NULL */
5696 void PastePositionProc(w, event, prms, nprms)
5702 XtGetSelectionValue(menuBarWidget,
5703 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5704 /* (XtSelectionCallbackProc) */ PastePositionCB,
5705 NULL, /* client_data passed to PastePositionCB */
5707 /* better to use the time field from the event that triggered the
5708 * call to this function, but that isn't trivial to get
5716 SendGameSelection(Widget w, Atom *selection, Atom *target,
5717 Atom *type_return, XtPointer *value_return,
5718 unsigned long *length_return, int *format_return)
5720 char *selection_tmp;
5722 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5723 FILE* f = fopen(gameCopyFilename, "r");
5726 if (f == NULL) return False;
5730 selection_tmp = XtMalloc(len + 1);
5731 count = fread(selection_tmp, 1, len, f);
5734 XtFree(selection_tmp);
5737 selection_tmp[len] = NULLCHAR;
5738 *value_return = selection_tmp;
5739 *length_return = len;
5740 *type_return = *target;
5741 *format_return = 8; /* bits per byte */
5743 } else if (*target == XA_TARGETS(xDisplay)) {
5744 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5745 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5746 targets_tmp[1] = XA_STRING;
5747 *value_return = targets_tmp;
5748 *type_return = XA_ATOM;
5750 *format_return = 8 * sizeof(Atom);
5751 if (*format_return > 32) {
5752 *length_return *= *format_return / 32;
5753 *format_return = 32;
5761 void CopySomething()
5764 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5765 * have a notion of a game that is selected but not copied.
5766 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5768 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5771 NULL/* lose_ownership_proc */ ,
5772 NULL/* transfer_done_proc */);
5773 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5776 NULL/* lose_ownership_proc */ ,
5777 NULL/* transfer_done_proc */);
5780 /* note: when called from menu all parameters are NULL, so no clue what the
5781 * Widget which was clicked on was, or what the click event was
5783 void CopyGameProc(w, event, prms, nprms)
5791 ret = SaveGameToFile(gameCopyFilename, FALSE);
5797 void CopyGameListProc(w, event, prms, nprms)
5803 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5807 /* function called when the data to Paste is ready */
5809 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5810 Atom *type, XtPointer value, unsigned long *len, int *format)
5813 if (value == NULL || *len == 0) {
5814 return; /* nothing had been selected to copy */
5816 f = fopen(gamePasteFilename, "w");
5818 DisplayError(_("Can't open temp file"), errno);
5821 fwrite(value, 1, *len, f);
5824 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5827 /* called when Paste Game button is pressed,
5828 * all parameters will be NULL */
5829 void PasteGameProc(w, event, prms, nprms)
5835 XtGetSelectionValue(menuBarWidget,
5836 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5837 /* (XtSelectionCallbackProc) */ PasteGameCB,
5838 NULL, /* client_data passed to PasteGameCB */
5840 /* better to use the time field from the event that triggered the
5841 * call to this function, but that isn't trivial to get
5851 SaveGameProc(NULL, NULL, NULL, NULL);
5855 void QuitProc(w, event, prms, nprms)
5864 void PauseProc(w, event, prms, nprms)
5874 void MachineBlackProc(w, event, prms, nprms)
5880 MachineBlackEvent();
5883 void MachineWhiteProc(w, event, prms, nprms)
5889 MachineWhiteEvent();
5892 void AnalyzeModeProc(w, event, prms, nprms)
5900 if (!first.analysisSupport) {
5901 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5902 DisplayError(buf, 0);
5905 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5906 if (appData.icsActive) {
5907 if (gameMode != IcsObserving) {
5908 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5909 DisplayError(buf, 0);
5911 if (appData.icsEngineAnalyze) {
5912 if (appData.debugMode)
5913 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5919 /* if enable, use want disable icsEngineAnalyze */
5920 if (appData.icsEngineAnalyze) {
5925 appData.icsEngineAnalyze = TRUE;
5926 if (appData.debugMode)
5927 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5929 #ifndef OPTIONSDIALOG
5930 if (!appData.showThinking)
5931 ShowThinkingProc(w,event,prms,nprms);
5937 void AnalyzeFileProc(w, event, prms, nprms)
5943 if (!first.analysisSupport) {
5945 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5946 DisplayError(buf, 0);
5949 // Reset(FALSE, TRUE);
5950 #ifndef OPTIONSDIALOG
5951 if (!appData.showThinking)
5952 ShowThinkingProc(w,event,prms,nprms);
5955 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5956 AnalysisPeriodicEvent(1);
5959 void TwoMachinesProc(w, event, prms, nprms)
5968 void MatchProc(w, event, prms, nprms)
5977 void IcsClientProc(w, event, prms, nprms)
5986 void EditGameProc(w, event, prms, nprms)
5995 void EditPositionProc(w, event, prms, nprms)
6001 EditPositionEvent();
6004 void TrainingProc(w, event, prms, nprms)
6013 void EditCommentProc(w, event, prms, nprms)
6021 if (PopDown(1)) { // popdown succesful
6023 XtSetArg(args[j], XtNleftBitmap, None); j++;
6024 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6025 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6026 } else // was not up
6030 void IcsInputBoxProc(w, event, prms, nprms)
6036 if (!PopDown(4)) ICSInputBoxPopUp();
6039 void AcceptProc(w, event, prms, nprms)
6048 void DeclineProc(w, event, prms, nprms)
6057 void RematchProc(w, event, prms, nprms)
6066 void CallFlagProc(w, event, prms, nprms)
6075 void DrawProc(w, event, prms, nprms)
6084 void AbortProc(w, event, prms, nprms)
6093 void AdjournProc(w, event, prms, nprms)
6102 void ResignProc(w, event, prms, nprms)
6111 void AdjuWhiteProc(w, event, prms, nprms)
6117 UserAdjudicationEvent(+1);
6120 void AdjuBlackProc(w, event, prms, nprms)
6126 UserAdjudicationEvent(-1);
6129 void AdjuDrawProc(w, event, prms, nprms)
6135 UserAdjudicationEvent(0);
6138 void EnterKeyProc(w, event, prms, nprms)
6144 if (shellUp[4] == True)
6148 void UpKeyProc(w, event, prms, nprms)
6153 { // [HGM] input: let up-arrow recall previous line from history
6160 if (!shellUp[4]) return;
6161 edit = boxOptions[0].handle;
6163 XtSetArg(args[j], XtNstring, &val); j++;
6164 XtGetValues(edit, args, j);
6165 val = PrevInHistory(val);
6166 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6167 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6169 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6170 XawTextReplace(edit, 0, 0, &t);
6171 XawTextSetInsertionPoint(edit, 9999);
6175 void DownKeyProc(w, event, prms, nprms)
6180 { // [HGM] input: let down-arrow recall next line from history
6185 if (!shellUp[4]) return;
6186 edit = boxOptions[0].handle;
6187 val = NextInHistory();
6188 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6189 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6191 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6192 XawTextReplace(edit, 0, 0, &t);
6193 XawTextSetInsertionPoint(edit, 9999);
6197 void StopObservingProc(w, event, prms, nprms)
6203 StopObservingEvent();
6206 void StopExaminingProc(w, event, prms, nprms)
6212 StopExaminingEvent();
6215 void UploadProc(w, event, prms, nprms)
6225 void ForwardProc(w, event, prms, nprms)
6235 void BackwardProc(w, event, prms, nprms)
6244 void ToStartProc(w, event, prms, nprms)
6253 void ToEndProc(w, event, prms, nprms)
6262 void RevertProc(w, event, prms, nprms)
6271 void AnnotateProc(w, event, prms, nprms)
6280 void TruncateGameProc(w, event, prms, nprms)
6286 TruncateGameEvent();
6288 void RetractMoveProc(w, event, prms, nprms)
6297 void MoveNowProc(w, event, prms, nprms)
6306 void FlipViewProc(w, event, prms, nprms)
6312 flipView = !flipView;
6313 DrawPosition(True, NULL);
6316 void PonderNextMoveProc(w, event, prms, nprms)
6324 PonderNextMoveEvent(!appData.ponderNextMove);
6325 #ifndef OPTIONSDIALOG
6326 if (appData.ponderNextMove) {
6327 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6329 XtSetArg(args[0], XtNleftBitmap, None);
6331 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6336 #ifndef OPTIONSDIALOG
6337 void AlwaysQueenProc(w, event, prms, nprms)
6345 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6347 if (appData.alwaysPromoteToQueen) {
6348 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6350 XtSetArg(args[0], XtNleftBitmap, None);
6352 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6356 void AnimateDraggingProc(w, event, prms, nprms)
6364 appData.animateDragging = !appData.animateDragging;
6366 if (appData.animateDragging) {
6367 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6370 XtSetArg(args[0], XtNleftBitmap, None);
6372 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6376 void AnimateMovingProc(w, event, prms, nprms)
6384 appData.animate = !appData.animate;
6386 if (appData.animate) {
6387 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6390 XtSetArg(args[0], XtNleftBitmap, None);
6392 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6396 void AutoflagProc(w, event, prms, nprms)
6404 appData.autoCallFlag = !appData.autoCallFlag;
6406 if (appData.autoCallFlag) {
6407 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6409 XtSetArg(args[0], XtNleftBitmap, None);
6411 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6415 void AutoflipProc(w, event, prms, nprms)
6423 appData.autoFlipView = !appData.autoFlipView;
6425 if (appData.autoFlipView) {
6426 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6428 XtSetArg(args[0], XtNleftBitmap, None);
6430 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6434 void BlindfoldProc(w, event, prms, nprms)
6442 appData.blindfold = !appData.blindfold;
6444 if (appData.blindfold) {
6445 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6447 XtSetArg(args[0], XtNleftBitmap, None);
6449 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6452 DrawPosition(True, NULL);
6455 void TestLegalityProc(w, event, prms, nprms)
6463 appData.testLegality = !appData.testLegality;
6465 if (appData.testLegality) {
6466 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6468 XtSetArg(args[0], XtNleftBitmap, None);
6470 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6475 void FlashMovesProc(w, event, prms, nprms)
6483 if (appData.flashCount == 0) {
6484 appData.flashCount = 3;
6486 appData.flashCount = -appData.flashCount;
6489 if (appData.flashCount > 0) {
6490 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6492 XtSetArg(args[0], XtNleftBitmap, None);
6494 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6499 void HighlightDraggingProc(w, event, prms, nprms)
6507 appData.highlightDragging = !appData.highlightDragging;
6509 if (appData.highlightDragging) {
6510 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6512 XtSetArg(args[0], XtNleftBitmap, None);
6514 XtSetValues(XtNameToWidget(menuBarWidget,
6515 "menuOptions.Highlight Dragging"), args, 1);
6519 void HighlightLastMoveProc(w, event, prms, nprms)
6527 appData.highlightLastMove = !appData.highlightLastMove;
6529 if (appData.highlightLastMove) {
6530 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6532 XtSetArg(args[0], XtNleftBitmap, None);
6534 XtSetValues(XtNameToWidget(menuBarWidget,
6535 "menuOptions.Highlight Last Move"), args, 1);
6538 void HighlightArrowProc(w, event, prms, nprms)
6546 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6548 if (appData.highlightMoveWithArrow) {
6549 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6551 XtSetArg(args[0], XtNleftBitmap, None);
6553 XtSetValues(XtNameToWidget(menuBarWidget,
6554 "menuOptions.Arrow"), args, 1);
6558 void IcsAlarmProc(w, event, prms, nprms)
6566 appData.icsAlarm = !appData.icsAlarm;
6568 if (appData.icsAlarm) {
6569 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6571 XtSetArg(args[0], XtNleftBitmap, None);
6573 XtSetValues(XtNameToWidget(menuBarWidget,
6574 "menuOptions.ICS Alarm"), args, 1);
6578 void MoveSoundProc(w, event, prms, nprms)
6586 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6588 if (appData.ringBellAfterMoves) {
6589 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6591 XtSetArg(args[0], XtNleftBitmap, None);
6593 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6597 void OneClickProc(w, event, prms, nprms)
6605 appData.oneClick = !appData.oneClick;
6607 if (appData.oneClick) {
6608 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6610 XtSetArg(args[0], XtNleftBitmap, None);
6612 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6616 void PeriodicUpdatesProc(w, event, prms, nprms)
6624 PeriodicUpdatesEvent(!appData.periodicUpdates);
6626 if (appData.periodicUpdates) {
6627 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6629 XtSetArg(args[0], XtNleftBitmap, None);
6631 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6635 void PopupExitMessageProc(w, event, prms, nprms)
6643 appData.popupExitMessage = !appData.popupExitMessage;
6645 if (appData.popupExitMessage) {
6646 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6648 XtSetArg(args[0], XtNleftBitmap, None);
6650 XtSetValues(XtNameToWidget(menuBarWidget,
6651 "menuOptions.Popup Exit Message"), args, 1);
6654 void PopupMoveErrorsProc(w, event, prms, nprms)
6662 appData.popupMoveErrors = !appData.popupMoveErrors;
6664 if (appData.popupMoveErrors) {
6665 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6667 XtSetArg(args[0], XtNleftBitmap, None);
6669 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6674 void PremoveProc(w, event, prms, nprms)
6682 appData.premove = !appData.premove;
6684 if (appData.premove) {
6685 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6687 XtSetArg(args[0], XtNleftBitmap, None);
6689 XtSetValues(XtNameToWidget(menuBarWidget,
6690 "menuOptions.Premove"), args, 1);
6694 void ShowCoordsProc(w, event, prms, nprms)
6702 appData.showCoords = !appData.showCoords;
6704 if (appData.showCoords) {
6705 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6707 XtSetArg(args[0], XtNleftBitmap, None);
6709 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6712 DrawPosition(True, NULL);
6715 void ShowThinkingProc(w, event, prms, nprms)
6721 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6722 ShowThinkingEvent();
6725 void HideThinkingProc(w, event, prms, nprms)
6733 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6734 ShowThinkingEvent();
6736 if (appData.hideThinkingFromHuman) {
6737 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6739 XtSetArg(args[0], XtNleftBitmap, None);
6741 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6746 void SaveOnExitProc(w, event, prms, nprms)
6754 saveSettingsOnExit = !saveSettingsOnExit;
6756 if (saveSettingsOnExit) {
6757 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6759 XtSetArg(args[0], XtNleftBitmap, None);
6761 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6765 void SaveSettingsProc(w, event, prms, nprms)
6771 SaveSettings(settingsFileName);
6774 void InfoProc(w, event, prms, nprms)
6781 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6786 void ManProc(w, event, prms, nprms)
6794 if (nprms && *nprms > 0)
6798 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6802 void HintProc(w, event, prms, nprms)
6811 void BookProc(w, event, prms, nprms)
6820 void AboutProc(w, event, prms, nprms)
6828 char *zippy = _(" (with Zippy code)");
6832 snprintf(buf, sizeof(buf),
6834 "Copyright 1991 Digital Equipment Corporation\n"
6835 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6836 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6837 "%s is free software and carries NO WARRANTY;"
6838 "see the file COPYING for more information."),
6839 programVersion, zippy, PACKAGE);
6840 ErrorPopUp(_("About XBoard"), buf, FALSE);
6843 void DebugProc(w, event, prms, nprms)
6849 appData.debugMode = !appData.debugMode;
6852 void AboutGameProc(w, event, prms, nprms)
6861 void NothingProc(w, event, prms, nprms)
6870 void DisplayMessage(message, extMessage)
6871 char *message, *extMessage;
6873 /* display a message in the message widget */
6882 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6887 message = extMessage;
6891 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6893 /* need to test if messageWidget already exists, since this function
6894 can also be called during the startup, if for example a Xresource
6895 is not set up correctly */
6898 XtSetArg(arg, XtNlabel, message);
6899 XtSetValues(messageWidget, &arg, 1);
6905 void DisplayTitle(text)
6910 char title[MSG_SIZ];
6913 if (text == NULL) text = "";
6915 if (appData.titleInWindow) {
6917 XtSetArg(args[i], XtNlabel, text); i++;
6918 XtSetValues(titleWidget, args, i);
6921 if (*text != NULLCHAR) {
6922 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6923 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6924 } else if (appData.icsActive) {
6925 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6926 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6927 } else if (appData.cmailGameName[0] != NULLCHAR) {
6928 snprintf(icon, sizeof(icon), "%s", "CMail");
6929 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6931 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6932 } else if (gameInfo.variant == VariantGothic) {
6933 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6934 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6937 } else if (gameInfo.variant == VariantFalcon) {
6938 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6939 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6941 } else if (appData.noChessProgram) {
6942 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6943 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6945 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6946 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6949 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6950 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6951 XtSetValues(shellWidget, args, i);
6952 XSync(xDisplay, False);
6957 DisplayError(message, error)
6964 if (appData.debugMode || appData.matchMode) {
6965 fprintf(stderr, "%s: %s\n", programName, message);
6968 if (appData.debugMode || appData.matchMode) {
6969 fprintf(stderr, "%s: %s: %s\n",
6970 programName, message, strerror(error));
6972 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6975 ErrorPopUp(_("Error"), message, FALSE);
6979 void DisplayMoveError(message)
6984 DrawPosition(FALSE, NULL);
6985 if (appData.debugMode || appData.matchMode) {
6986 fprintf(stderr, "%s: %s\n", programName, message);
6988 if (appData.popupMoveErrors) {
6989 ErrorPopUp(_("Error"), message, FALSE);
6991 DisplayMessage(message, "");
6996 void DisplayFatalError(message, error, status)
7002 errorExitStatus = status;
7004 fprintf(stderr, "%s: %s\n", programName, message);
7006 fprintf(stderr, "%s: %s: %s\n",
7007 programName, message, strerror(error));
7008 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7011 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7012 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7018 void DisplayInformation(message)
7022 ErrorPopUp(_("Information"), message, TRUE);
7025 void DisplayNote(message)
7029 ErrorPopUp(_("Note"), message, FALSE);
7033 NullXErrorCheck(dpy, error_event)
7035 XErrorEvent *error_event;
7040 void DisplayIcsInteractionTitle(message)
7043 if (oldICSInteractionTitle == NULL) {
7044 /* Magic to find the old window title, adapted from vim */
7045 char *wina = getenv("WINDOWID");
7047 Window win = (Window) atoi(wina);
7048 Window root, parent, *children;
7049 unsigned int nchildren;
7050 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7052 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7053 if (!XQueryTree(xDisplay, win, &root, &parent,
7054 &children, &nchildren)) break;
7055 if (children) XFree((void *)children);
7056 if (parent == root || parent == 0) break;
7059 XSetErrorHandler(oldHandler);
7061 if (oldICSInteractionTitle == NULL) {
7062 oldICSInteractionTitle = "xterm";
7065 printf("\033]0;%s\007", message);
7069 char pendingReplyPrefix[MSG_SIZ];
7070 ProcRef pendingReplyPR;
7072 void AskQuestionProc(w, event, prms, nprms)
7079 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7083 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7086 void AskQuestionPopDown()
7088 if (!askQuestionUp) return;
7089 XtPopdown(askQuestionShell);
7090 XtDestroyWidget(askQuestionShell);
7091 askQuestionUp = False;
7094 void AskQuestionReplyAction(w, event, prms, nprms)
7104 reply = XawDialogGetValueString(w = XtParent(w));
7105 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7106 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7107 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7108 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7109 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7110 AskQuestionPopDown();
7112 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7115 void AskQuestionCallback(w, client_data, call_data)
7117 XtPointer client_data, call_data;
7122 XtSetArg(args[0], XtNlabel, &name);
7123 XtGetValues(w, args, 1);
7125 if (strcmp(name, _("cancel")) == 0) {
7126 AskQuestionPopDown();
7128 AskQuestionReplyAction(w, NULL, NULL, NULL);
7132 void AskQuestion(title, question, replyPrefix, pr)
7133 char *title, *question, *replyPrefix;
7137 Widget popup, layout, dialog, edit;
7143 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7144 pendingReplyPR = pr;
7147 XtSetArg(args[i], XtNresizable, True); i++;
7148 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7149 askQuestionShell = popup =
7150 XtCreatePopupShell(title, transientShellWidgetClass,
7151 shellWidget, args, i);
7154 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7155 layoutArgs, XtNumber(layoutArgs));
7158 XtSetArg(args[i], XtNlabel, question); i++;
7159 XtSetArg(args[i], XtNvalue, ""); i++;
7160 XtSetArg(args[i], XtNborderWidth, 0); i++;
7161 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7164 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7165 (XtPointer) dialog);
7166 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7167 (XtPointer) dialog);
7169 XtRealizeWidget(popup);
7170 CatchDeleteWindow(popup, "AskQuestionPopDown");
7172 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7173 &x, &y, &win_x, &win_y, &mask);
7175 XtSetArg(args[0], XtNx, x - 10);
7176 XtSetArg(args[1], XtNy, y - 30);
7177 XtSetValues(popup, args, 2);
7179 XtPopup(popup, XtGrabExclusive);
7180 askQuestionUp = True;
7182 edit = XtNameToWidget(dialog, "*value");
7183 XtSetKeyboardFocus(popup, edit);
7191 if (*name == NULLCHAR) {
7193 } else if (strcmp(name, "$") == 0) {
7194 putc(BELLCHAR, stderr);
7197 char *prefix = "", *sep = "";
7198 if(appData.soundProgram[0] == NULLCHAR) return;
7199 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7200 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7208 PlaySound(appData.soundMove);
7214 PlaySound(appData.soundIcsWin);
7220 PlaySound(appData.soundIcsLoss);
7226 PlaySound(appData.soundIcsDraw);
7230 PlayIcsUnfinishedSound()
7232 PlaySound(appData.soundIcsUnfinished);
7238 PlaySound(appData.soundIcsAlarm);
7244 PlaySound(appData.soundTell);
7250 system("stty echo");
7257 system("stty -echo");
7262 RunCommand(char *buf)
7268 Colorize(cc, continuation)
7273 int count, outCount, error;
7275 if (textColors[(int)cc].bg > 0) {
7276 if (textColors[(int)cc].fg > 0) {
7277 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7278 textColors[(int)cc].fg, textColors[(int)cc].bg);
7280 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7281 textColors[(int)cc].bg);
7284 if (textColors[(int)cc].fg > 0) {
7285 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7286 textColors[(int)cc].fg);
7288 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7291 count = strlen(buf);
7292 outCount = OutputToProcess(NoProc, buf, count, &error);
7293 if (outCount < count) {
7294 DisplayFatalError(_("Error writing to display"), error, 1);
7297 if (continuation) return;
7300 PlaySound(appData.soundShout);
7303 PlaySound(appData.soundSShout);
7306 PlaySound(appData.soundChannel1);
7309 PlaySound(appData.soundChannel);
7312 PlaySound(appData.soundKibitz);
7315 PlaySound(appData.soundTell);
7317 case ColorChallenge:
7318 PlaySound(appData.soundChallenge);
7321 PlaySound(appData.soundRequest);
7324 PlaySound(appData.soundSeek);
7335 return getpwuid(getuid())->pw_name;
7339 ExpandPathName(path)
7342 static char static_buf[4*MSG_SIZ];
7343 char *d, *s, buf[4*MSG_SIZ];
7349 while (*s && isspace(*s))
7358 if (*(s+1) == '/') {
7359 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7363 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7364 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7365 pwd = getpwnam(buf);
7368 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7372 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7373 strcat(d, strchr(s+1, '/'));
7377 safeStrCpy(d, s, 4*MSG_SIZ );
7384 static char host_name[MSG_SIZ];
7386 #if HAVE_GETHOSTNAME
7387 gethostname(host_name, MSG_SIZ);
7389 #else /* not HAVE_GETHOSTNAME */
7390 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7391 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7393 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7395 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7396 #endif /* not HAVE_GETHOSTNAME */
7399 XtIntervalId delayedEventTimerXID = 0;
7400 DelayedEventCallback delayedEventCallback = 0;
7405 delayedEventTimerXID = 0;
7406 delayedEventCallback();
7410 ScheduleDelayedEvent(cb, millisec)
7411 DelayedEventCallback cb; long millisec;
7413 if(delayedEventTimerXID && delayedEventCallback == cb)
7414 // [HGM] alive: replace, rather than add or flush identical event
7415 XtRemoveTimeOut(delayedEventTimerXID);
7416 delayedEventCallback = cb;
7417 delayedEventTimerXID =
7418 XtAppAddTimeOut(appContext, millisec,
7419 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7422 DelayedEventCallback
7425 if (delayedEventTimerXID) {
7426 return delayedEventCallback;
7433 CancelDelayedEvent()
7435 if (delayedEventTimerXID) {
7436 XtRemoveTimeOut(delayedEventTimerXID);
7437 delayedEventTimerXID = 0;
7441 XtIntervalId loadGameTimerXID = 0;
7443 int LoadGameTimerRunning()
7445 return loadGameTimerXID != 0;
7448 int StopLoadGameTimer()
7450 if (loadGameTimerXID != 0) {
7451 XtRemoveTimeOut(loadGameTimerXID);
7452 loadGameTimerXID = 0;
7460 LoadGameTimerCallback(arg, id)
7464 loadGameTimerXID = 0;
7469 StartLoadGameTimer(millisec)
7473 XtAppAddTimeOut(appContext, millisec,
7474 (XtTimerCallbackProc) LoadGameTimerCallback,
7478 XtIntervalId analysisClockXID = 0;
7481 AnalysisClockCallback(arg, id)
7485 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7486 || appData.icsEngineAnalyze) { // [DM]
7487 AnalysisPeriodicEvent(0);
7488 StartAnalysisClock();
7493 StartAnalysisClock()
7496 XtAppAddTimeOut(appContext, 2000,
7497 (XtTimerCallbackProc) AnalysisClockCallback,
7501 XtIntervalId clockTimerXID = 0;
7503 int ClockTimerRunning()
7505 return clockTimerXID != 0;
7508 int StopClockTimer()
7510 if (clockTimerXID != 0) {
7511 XtRemoveTimeOut(clockTimerXID);
7520 ClockTimerCallback(arg, id)
7529 StartClockTimer(millisec)
7533 XtAppAddTimeOut(appContext, millisec,
7534 (XtTimerCallbackProc) ClockTimerCallback,
7539 DisplayTimerLabel(w, color, timer, highlight)
7548 /* check for low time warning */
7549 Pixel foregroundOrWarningColor = timerForegroundPixel;
7552 appData.lowTimeWarning &&
7553 (timer / 1000) < appData.icsAlarmTime)
7554 foregroundOrWarningColor = lowTimeWarningColor;
7556 if (appData.clockMode) {
7557 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7558 XtSetArg(args[0], XtNlabel, buf);
7560 snprintf(buf, MSG_SIZ, "%s ", color);
7561 XtSetArg(args[0], XtNlabel, buf);
7566 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7567 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7569 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7570 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7573 XtSetValues(w, args, 3);
7577 DisplayWhiteClock(timeRemaining, highlight)
7583 if(appData.noGUI) return;
7584 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7585 if (highlight && iconPixmap == bIconPixmap) {
7586 iconPixmap = wIconPixmap;
7587 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7588 XtSetValues(shellWidget, args, 1);
7593 DisplayBlackClock(timeRemaining, highlight)
7599 if(appData.noGUI) return;
7600 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7601 if (highlight && iconPixmap == wIconPixmap) {
7602 iconPixmap = bIconPixmap;
7603 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7604 XtSetValues(shellWidget, args, 1);
7622 int StartChildProcess(cmdLine, dir, pr)
7629 int to_prog[2], from_prog[2];
7633 if (appData.debugMode) {
7634 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7637 /* We do NOT feed the cmdLine to the shell; we just
7638 parse it into blank-separated arguments in the
7639 most simple-minded way possible.
7642 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7645 while(*p == ' ') p++;
7647 if(*p == '"' || *p == '\'')
7648 p = strchr(++argv[i-1], *p);
7649 else p = strchr(p, ' ');
7650 if (p == NULL) break;
7655 SetUpChildIO(to_prog, from_prog);
7657 if ((pid = fork()) == 0) {
7659 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7660 close(to_prog[1]); // first close the unused pipe ends
7661 close(from_prog[0]);
7662 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7663 dup2(from_prog[1], 1);
7664 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7665 close(from_prog[1]); // and closing again loses one of the pipes!
7666 if(fileno(stderr) >= 2) // better safe than sorry...
7667 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7669 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7674 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7676 execvp(argv[0], argv);
7678 /* If we get here, exec failed */
7683 /* Parent process */
7685 close(from_prog[1]);
7687 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7690 cp->fdFrom = from_prog[0];
7691 cp->fdTo = to_prog[1];
7696 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7697 static RETSIGTYPE AlarmCallBack(int n)
7703 DestroyChildProcess(pr, signalType)
7707 ChildProc *cp = (ChildProc *) pr;
7709 if (cp->kind != CPReal) return;
7711 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7712 signal(SIGALRM, AlarmCallBack);
7714 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7715 kill(cp->pid, SIGKILL); // kill it forcefully
7716 wait((int *) 0); // and wait again
7720 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7722 /* Process is exiting either because of the kill or because of
7723 a quit command sent by the backend; either way, wait for it to die.
7732 InterruptChildProcess(pr)
7735 ChildProc *cp = (ChildProc *) pr;
7737 if (cp->kind != CPReal) return;
7738 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7741 int OpenTelnet(host, port, pr)
7746 char cmdLine[MSG_SIZ];
7748 if (port[0] == NULLCHAR) {
7749 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7751 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7753 return StartChildProcess(cmdLine, "", pr);
7756 int OpenTCP(host, port, pr)
7762 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7763 #else /* !OMIT_SOCKETS */
7764 struct addrinfo hints;
7765 struct addrinfo *ais, *ai;
7770 memset(&hints, 0, sizeof(hints));
7771 hints.ai_family = AF_UNSPEC;
7772 hints.ai_socktype = SOCK_STREAM;
7774 error = getaddrinfo(host, port, &hints, &ais);
7776 /* a getaddrinfo error is not an errno, so can't return it */
7777 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7778 host, port, gai_strerror(error));
7782 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7783 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7787 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7800 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7806 #endif /* !OMIT_SOCKETS */
7811 int OpenCommPort(name, pr)
7818 fd = open(name, 2, 0);
7819 if (fd < 0) return errno;
7821 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7831 int OpenLoopback(pr)
7837 SetUpChildIO(to, from);
7839 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7842 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7849 int OpenRcmd(host, user, cmd, pr)
7850 char *host, *user, *cmd;
7853 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7857 #define INPUT_SOURCE_BUF_SIZE 8192
7866 char buf[INPUT_SOURCE_BUF_SIZE];
7871 DoInputCallback(closure, source, xid)
7876 InputSource *is = (InputSource *) closure;
7881 if (is->lineByLine) {
7882 count = read(is->fd, is->unused,
7883 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7885 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7888 is->unused += count;
7890 while (p < is->unused) {
7891 q = memchr(p, '\n', is->unused - p);
7892 if (q == NULL) break;
7894 (is->func)(is, is->closure, p, q - p, 0);
7898 while (p < is->unused) {
7903 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7908 (is->func)(is, is->closure, is->buf, count, error);
7912 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7919 ChildProc *cp = (ChildProc *) pr;
7921 is = (InputSource *) calloc(1, sizeof(InputSource));
7922 is->lineByLine = lineByLine;
7926 is->fd = fileno(stdin);
7928 is->kind = cp->kind;
7929 is->fd = cp->fdFrom;
7932 is->unused = is->buf;
7935 is->xid = XtAppAddInput(appContext, is->fd,
7936 (XtPointer) (XtInputReadMask),
7937 (XtInputCallbackProc) DoInputCallback,
7939 is->closure = closure;
7940 return (InputSourceRef) is;
7944 RemoveInputSource(isr)
7947 InputSource *is = (InputSource *) isr;
7949 if (is->xid == 0) return;
7950 XtRemoveInput(is->xid);
7954 int OutputToProcess(pr, message, count, outError)
7960 static int line = 0;
7961 ChildProc *cp = (ChildProc *) pr;
7966 if (appData.noJoin || !appData.useInternalWrap)
7967 outCount = fwrite(message, 1, count, stdout);
7970 int width = get_term_width();
7971 int len = wrap(NULL, message, count, width, &line);
7972 char *msg = malloc(len);
7976 outCount = fwrite(message, 1, count, stdout);
7979 dbgchk = wrap(msg, message, count, width, &line);
7980 if (dbgchk != len && appData.debugMode)
7981 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7982 outCount = fwrite(msg, 1, dbgchk, stdout);
7988 outCount = write(cp->fdTo, message, count);
7998 /* Output message to process, with "ms" milliseconds of delay
7999 between each character. This is needed when sending the logon
8000 script to ICC, which for some reason doesn't like the
8001 instantaneous send. */
8002 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8009 ChildProc *cp = (ChildProc *) pr;
8014 r = write(cp->fdTo, message++, 1);
8027 /**** Animation code by Hugh Fisher, DCS, ANU.
8029 Known problem: if a window overlapping the board is
8030 moved away while a piece is being animated underneath,
8031 the newly exposed area won't be updated properly.
8032 I can live with this.
8034 Known problem: if you look carefully at the animation
8035 of pieces in mono mode, they are being drawn as solid
8036 shapes without interior detail while moving. Fixing
8037 this would be a major complication for minimal return.
8040 /* Masks for XPM pieces. Black and white pieces can have
8041 different shapes, but in the interest of retaining my
8042 sanity pieces must have the same outline on both light
8043 and dark squares, and all pieces must use the same
8044 background square colors/images. */
8046 static int xpmDone = 0;
8049 CreateAnimMasks (pieceDepth)
8056 unsigned long plane;
8059 /* Need a bitmap just to get a GC with right depth */
8060 buf = XCreatePixmap(xDisplay, xBoardWindow,
8062 values.foreground = 1;
8063 values.background = 0;
8064 /* Don't use XtGetGC, not read only */
8065 maskGC = XCreateGC(xDisplay, buf,
8066 GCForeground | GCBackground, &values);
8067 XFreePixmap(xDisplay, buf);
8069 buf = XCreatePixmap(xDisplay, xBoardWindow,
8070 squareSize, squareSize, pieceDepth);
8071 values.foreground = XBlackPixel(xDisplay, xScreen);
8072 values.background = XWhitePixel(xDisplay, xScreen);
8073 bufGC = XCreateGC(xDisplay, buf,
8074 GCForeground | GCBackground, &values);
8076 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8077 /* Begin with empty mask */
8078 if(!xpmDone) // [HGM] pieces: keep using existing
8079 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8080 squareSize, squareSize, 1);
8081 XSetFunction(xDisplay, maskGC, GXclear);
8082 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8083 0, 0, squareSize, squareSize);
8085 /* Take a copy of the piece */
8090 XSetFunction(xDisplay, bufGC, GXcopy);
8091 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8093 0, 0, squareSize, squareSize, 0, 0);
8095 /* XOR the background (light) over the piece */
8096 XSetFunction(xDisplay, bufGC, GXxor);
8098 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8099 0, 0, squareSize, squareSize, 0, 0);
8101 XSetForeground(xDisplay, bufGC, lightSquareColor);
8102 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8105 /* We now have an inverted piece image with the background
8106 erased. Construct mask by just selecting all the non-zero
8107 pixels - no need to reconstruct the original image. */
8108 XSetFunction(xDisplay, maskGC, GXor);
8110 /* Might be quicker to download an XImage and create bitmap
8111 data from it rather than this N copies per piece, but it
8112 only takes a fraction of a second and there is a much
8113 longer delay for loading the pieces. */
8114 for (n = 0; n < pieceDepth; n ++) {
8115 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8116 0, 0, squareSize, squareSize,
8122 XFreePixmap(xDisplay, buf);
8123 XFreeGC(xDisplay, bufGC);
8124 XFreeGC(xDisplay, maskGC);
8128 InitAnimState (anim, info)
8130 XWindowAttributes * info;
8135 /* Each buffer is square size, same depth as window */
8136 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8137 squareSize, squareSize, info->depth);
8138 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8139 squareSize, squareSize, info->depth);
8141 /* Create a plain GC for blitting */
8142 mask = GCForeground | GCBackground | GCFunction |
8143 GCPlaneMask | GCGraphicsExposures;
8144 values.foreground = XBlackPixel(xDisplay, xScreen);
8145 values.background = XWhitePixel(xDisplay, xScreen);
8146 values.function = GXcopy;
8147 values.plane_mask = AllPlanes;
8148 values.graphics_exposures = False;
8149 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8151 /* Piece will be copied from an existing context at
8152 the start of each new animation/drag. */
8153 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8155 /* Outline will be a read-only copy of an existing */
8156 anim->outlineGC = None;
8162 XWindowAttributes info;
8164 if (xpmDone && gameInfo.variant == oldVariant) return;
8165 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8166 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8168 InitAnimState(&game, &info);
8169 InitAnimState(&player, &info);
8171 /* For XPM pieces, we need bitmaps to use as masks. */
8173 CreateAnimMasks(info.depth), xpmDone = 1;
8178 static Boolean frameWaiting;
8180 static RETSIGTYPE FrameAlarm (sig)
8183 frameWaiting = False;
8184 /* In case System-V style signals. Needed?? */
8185 signal(SIGALRM, FrameAlarm);
8192 struct itimerval delay;
8194 XSync(xDisplay, False);
8197 frameWaiting = True;
8198 signal(SIGALRM, FrameAlarm);
8199 delay.it_interval.tv_sec =
8200 delay.it_value.tv_sec = time / 1000;
8201 delay.it_interval.tv_usec =
8202 delay.it_value.tv_usec = (time % 1000) * 1000;
8203 setitimer(ITIMER_REAL, &delay, NULL);
8204 while (frameWaiting) pause();
8205 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8206 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8207 setitimer(ITIMER_REAL, &delay, NULL);
8217 XSync(xDisplay, False);
8219 usleep(time * 1000);
8230 /* Convert board position to corner of screen rect and color */
8233 ScreenSquare(column, row, pt, color)
8234 int column; int row; XPoint * pt; int * color;
8237 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8238 pt->y = lineGap + row * (squareSize + lineGap);
8240 pt->x = lineGap + column * (squareSize + lineGap);
8241 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8243 *color = SquareColor(row, column);
8246 /* Convert window coords to square */
8249 BoardSquare(x, y, column, row)
8250 int x; int y; int * column; int * row;
8252 *column = EventToSquare(x, BOARD_WIDTH);
8253 if (flipView && *column >= 0)
8254 *column = BOARD_WIDTH - 1 - *column;
8255 *row = EventToSquare(y, BOARD_HEIGHT);
8256 if (!flipView && *row >= 0)
8257 *row = BOARD_HEIGHT - 1 - *row;
8262 #undef Max /* just in case */
8264 #define Max(a, b) ((a) > (b) ? (a) : (b))
8265 #define Min(a, b) ((a) < (b) ? (a) : (b))
8268 SetRect(rect, x, y, width, height)
8269 XRectangle * rect; int x; int y; int width; int height;
8273 rect->width = width;
8274 rect->height = height;
8277 /* Test if two frames overlap. If they do, return
8278 intersection rect within old and location of
8279 that rect within new. */
8282 Intersect(old, new, size, area, pt)
8283 XPoint * old; XPoint * new;
8284 int size; XRectangle * area; XPoint * pt;
8286 if (old->x > new->x + size || new->x > old->x + size ||
8287 old->y > new->y + size || new->y > old->y + size) {
8290 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8291 size - abs(old->x - new->x), size - abs(old->y - new->y));
8292 pt->x = Max(old->x - new->x, 0);
8293 pt->y = Max(old->y - new->y, 0);
8298 /* For two overlapping frames, return the rect(s)
8299 in the old that do not intersect with the new. */
8302 CalcUpdateRects(old, new, size, update, nUpdates)
8303 XPoint * old; XPoint * new; int size;
8304 XRectangle update[]; int * nUpdates;
8308 /* If old = new (shouldn't happen) then nothing to draw */
8309 if (old->x == new->x && old->y == new->y) {
8313 /* Work out what bits overlap. Since we know the rects
8314 are the same size we don't need a full intersect calc. */
8316 /* Top or bottom edge? */
8317 if (new->y > old->y) {
8318 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8320 } else if (old->y > new->y) {
8321 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8322 size, old->y - new->y);
8325 /* Left or right edge - don't overlap any update calculated above. */
8326 if (new->x > old->x) {
8327 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8328 new->x - old->x, size - abs(new->y - old->y));
8330 } else if (old->x > new->x) {
8331 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8332 old->x - new->x, size - abs(new->y - old->y));
8339 /* Generate a series of frame coords from start->mid->finish.
8340 The movement rate doubles until the half way point is
8341 reached, then halves back down to the final destination,
8342 which gives a nice slow in/out effect. The algorithmn
8343 may seem to generate too many intermediates for short
8344 moves, but remember that the purpose is to attract the
8345 viewers attention to the piece about to be moved and
8346 then to where it ends up. Too few frames would be less
8350 Tween(start, mid, finish, factor, frames, nFrames)
8351 XPoint * start; XPoint * mid;
8352 XPoint * finish; int factor;
8353 XPoint frames[]; int * nFrames;
8355 int fraction, n, count;
8359 /* Slow in, stepping 1/16th, then 1/8th, ... */
8361 for (n = 0; n < factor; n++)
8363 for (n = 0; n < factor; n++) {
8364 frames[count].x = start->x + (mid->x - start->x) / fraction;
8365 frames[count].y = start->y + (mid->y - start->y) / fraction;
8367 fraction = fraction / 2;
8371 frames[count] = *mid;
8374 /* Slow out, stepping 1/2, then 1/4, ... */
8376 for (n = 0; n < factor; n++) {
8377 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8378 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8380 fraction = fraction * 2;
8385 /* Draw a piece on the screen without disturbing what's there */
8388 SelectGCMask(piece, clip, outline, mask)
8389 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8393 /* Bitmap for piece being moved. */
8394 if (appData.monoMode) {
8395 *mask = *pieceToSolid(piece);
8396 } else if (useImages) {
8398 *mask = xpmMask[piece];
8400 *mask = ximMaskPm[piece];
8403 *mask = *pieceToSolid(piece);
8406 /* GC for piece being moved. Square color doesn't matter, but
8407 since it gets modified we make a copy of the original. */
8409 if (appData.monoMode)
8414 if (appData.monoMode)
8419 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8421 /* Outline only used in mono mode and is not modified */
8423 *outline = bwPieceGC;
8425 *outline = wbPieceGC;
8429 OverlayPiece(piece, clip, outline, dest)
8430 ChessSquare piece; GC clip; GC outline; Drawable dest;
8435 /* Draw solid rectangle which will be clipped to shape of piece */
8436 XFillRectangle(xDisplay, dest, clip,
8437 0, 0, squareSize, squareSize);
8438 if (appData.monoMode)
8439 /* Also draw outline in contrasting color for black
8440 on black / white on white cases */
8441 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8442 0, 0, squareSize, squareSize, 0, 0, 1);
8444 /* Copy the piece */
8449 if(appData.upsideDown && flipView) kind ^= 2;
8450 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8452 0, 0, squareSize, squareSize,
8457 /* Animate the movement of a single piece */
8460 BeginAnimation(anim, piece, startColor, start)
8468 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8469 /* The old buffer is initialised with the start square (empty) */
8470 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8471 anim->prevFrame = *start;
8473 /* The piece will be drawn using its own bitmap as a matte */
8474 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8475 XSetClipMask(xDisplay, anim->pieceGC, mask);
8479 AnimationFrame(anim, frame, piece)
8484 XRectangle updates[4];
8489 /* Save what we are about to draw into the new buffer */
8490 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8491 frame->x, frame->y, squareSize, squareSize,
8494 /* Erase bits of the previous frame */
8495 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8496 /* Where the new frame overlapped the previous,
8497 the contents in newBuf are wrong. */
8498 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8499 overlap.x, overlap.y,
8500 overlap.width, overlap.height,
8502 /* Repaint the areas in the old that don't overlap new */
8503 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8504 for (i = 0; i < count; i++)
8505 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8506 updates[i].x - anim->prevFrame.x,
8507 updates[i].y - anim->prevFrame.y,
8508 updates[i].width, updates[i].height,
8509 updates[i].x, updates[i].y);
8511 /* Easy when no overlap */
8512 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8513 0, 0, squareSize, squareSize,
8514 anim->prevFrame.x, anim->prevFrame.y);
8517 /* Save this frame for next time round */
8518 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8519 0, 0, squareSize, squareSize,
8521 anim->prevFrame = *frame;
8523 /* Draw piece over original screen contents, not current,
8524 and copy entire rect. Wipes out overlapping piece images. */
8525 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8526 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8527 0, 0, squareSize, squareSize,
8528 frame->x, frame->y);
8532 EndAnimation (anim, finish)
8536 XRectangle updates[4];
8541 /* The main code will redraw the final square, so we
8542 only need to erase the bits that don't overlap. */
8543 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8544 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8545 for (i = 0; i < count; i++)
8546 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8547 updates[i].x - anim->prevFrame.x,
8548 updates[i].y - anim->prevFrame.y,
8549 updates[i].width, updates[i].height,
8550 updates[i].x, updates[i].y);
8552 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8553 0, 0, squareSize, squareSize,
8554 anim->prevFrame.x, anim->prevFrame.y);
8559 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8561 ChessSquare piece; int startColor;
8562 XPoint * start; XPoint * finish;
8563 XPoint frames[]; int nFrames;
8567 BeginAnimation(anim, piece, startColor, start);
8568 for (n = 0; n < nFrames; n++) {
8569 AnimationFrame(anim, &(frames[n]), piece);
8570 FrameDelay(appData.animSpeed);
8572 EndAnimation(anim, finish);
8576 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8579 ChessSquare piece = board[fromY][toY];
8580 board[fromY][toY] = EmptySquare;
8581 DrawPosition(FALSE, board);
8583 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8584 y = lineGap + toY * (squareSize + lineGap);
8586 x = lineGap + toX * (squareSize + lineGap);
8587 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8589 for(i=1; i<4*kFactor; i++) {
8590 int r = squareSize * 9 * i/(20*kFactor - 5);
8591 XFillArc(xDisplay, xBoardWindow, highlineGC,
8592 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8593 FrameDelay(appData.animSpeed);
8595 board[fromY][toY] = piece;
8598 /* Main control logic for deciding what to animate and how */
8601 AnimateMove(board, fromX, fromY, toX, toY)
8610 XPoint start, finish, mid;
8611 XPoint frames[kFactor * 2 + 1];
8612 int nFrames, startColor, endColor;
8614 /* Are we animating? */
8615 if (!appData.animate || appData.blindfold)
8618 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8619 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8620 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8622 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8623 piece = board[fromY][fromX];
8624 if (piece >= EmptySquare) return;
8629 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8632 if (appData.debugMode) {
8633 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8634 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8635 piece, fromX, fromY, toX, toY); }
8637 ScreenSquare(fromX, fromY, &start, &startColor);
8638 ScreenSquare(toX, toY, &finish, &endColor);
8641 /* Knight: make straight movement then diagonal */
8642 if (abs(toY - fromY) < abs(toX - fromX)) {
8643 mid.x = start.x + (finish.x - start.x) / 2;
8647 mid.y = start.y + (finish.y - start.y) / 2;
8650 mid.x = start.x + (finish.x - start.x) / 2;
8651 mid.y = start.y + (finish.y - start.y) / 2;
8654 /* Don't use as many frames for very short moves */
8655 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8656 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8658 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8659 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8660 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8662 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8663 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8666 /* Be sure end square is redrawn */
8667 damage[0][toY][toX] = True;
8671 DragPieceBegin(x, y, instantly)
8672 int x; int y; Boolean instantly;
8674 int boardX, boardY, color;
8677 /* Are we animating? */
8678 if (!appData.animateDragging || appData.blindfold)
8681 /* Figure out which square we start in and the
8682 mouse position relative to top left corner. */
8683 BoardSquare(x, y, &boardX, &boardY);
8684 player.startBoardX = boardX;
8685 player.startBoardY = boardY;
8686 ScreenSquare(boardX, boardY, &corner, &color);
8687 player.startSquare = corner;
8688 player.startColor = color;
8689 /* As soon as we start dragging, the piece will jump slightly to
8690 be centered over the mouse pointer. */
8691 player.mouseDelta.x = squareSize/2;
8692 player.mouseDelta.y = squareSize/2;
8693 /* Initialise animation */
8694 player.dragPiece = PieceForSquare(boardX, boardY);
8696 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8697 player.dragActive = True;
8698 BeginAnimation(&player, player.dragPiece, color, &corner);
8699 /* Mark this square as needing to be redrawn. Note that
8700 we don't remove the piece though, since logically (ie
8701 as seen by opponent) the move hasn't been made yet. */
8702 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8703 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8704 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8705 corner.x, corner.y, squareSize, squareSize,
8706 0, 0); // [HGM] zh: unstack in stead of grab
8707 if(gatingPiece != EmptySquare) {
8708 /* Kludge alert: When gating we want the introduced
8709 piece to appear on the from square. To generate an
8710 image of it, we draw it on the board, copy the image,
8711 and draw the original piece again. */
8712 ChessSquare piece = boards[currentMove][boardY][boardX];
8713 DrawSquare(boardY, boardX, gatingPiece, 0);
8714 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8715 corner.x, corner.y, squareSize, squareSize, 0, 0);
8716 DrawSquare(boardY, boardX, piece, 0);
8718 damage[0][boardY][boardX] = True;
8720 player.dragActive = False;
8725 ChangeDragPiece(ChessSquare piece)
8728 player.dragPiece = piece;
8729 /* The piece will be drawn using its own bitmap as a matte */
8730 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8731 XSetClipMask(xDisplay, player.pieceGC, mask);
8740 /* Are we animating? */
8741 if (!appData.animateDragging || appData.blindfold)
8745 if (! player.dragActive)
8747 /* Move piece, maintaining same relative position
8748 of mouse within square */
8749 corner.x = x - player.mouseDelta.x;
8750 corner.y = y - player.mouseDelta.y;
8751 AnimationFrame(&player, &corner, player.dragPiece);
8753 if (appData.highlightDragging) {
8755 BoardSquare(x, y, &boardX, &boardY);
8756 SetHighlights(fromX, fromY, boardX, boardY);
8765 int boardX, boardY, color;
8768 /* Are we animating? */
8769 if (!appData.animateDragging || appData.blindfold)
8773 if (! player.dragActive)
8775 /* Last frame in sequence is square piece is
8776 placed on, which may not match mouse exactly. */
8777 BoardSquare(x, y, &boardX, &boardY);
8778 ScreenSquare(boardX, boardY, &corner, &color);
8779 EndAnimation(&player, &corner);
8781 /* Be sure end square is redrawn */
8782 damage[0][boardY][boardX] = True;
8784 /* This prevents weird things happening with fast successive
8785 clicks which on my Sun at least can cause motion events
8786 without corresponding press/release. */
8787 player.dragActive = False;
8790 /* Handle expose event while piece being dragged */
8795 if (!player.dragActive || appData.blindfold)
8798 /* What we're doing: logically, the move hasn't been made yet,
8799 so the piece is still in it's original square. But visually
8800 it's being dragged around the board. So we erase the square
8801 that the piece is on and draw it at the last known drag point. */
8802 BlankSquare(player.startSquare.x, player.startSquare.y,
8803 player.startColor, EmptySquare, xBoardWindow, 1);
8804 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8805 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8808 #include <sys/ioctl.h>
8809 int get_term_width()
8811 int fd, default_width;
8814 default_width = 79; // this is FICS default anyway...
8816 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8818 if (!ioctl(fd, TIOCGSIZE, &win))
8819 default_width = win.ts_cols;
8820 #elif defined(TIOCGWINSZ)
8822 if (!ioctl(fd, TIOCGWINSZ, &win))
8823 default_width = win.ws_col;
8825 return default_width;
8831 static int old_width = 0;
8832 int new_width = get_term_width();
8834 if (old_width != new_width)
8835 ics_printf("set width %d\n", new_width);
8836 old_width = new_width;
8839 void NotifyFrontendLogin()
8844 /* [AS] Arrow highlighting support */
8846 static double A_WIDTH = 5; /* Width of arrow body */
8848 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8849 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8851 static double Sqr( double x )
8856 static int Round( double x )
8858 return (int) (x + 0.5);
8861 void SquareToPos(int rank, int file, int *x, int *y)
8864 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8865 *y = lineGap + rank * (squareSize + lineGap);
8867 *x = lineGap + file * (squareSize + lineGap);
8868 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8872 /* Draw an arrow between two points using current settings */
8873 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8876 double dx, dy, j, k, x, y;
8879 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8881 arrow[0].x = s_x + A_WIDTH + 0.5;
8884 arrow[1].x = s_x + A_WIDTH + 0.5;
8885 arrow[1].y = d_y - h;
8887 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8888 arrow[2].y = d_y - h;
8893 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8894 arrow[5].y = d_y - h;
8896 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8897 arrow[4].y = d_y - h;
8899 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8902 else if( d_y == s_y ) {
8903 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8906 arrow[0].y = s_y + A_WIDTH + 0.5;
8908 arrow[1].x = d_x - w;
8909 arrow[1].y = s_y + A_WIDTH + 0.5;
8911 arrow[2].x = d_x - w;
8912 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8917 arrow[5].x = d_x - w;
8918 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8920 arrow[4].x = d_x - w;
8921 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8924 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8927 /* [AS] Needed a lot of paper for this! :-) */
8928 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8929 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8931 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8933 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8938 arrow[0].x = Round(x - j);
8939 arrow[0].y = Round(y + j*dx);
8941 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8942 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8945 x = (double) d_x - k;
8946 y = (double) d_y - k*dy;
8949 x = (double) d_x + k;
8950 y = (double) d_y + k*dy;
8953 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8955 arrow[6].x = Round(x - j);
8956 arrow[6].y = Round(y + j*dx);
8958 arrow[2].x = Round(arrow[6].x + 2*j);
8959 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8961 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8962 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8967 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8968 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8971 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8972 // Polygon( hdc, arrow, 7 );
8975 /* [AS] Draw an arrow between two squares */
8976 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8978 int s_x, s_y, d_x, d_y, hor, vert, i;
8980 if( s_col == d_col && s_row == d_row ) {
8984 /* Get source and destination points */
8985 SquareToPos( s_row, s_col, &s_x, &s_y);
8986 SquareToPos( d_row, d_col, &d_x, &d_y);
8989 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8991 else if( d_y < s_y ) {
8992 d_y += squareSize / 2 + squareSize / 4;
8995 d_y += squareSize / 2;
8999 d_x += squareSize / 2 - squareSize / 4;
9001 else if( d_x < s_x ) {
9002 d_x += squareSize / 2 + squareSize / 4;
9005 d_x += squareSize / 2;
9008 s_x += squareSize / 2;
9009 s_y += squareSize / 2;
9012 A_WIDTH = squareSize / 14.; //[HGM] make float
9014 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9016 hor = 64*s_col + 32; vert = 64*s_row + 32;
9017 for(i=0; i<= 64; i++) {
9018 damage[0][vert+6>>6][hor+6>>6] = True;
9019 damage[0][vert-6>>6][hor+6>>6] = True;
9020 damage[0][vert+6>>6][hor-6>>6] = True;
9021 damage[0][vert-6>>6][hor-6>>6] = True;
9022 hor += d_col - s_col; vert += d_row - s_row;
9026 Boolean IsDrawArrowEnabled()
9028 return appData.highlightMoveWithArrow && squareSize >= 32;
9031 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9033 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9034 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9037 void UpdateLogos(int displ)
9039 return; // no logos in XBoard yet