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
145 #include <X11/Intrinsic.h>
146 #include <X11/StringDefs.h>
147 #include <X11/Shell.h>
148 #include <X11/cursorfont.h>
149 #include <X11/Xatom.h>
150 #include <X11/Xmu/Atoms.h>
152 #include <X11/Xaw3d/Dialog.h>
153 #include <X11/Xaw3d/Form.h>
154 #include <X11/Xaw3d/List.h>
155 #include <X11/Xaw3d/Label.h>
156 #include <X11/Xaw3d/SimpleMenu.h>
157 #include <X11/Xaw3d/SmeBSB.h>
158 #include <X11/Xaw3d/SmeLine.h>
159 #include <X11/Xaw3d/Box.h>
160 #include <X11/Xaw3d/MenuButton.h>
161 #include <X11/Xaw3d/Text.h>
162 #include <X11/Xaw3d/AsciiText.h>
164 #include <X11/Xaw/Dialog.h>
165 #include <X11/Xaw/Form.h>
166 #include <X11/Xaw/List.h>
167 #include <X11/Xaw/Label.h>
168 #include <X11/Xaw/SimpleMenu.h>
169 #include <X11/Xaw/SmeBSB.h>
170 #include <X11/Xaw/SmeLine.h>
171 #include <X11/Xaw/Box.h>
172 #include <X11/Xaw/MenuButton.h>
173 #include <X11/Xaw/Text.h>
174 #include <X11/Xaw/AsciiText.h>
177 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
182 #include "pixmaps/pixmaps.h"
183 #define IMAGE_EXT "xpm"
185 #define IMAGE_EXT "xim"
186 #include "bitmaps/bitmaps.h"
189 #include "bitmaps/icon_white.bm"
190 #include "bitmaps/icon_black.bm"
191 #include "bitmaps/checkmark.bm"
193 #include "frontend.h"
195 #include "backendz.h"
199 #include "xgamelist.h"
200 #include "xhistory.h"
201 #include "xedittags.h"
204 // must be moved to xengineoutput.h
206 void EngineOutputProc P((Widget w, XEvent *event,
207 String *prms, Cardinal *nprms));
208 void EvalGraphProc P((Widget w, XEvent *event,
209 String *prms, Cardinal *nprms));
216 #define usleep(t) _sleep2(((t)+500)/1000)
220 # define _(s) gettext (s)
221 # define N_(s) gettext_noop (s)
239 int main P((int argc, char **argv));
240 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
241 char *init_path, char *mode, int (*show_entry)(), char **name_return));
242 RETSIGTYPE CmailSigHandler P((int sig));
243 RETSIGTYPE IntSigHandler P((int sig));
244 RETSIGTYPE TermSizeSigHandler P((int sig));
245 void CreateGCs P((int redo));
246 void CreateXIMPieces P((void));
247 void CreateXPMPieces P((void));
248 void CreateXPMBoard P((char *s, int n));
249 void CreatePieces P((void));
250 void CreatePieceMenus P((void));
251 Widget CreateMenuBar P((Menu *mb));
252 Widget CreateButtonBar P ((MenuItem *mi));
253 char *FindFont P((char *pattern, int targetPxlSize));
254 void PieceMenuPopup P((Widget w, XEvent *event,
255 String *params, Cardinal *num_params));
256 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
257 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
258 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
259 u_int wreq, u_int hreq));
260 void CreateGrid P((void));
261 int EventToSquare P((int x, int limit));
262 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
263 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
264 void HandleUserMove P((Widget w, XEvent *event,
265 String *prms, Cardinal *nprms));
266 void AnimateUserMove P((Widget w, XEvent * event,
267 String * params, Cardinal * nParams));
268 void HandlePV P((Widget w, XEvent * event,
269 String * params, Cardinal * nParams));
270 void SelectPV P((Widget w, XEvent * event,
271 String * params, Cardinal * nParams));
272 void StopPV P((Widget w, XEvent * event,
273 String * params, Cardinal * nParams));
274 void WhiteClock P((Widget w, XEvent *event,
275 String *prms, Cardinal *nprms));
276 void BlackClock P((Widget w, XEvent *event,
277 String *prms, Cardinal *nprms));
278 void DrawPositionProc P((Widget w, XEvent *event,
279 String *prms, Cardinal *nprms));
280 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
282 void CommentClick P((Widget w, XEvent * event,
283 String * params, Cardinal * nParams));
284 void CommentPopUp P((char *title, char *label));
285 void CommentPopDown P((void));
286 void CommentCallback P((Widget w, XtPointer client_data,
287 XtPointer call_data));
288 void ICSInputBoxPopUp P((void));
289 void ICSInputBoxPopDown P((void));
290 void FileNamePopUp P((char *label, char *def,
291 FileProc proc, char *openMode));
292 void FileNamePopDown P((void));
293 void FileNameCallback P((Widget w, XtPointer client_data,
294 XtPointer call_data));
295 void FileNameAction P((Widget w, XEvent *event,
296 String *prms, Cardinal *nprms));
297 void AskQuestionReplyAction P((Widget w, XEvent *event,
298 String *prms, Cardinal *nprms));
299 void AskQuestionProc P((Widget w, XEvent *event,
300 String *prms, Cardinal *nprms));
301 void AskQuestionPopDown P((void));
302 void PromotionPopDown P((void));
303 void PromotionCallback P((Widget w, XtPointer client_data,
304 XtPointer call_data));
305 void EditCommentPopDown P((void));
306 void EditCommentCallback P((Widget w, XtPointer client_data,
307 XtPointer call_data));
308 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
309 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
310 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
311 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
313 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
315 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
317 void LoadPositionProc P((Widget w, XEvent *event,
318 String *prms, Cardinal *nprms));
319 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
321 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
323 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
325 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
327 void PastePositionProc P((Widget w, XEvent *event, String *prms,
329 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
330 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
331 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
332 void SavePositionProc P((Widget w, XEvent *event,
333 String *prms, Cardinal *nprms));
334 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
335 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
337 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
341 void MachineWhiteProc P((Widget w, XEvent *event,
342 String *prms, Cardinal *nprms));
343 void AnalyzeModeProc P((Widget w, XEvent *event,
344 String *prms, Cardinal *nprms));
345 void AnalyzeFileProc P((Widget w, XEvent *event,
346 String *prms, Cardinal *nprms));
347 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
349 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
350 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
351 void IcsClientProc P((Widget w, XEvent *event, String *prms,
353 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
354 void EditPositionProc P((Widget w, XEvent *event,
355 String *prms, Cardinal *nprms));
356 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void EditCommentProc P((Widget w, XEvent *event,
358 String *prms, Cardinal *nprms));
359 void IcsInputBoxProc P((Widget w, XEvent *event,
360 String *prms, Cardinal *nprms));
361 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void StopObservingProc P((Widget w, XEvent *event, String *prms,
377 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
379 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
386 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
388 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
390 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
393 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
395 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
397 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
400 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
402 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
403 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
405 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
407 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
409 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
412 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
414 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
416 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
418 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
420 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
421 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
423 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
425 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
427 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
429 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
430 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
431 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
433 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void DisplayMove P((int moveNumber));
441 void DisplayTitle P((char *title));
442 void ICSInitScript P((void));
443 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
444 void ErrorPopUp P((char *title, char *text, int modal));
445 void ErrorPopDown P((void));
446 static char *ExpandPathName P((char *path));
447 static void CreateAnimVars P((void));
448 static void DragPieceMove P((int x, int y));
449 static void DrawDragPiece P((void));
450 char *ModeToWidgetName P((GameMode mode));
451 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
453 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
454 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
455 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void GameListOptionsPopDown P(());
467 void ShufflePopDown P(());
468 void TimeControlPopDown P(());
469 void GenericPopDown P(());
470 void update_ics_width P(());
471 int get_term_width P(());
472 int CopyMemoProc P(());
473 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
474 Boolean IsDrawArrowEnabled P(());
477 * XBoard depends on Xt R4 or higher
479 int xtVersion = XtSpecificationRelease;
484 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
485 jailSquareColor, highlightSquareColor, premoveHighlightColor;
486 Pixel lowTimeWarningColor;
487 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
488 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
489 wjPieceGC, bjPieceGC, prelineGC, countGC;
490 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
491 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
492 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
493 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
494 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
495 ICSInputShell, fileNameShell, askQuestionShell;
496 Widget historyShell, evalGraphShell, gameListShell;
497 int hOffset; // [HGM] dual
498 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
499 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
500 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
501 Font clockFontID, coordFontID, countFontID;
502 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
503 XtAppContext appContext;
505 char *oldICSInteractionTitle;
509 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
511 Position commentX = -1, commentY = -1;
512 Dimension commentW, commentH;
513 typedef unsigned int BoardSize;
515 Boolean chessProgram;
517 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
518 int squareSize, smallLayout = 0, tinyLayout = 0,
519 marginW, marginH, // [HGM] for run-time resizing
520 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
521 ICSInputBoxUp = False, askQuestionUp = False,
522 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
523 editUp = False, errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
524 Pixel timerForegroundPixel, timerBackgroundPixel;
525 Pixel buttonForegroundPixel, buttonBackgroundPixel;
526 char *chessDir, *programName, *programVersion,
527 *gameCopyFilename, *gamePasteFilename;
528 Boolean alwaysOnTop = False;
529 Boolean saveSettingsOnExit;
530 char *settingsFileName;
531 char *icsTextMenuString;
533 char *firstChessProgramNames;
534 char *secondChessProgramNames;
536 WindowPlacement wpMain;
537 WindowPlacement wpConsole;
538 WindowPlacement wpComment;
539 WindowPlacement wpMoveHistory;
540 WindowPlacement wpEvalGraph;
541 WindowPlacement wpEngineOutput;
542 WindowPlacement wpGameList;
543 WindowPlacement wpTags;
547 Pixmap pieceBitmap[2][(int)BlackPawn];
548 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
549 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
550 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
551 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
552 Pixmap xpmBoardBitmap[2];
553 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
554 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
555 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
556 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
557 XImage *ximLightSquare, *ximDarkSquare;
560 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
561 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
563 #define White(piece) ((int)(piece) < (int)BlackPawn)
565 /* Variables for doing smooth animation. This whole thing
566 would be much easier if the board was double-buffered,
567 but that would require a fairly major rewrite. */
572 GC blitGC, pieceGC, outlineGC;
573 XPoint startSquare, prevFrame, mouseDelta;
577 int startBoardX, startBoardY;
580 /* There can be two pieces being animated at once: a player
581 can begin dragging a piece before the remote opponent has moved. */
583 static AnimState game, player;
585 /* Bitmaps for use as masks when drawing XPM pieces.
586 Need one for each black and white piece. */
587 static Pixmap xpmMask[BlackKing + 1];
589 /* This magic number is the number of intermediate frames used
590 in each half of the animation. For short moves it's reduced
591 by 1. The total number of frames will be factor * 2 + 1. */
594 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
596 MenuItem fileMenu[] = {
597 {N_("New Game Ctrl+N"), "New Game", ResetProc},
598 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
599 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
600 {"----", NULL, NothingProc},
601 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
602 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
603 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
604 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
605 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
606 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
607 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
608 {"----", NULL, NothingProc},
609 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
610 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
611 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
612 {"----", NULL, NothingProc},
613 {N_("Mail Move"), "Mail Move", MailMoveProc},
614 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
615 {"----", NULL, NothingProc},
616 {N_("Quit Ctr+Q"), "Exit", QuitProc},
620 MenuItem editMenu[] = {
621 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
622 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
623 {"----", NULL, NothingProc},
624 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
625 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
626 {"----", NULL, NothingProc},
627 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
628 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
629 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
630 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
631 {"----", NULL, NothingProc},
632 {N_("Revert Home"), "Revert", RevertProc},
633 {N_("Annotate"), "Annotate", AnnotateProc},
634 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
635 {"----", NULL, NothingProc},
636 {N_("Backward Alt+Left"), "Backward", BackwardProc},
637 {N_("Forward Alt+Right"), "Forward", ForwardProc},
638 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
639 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
643 MenuItem viewMenu[] = {
644 {N_("Flip View F2"), "Flip View", FlipViewProc},
645 {"----", NULL, NothingProc},
646 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
647 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
648 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
649 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
650 {N_("ICS text menu"), "ICStex", IcsTextProc},
651 {"----", NULL, NothingProc},
652 {N_("Tags"), "Show Tags", EditTagsProc},
653 {N_("Comments"), "Show Comments", EditCommentProc},
654 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
655 {"----", NULL, NothingProc},
656 {N_("Board..."), "Board Options", BoardOptionsProc},
657 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
661 MenuItem modeMenu[] = {
662 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
663 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
664 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
665 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
666 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
667 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
668 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
669 {N_("Training"), "Training", TrainingProc},
670 {N_("ICS Client"), "ICS Client", IcsClientProc},
671 {"----", NULL, NothingProc},
672 {N_("Machine Match"), "Machine Match", MatchProc},
673 {N_("Pause Pause"), "Pause", PauseProc},
677 MenuItem actionMenu[] = {
678 {N_("Accept F3"), "Accept", AcceptProc},
679 {N_("Decline F4"), "Decline", DeclineProc},
680 {N_("Rematch F12"), "Rematch", RematchProc},
681 {"----", NULL, NothingProc},
682 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
683 {N_("Draw F6"), "Draw", DrawProc},
684 {N_("Adjourn F7"), "Adjourn", AdjournProc},
685 {N_("Abort F8"),"Abort", AbortProc},
686 {N_("Resign F9"), "Resign", ResignProc},
687 {"----", NULL, NothingProc},
688 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
689 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
690 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
691 {"----", NULL, NothingProc},
692 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
693 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
694 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
698 MenuItem engineMenu[] = {
699 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
700 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
701 {"----", NULL, NothingProc},
702 {N_("Hint"), "Hint", HintProc},
703 {N_("Book"), "Book", BookProc},
704 {"----", NULL, NothingProc},
705 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
706 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
710 MenuItem optionsMenu[] = {
711 #define OPTIONSDIALOG
713 {N_("General ..."), "General", OptionsProc},
715 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
716 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
717 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
718 {N_("ICS ..."), "ICS", IcsOptionsProc},
719 {N_("Match ..."), "Match", MatchOptionsProc},
720 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
721 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
722 // {N_(" ..."), "", OptionsProc},
723 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
724 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
725 {"----", NULL, NothingProc},
726 #ifndef OPTIONSDIALOG
727 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
728 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
729 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
730 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
731 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
732 {N_("Blindfold"), "Blindfold", BlindfoldProc},
733 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
735 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
737 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
738 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
739 {N_("Move Sound"), "Move Sound", MoveSoundProc},
740 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
741 {N_("One-Click Moving"), "OneClick", OneClickProc},
742 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
743 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
744 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
745 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
746 // {N_("Premove"), "Premove", PremoveProc},
747 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
748 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
749 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
750 {"----", NULL, NothingProc},
752 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
753 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
757 MenuItem helpMenu[] = {
758 {N_("Info XBoard"), "Info XBoard", InfoProc},
759 {N_("Man XBoard F1"), "Man XBoard", ManProc},
760 {"----", NULL, NothingProc},
761 {N_("About XBoard"), "About XBoard", AboutProc},
766 {N_("File"), "File", fileMenu},
767 {N_("Edit"), "Edit", editMenu},
768 {N_("View"), "View", viewMenu},
769 {N_("Mode"), "Mode", modeMenu},
770 {N_("Action"), "Action", actionMenu},
771 {N_("Engine"), "Engine", engineMenu},
772 {N_("Options"), "Options", optionsMenu},
773 {N_("Help"), "Help", helpMenu},
777 #define PAUSE_BUTTON "P"
778 MenuItem buttonBar[] = {
779 {"<<", "<<", ToStartProc},
780 {"<", "<", BackwardProc},
781 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
782 {">", ">", ForwardProc},
783 {">>", ">>", ToEndProc},
787 #define PIECE_MENU_SIZE 18
788 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
789 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
790 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
791 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
792 N_("Empty square"), N_("Clear board") },
793 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
794 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
795 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
796 N_("Empty square"), N_("Clear board") }
798 /* must be in same order as PieceMenuStrings! */
799 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
800 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
801 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
802 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
803 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
804 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
805 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
806 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
807 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
810 #define DROP_MENU_SIZE 6
811 String dropMenuStrings[DROP_MENU_SIZE] = {
812 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
814 /* must be in same order as PieceMenuStrings! */
815 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
816 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
817 WhiteRook, WhiteQueen
825 DropMenuEnables dmEnables[] = {
843 { XtNborderWidth, 0 },
844 { XtNdefaultDistance, 0 },
848 { XtNborderWidth, 0 },
849 { XtNresizable, (XtArgVal) True },
853 { XtNborderWidth, 0 },
859 { XtNjustify, (XtArgVal) XtJustifyRight },
860 { XtNlabel, (XtArgVal) "..." },
861 { XtNresizable, (XtArgVal) True },
862 { XtNresize, (XtArgVal) False }
865 Arg messageArgs[] = {
866 { XtNjustify, (XtArgVal) XtJustifyLeft },
867 { XtNlabel, (XtArgVal) "..." },
868 { XtNresizable, (XtArgVal) True },
869 { XtNresize, (XtArgVal) False }
873 { XtNborderWidth, 0 },
874 { XtNjustify, (XtArgVal) XtJustifyLeft }
877 XtResource clientResources[] = {
878 { "flashCount", "flashCount", XtRInt, sizeof(int),
879 XtOffset(AppDataPtr, flashCount), XtRImmediate,
880 (XtPointer) FLASH_COUNT },
883 XrmOptionDescRec shellOptions[] = {
884 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
885 { "-flash", "flashCount", XrmoptionNoArg, "3" },
886 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
889 XtActionsRec boardActions[] = {
890 { "DrawPosition", DrawPositionProc },
891 { "HandleUserMove", HandleUserMove },
892 { "AnimateUserMove", AnimateUserMove },
893 { "HandlePV", HandlePV },
894 { "SelectPV", SelectPV },
895 { "StopPV", StopPV },
896 { "FileNameAction", FileNameAction },
897 { "AskQuestionProc", AskQuestionProc },
898 { "AskQuestionReplyAction", AskQuestionReplyAction },
899 { "PieceMenuPopup", PieceMenuPopup },
900 { "WhiteClock", WhiteClock },
901 { "BlackClock", BlackClock },
902 { "Iconify", Iconify },
903 { "ResetProc", ResetProc },
904 { "NewVariantProc", NewVariantProc },
905 { "LoadGameProc", LoadGameProc },
906 { "LoadNextGameProc", LoadNextGameProc },
907 { "LoadPrevGameProc", LoadPrevGameProc },
908 { "LoadSelectedProc", LoadSelectedProc },
909 { "SetFilterProc", SetFilterProc },
910 { "ReloadGameProc", ReloadGameProc },
911 { "LoadPositionProc", LoadPositionProc },
912 { "LoadNextPositionProc", LoadNextPositionProc },
913 { "LoadPrevPositionProc", LoadPrevPositionProc },
914 { "ReloadPositionProc", ReloadPositionProc },
915 { "CopyPositionProc", CopyPositionProc },
916 { "PastePositionProc", PastePositionProc },
917 { "CopyGameProc", CopyGameProc },
918 { "PasteGameProc", PasteGameProc },
919 { "SaveGameProc", SaveGameProc },
920 { "SavePositionProc", SavePositionProc },
921 { "MailMoveProc", MailMoveProc },
922 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
923 { "QuitProc", QuitProc },
924 { "MachineWhiteProc", MachineWhiteProc },
925 { "MachineBlackProc", MachineBlackProc },
926 { "AnalysisModeProc", AnalyzeModeProc },
927 { "AnalyzeFileProc", AnalyzeFileProc },
928 { "TwoMachinesProc", TwoMachinesProc },
929 { "IcsClientProc", IcsClientProc },
930 { "EditGameProc", EditGameProc },
931 { "EditPositionProc", EditPositionProc },
932 { "TrainingProc", EditPositionProc },
933 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
934 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
935 { "ShowGameListProc", ShowGameListProc },
936 { "ShowMoveListProc", HistoryShowProc},
937 { "EditTagsProc", EditCommentProc },
938 { "EditCommentProc", EditCommentProc },
939 { "IcsInputBoxProc", IcsInputBoxProc },
940 { "PauseProc", PauseProc },
941 { "AcceptProc", AcceptProc },
942 { "DeclineProc", DeclineProc },
943 { "RematchProc", RematchProc },
944 { "CallFlagProc", CallFlagProc },
945 { "DrawProc", DrawProc },
946 { "AdjournProc", AdjournProc },
947 { "AbortProc", AbortProc },
948 { "ResignProc", ResignProc },
949 { "AdjuWhiteProc", AdjuWhiteProc },
950 { "AdjuBlackProc", AdjuBlackProc },
951 { "AdjuDrawProc", AdjuDrawProc },
952 { "EnterKeyProc", EnterKeyProc },
953 { "UpKeyProc", UpKeyProc },
954 { "DownKeyProc", DownKeyProc },
955 { "StopObservingProc", StopObservingProc },
956 { "StopExaminingProc", StopExaminingProc },
957 { "UploadProc", UploadProc },
958 { "BackwardProc", BackwardProc },
959 { "ForwardProc", ForwardProc },
960 { "ToStartProc", ToStartProc },
961 { "ToEndProc", ToEndProc },
962 { "RevertProc", RevertProc },
963 { "AnnotateProc", AnnotateProc },
964 { "TruncateGameProc", TruncateGameProc },
965 { "MoveNowProc", MoveNowProc },
966 { "RetractMoveProc", RetractMoveProc },
967 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
968 { "UciMenuProc", (XtActionProc) UciMenuProc },
969 { "TimeControlProc", (XtActionProc) TimeControlProc },
970 { "FlipViewProc", FlipViewProc },
971 { "PonderNextMoveProc", PonderNextMoveProc },
972 #ifndef OPTIONSDIALOG
973 { "AlwaysQueenProc", AlwaysQueenProc },
974 { "AnimateDraggingProc", AnimateDraggingProc },
975 { "AnimateMovingProc", AnimateMovingProc },
976 { "AutoflagProc", AutoflagProc },
977 { "AutoflipProc", AutoflipProc },
978 { "BlindfoldProc", BlindfoldProc },
979 { "FlashMovesProc", FlashMovesProc },
981 { "HighlightDraggingProc", HighlightDraggingProc },
983 { "HighlightLastMoveProc", HighlightLastMoveProc },
984 // { "IcsAlarmProc", IcsAlarmProc },
985 { "MoveSoundProc", MoveSoundProc },
986 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
987 { "PopupExitMessageProc", PopupExitMessageProc },
988 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
989 // { "PremoveProc", PremoveProc },
990 { "ShowCoordsProc", ShowCoordsProc },
991 { "ShowThinkingProc", ShowThinkingProc },
992 { "HideThinkingProc", HideThinkingProc },
993 { "TestLegalityProc", TestLegalityProc },
995 { "SaveSettingsProc", SaveSettingsProc },
996 { "SaveOnExitProc", SaveOnExitProc },
997 { "InfoProc", InfoProc },
998 { "ManProc", ManProc },
999 { "HintProc", HintProc },
1000 { "BookProc", BookProc },
1001 { "AboutGameProc", AboutGameProc },
1002 { "AboutProc", AboutProc },
1003 { "DebugProc", DebugProc },
1004 { "NothingProc", NothingProc },
1005 { "CommentClick", (XtActionProc) CommentClick },
1006 { "CommentPopDown", (XtActionProc) CommentPopDown },
1007 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
1008 { "TagsPopDown", (XtActionProc) TagsPopDown },
1009 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1010 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1011 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1012 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1013 { "GameListPopDown", (XtActionProc) GameListPopDown },
1014 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1015 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1016 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1017 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1018 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1019 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1020 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1021 { "GenericPopDown", (XtActionProc) GenericPopDown },
1022 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1025 char globalTranslations[] =
1026 ":<Key>F9: ResignProc() \n \
1027 :Ctrl<Key>n: ResetProc() \n \
1028 :Meta<Key>V: NewVariantProc() \n \
1029 :Ctrl<Key>o: LoadGameProc() \n \
1030 :Meta<Key>Next: LoadNextGameProc() \n \
1031 :Meta<Key>Prior: LoadPrevGameProc() \n \
1032 :Ctrl<Key>s: SaveGameProc() \n \
1033 :Ctrl<Key>c: CopyGameProc() \n \
1034 :Ctrl<Key>v: PasteGameProc() \n \
1035 :Ctrl<Key>O: LoadPositionProc() \n \
1036 :Shift<Key>Next: LoadNextPositionProc() \n \
1037 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1038 :Ctrl<Key>S: SavePositionProc() \n \
1039 :Ctrl<Key>C: CopyPositionProc() \n \
1040 :Ctrl<Key>V: PastePositionProc() \n \
1041 :Ctrl<Key>q: QuitProc() \n \
1042 :Ctrl<Key>w: MachineWhiteProc() \n \
1043 :Ctrl<Key>b: MachineBlackProc() \n \
1044 :Ctrl<Key>t: TwoMachinesProc() \n \
1045 :Ctrl<Key>a: AnalysisModeProc() \n \
1046 :Ctrl<Key>f: AnalyzeFileProc() \n \
1047 :Ctrl<Key>e: EditGameProc() \n \
1048 :Ctrl<Key>E: EditPositionProc() \n \
1049 :Meta<Key>O: EngineOutputProc() \n \
1050 :Meta<Key>E: EvalGraphProc() \n \
1051 :Meta<Key>G: ShowGameListProc() \n \
1052 :Meta<Key>H: ShowMoveListProc() \n \
1053 :<Key>Pause: PauseProc() \n \
1054 :<Key>F3: AcceptProc() \n \
1055 :<Key>F4: DeclineProc() \n \
1056 :<Key>F12: RematchProc() \n \
1057 :<Key>F5: CallFlagProc() \n \
1058 :<Key>F6: DrawProc() \n \
1059 :<Key>F7: AdjournProc() \n \
1060 :<Key>F8: AbortProc() \n \
1061 :<Key>F10: StopObservingProc() \n \
1062 :<Key>F11: StopExaminingProc() \n \
1063 :Meta Ctrl<Key>F12: DebugProc() \n \
1064 :Meta<Key>End: ToEndProc() \n \
1065 :Meta<Key>Right: ForwardProc() \n \
1066 :Meta<Key>Home: ToStartProc() \n \
1067 :Meta<Key>Left: BackwardProc() \n \
1068 :<Key>Home: RevertProc() \n \
1069 :<Key>End: TruncateGameProc() \n \
1070 :Ctrl<Key>m: MoveNowProc() \n \
1071 :Ctrl<Key>x: RetractMoveProc() \n \
1072 :Meta<Key>J: EngineMenuProc() \n \
1073 :Meta<Key>U: UciMenuProc() \n \
1074 :Meta<Key>T: TimeControlProc() \n \
1075 :Ctrl<Key>P: PonderNextMoveProc() \n "
1076 #ifndef OPTIONSDIALOG
1078 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1079 :Ctrl<Key>F: AutoflagProc() \n \
1080 :Ctrl<Key>A: AnimateMovingProc() \n \
1081 :Ctrl<Key>L: TestLegalityProc() \n \
1082 :Ctrl<Key>H: HideThinkingProc() \n "
1085 :<Key>-: Iconify() \n \
1086 :<Key>F1: ManProc() \n \
1087 :<Key>F2: FlipViewProc() \n \
1088 <KeyDown>.: BackwardProc() \n \
1089 <KeyUp>.: ForwardProc() \n \
1090 Shift<Key>1: AskQuestionProc(\"Direct command\",\
1091 \"Send to chess program:\",,1) \n \
1092 Shift<Key>2: AskQuestionProc(\"Direct command\",\
1093 \"Send to second chess program:\",,2) \n";
1095 char boardTranslations[] =
1096 "<Btn1Down>: HandleUserMove(0) \n \
1097 Shift<Btn1Up>: HandleUserMove(1) \n \
1098 <Btn1Up>: HandleUserMove(0) \n \
1099 <Btn1Motion>: AnimateUserMove() \n \
1100 <Btn3Motion>: HandlePV() \n \
1101 <Btn3Up>: PieceMenuPopup(menuB) \n \
1102 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1103 PieceMenuPopup(menuB) \n \
1104 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1105 PieceMenuPopup(menuW) \n \
1106 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1107 PieceMenuPopup(menuW) \n \
1108 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1109 PieceMenuPopup(menuB) \n";
1111 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1112 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1114 char ICSInputTranslations[] =
1115 "<Key>Up: UpKeyProc() \n "
1116 "<Key>Down: DownKeyProc() \n "
1117 "<Key>Return: EnterKeyProc() \n";
1119 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1120 // as the widget is destroyed before the up-click can call extend-end
1121 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1123 String xboardResources[] = {
1124 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1125 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1126 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1131 /* Max possible square size */
1132 #define MAXSQSIZE 256
1134 static int xpm_avail[MAXSQSIZE];
1136 #ifdef HAVE_DIR_STRUCT
1138 /* Extract piece size from filename */
1140 xpm_getsize(name, len, ext)
1151 if ((p=strchr(name, '.')) == NULL ||
1152 StrCaseCmp(p+1, ext) != 0)
1158 while (*p && isdigit(*p))
1165 /* Setup xpm_avail */
1167 xpm_getavail(dirname, ext)
1175 for (i=0; i<MAXSQSIZE; ++i)
1178 if (appData.debugMode)
1179 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1181 dir = opendir(dirname);
1184 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1185 programName, dirname);
1189 while ((ent=readdir(dir)) != NULL) {
1190 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1191 if (i > 0 && i < MAXSQSIZE)
1201 xpm_print_avail(fp, ext)
1207 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1208 for (i=1; i<MAXSQSIZE; ++i) {
1214 /* Return XPM piecesize closest to size */
1216 xpm_closest_to(dirname, size, ext)
1222 int sm_diff = MAXSQSIZE;
1226 xpm_getavail(dirname, ext);
1228 if (appData.debugMode)
1229 xpm_print_avail(stderr, ext);
1231 for (i=1; i<MAXSQSIZE; ++i) {
1234 diff = (diff<0) ? -diff : diff;
1235 if (diff < sm_diff) {
1243 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1249 #else /* !HAVE_DIR_STRUCT */
1250 /* If we are on a system without a DIR struct, we can't
1251 read the directory, so we can't collect a list of
1252 filenames, etc., so we can't do any size-fitting. */
1254 xpm_closest_to(dirname, size, ext)
1259 fprintf(stderr, _("\
1260 Warning: No DIR structure found on this system --\n\
1261 Unable to autosize for XPM/XIM pieces.\n\
1262 Please report this error to frankm@hiwaay.net.\n\
1263 Include system type & operating system in message.\n"));
1266 #endif /* HAVE_DIR_STRUCT */
1268 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1269 "magenta", "cyan", "white" };
1273 TextColors textColors[(int)NColorClasses];
1275 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1277 parse_color(str, which)
1281 char *p, buf[100], *d;
1284 if (strlen(str) > 99) /* watch bounds on buf */
1289 for (i=0; i<which; ++i) {
1296 /* Could be looking at something like:
1298 .. in which case we want to stop on a comma also */
1299 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1303 return -1; /* Use default for empty field */
1306 if (which == 2 || isdigit(*p))
1309 while (*p && isalpha(*p))
1314 for (i=0; i<8; ++i) {
1315 if (!StrCaseCmp(buf, cnames[i]))
1316 return which? (i+40) : (i+30);
1318 if (!StrCaseCmp(buf, "default")) return -1;
1320 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1325 parse_cpair(cc, str)
1329 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1330 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1335 /* bg and attr are optional */
1336 textColors[(int)cc].bg = parse_color(str, 1);
1337 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1338 textColors[(int)cc].attr = 0;
1344 /* Arrange to catch delete-window events */
1345 Atom wm_delete_window;
1347 CatchDeleteWindow(Widget w, String procname)
1350 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1351 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1352 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1359 XtSetArg(args[0], XtNiconic, False);
1360 XtSetValues(shellWidget, args, 1);
1362 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1365 //---------------------------------------------------------------------------------------------------------
1366 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1369 #define CW_USEDEFAULT (1<<31)
1370 #define ICS_TEXT_MENU_SIZE 90
1371 #define DEBUG_FILE "xboard.debug"
1372 #define SetCurrentDirectory chdir
1373 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1377 // these two must some day move to frontend.h, when they are implemented
1378 Boolean GameListIsUp();
1380 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1383 // front-end part of option handling
1385 // [HGM] This platform-dependent table provides the location for storing the color info
1386 extern char *crWhite, * crBlack;
1390 &appData.whitePieceColor,
1391 &appData.blackPieceColor,
1392 &appData.lightSquareColor,
1393 &appData.darkSquareColor,
1394 &appData.highlightSquareColor,
1395 &appData.premoveHighlightColor,
1396 &appData.lowTimeWarningColor,
1407 // [HGM] font: keep a font for each square size, even non-stndard ones
1408 #define NUM_SIZES 18
1409 #define MAX_SIZE 130
1410 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1411 char *fontTable[NUM_FONTS][MAX_SIZE];
1414 ParseFont(char *name, int number)
1415 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1417 if(sscanf(name, "size%d:", &size)) {
1418 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1419 // defer processing it until we know if it matches our board size
1420 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1421 fontTable[number][size] = strdup(strchr(name, ':')+1);
1422 fontValid[number][size] = True;
1427 case 0: // CLOCK_FONT
1428 appData.clockFont = strdup(name);
1430 case 1: // MESSAGE_FONT
1431 appData.font = strdup(name);
1433 case 2: // COORD_FONT
1434 appData.coordFont = strdup(name);
1439 fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1444 { // only 2 fonts currently
1445 appData.clockFont = CLOCK_FONT_NAME;
1446 appData.coordFont = COORD_FONT_NAME;
1447 appData.font = DEFAULT_FONT_NAME;
1452 { // no-op, until we identify the code for this already in XBoard and move it here
1456 ParseColor(int n, char *name)
1457 { // in XBoard, just copy the color-name string
1458 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1462 ParseTextAttribs(ColorClass cc, char *s)
1464 (&appData.colorShout)[cc] = strdup(s);
1468 ParseBoardSize(void *addr, char *name)
1470 appData.boardSize = strdup(name);
1475 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1479 SetCommPortDefaults()
1480 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1483 // [HGM] args: these three cases taken out to stay in front-end
1485 SaveFontArg(FILE *f, ArgDescriptor *ad)
1488 int i, n = (int)(intptr_t)ad->argLoc;
1490 case 0: // CLOCK_FONT
1491 name = appData.clockFont;
1493 case 1: // MESSAGE_FONT
1494 name = appData.font;
1496 case 2: // COORD_FONT
1497 name = appData.coordFont;
1502 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1503 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1504 fontTable[n][squareSize] = strdup(name);
1505 fontValid[n][squareSize] = True;
1508 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1509 fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
1514 { // nothing to do, as the sounds are at all times represented by their text-string names already
1518 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1519 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1520 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1524 SaveColor(FILE *f, ArgDescriptor *ad)
1525 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1526 if(colorVariable[(int)(intptr_t)ad->argLoc])
1527 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1531 SaveBoardSize(FILE *f, char *name, void *addr)
1532 { // wrapper to shield back-end from BoardSize & sizeInfo
1533 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1537 ParseCommPortSettings(char *s)
1538 { // no such option in XBoard (yet)
1541 extern Widget engineOutputShell;
1542 extern Widget tagsShell, editTagsShell;
1544 GetActualPlacement(Widget wg, WindowPlacement *wp)
1554 XtSetArg(args[i], XtNx, &x); i++;
1555 XtSetArg(args[i], XtNy, &y); i++;
1556 XtSetArg(args[i], XtNwidth, &w); i++;
1557 XtSetArg(args[i], XtNheight, &h); i++;
1558 XtGetValues(wg, args, i);
1567 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1568 // In XBoard this will have to wait until awareness of window parameters is implemented
1569 GetActualPlacement(shellWidget, &wpMain);
1570 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1571 if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1572 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1573 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1574 if(commentShell) GetActualPlacement(commentShell, &wpComment);
1575 else GetActualPlacement(editShell, &wpComment);
1576 if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1577 else GetActualPlacement(editTagsShell, &wpTags);
1581 PrintCommPortSettings(FILE *f, char *name)
1582 { // This option does not exist in XBoard
1586 MySearchPath(char *installDir, char *name, char *fullname)
1587 { // just append installDir and name. Perhaps ExpandPath should be used here?
1588 name = ExpandPathName(name);
1589 if(name && name[0] == '/')
1590 safeStrCpy(fullname, name, MSG_SIZ );
1592 sprintf(fullname, "%s%c%s", installDir, '/', name);
1598 MyGetFullPathName(char *name, char *fullname)
1599 { // should use ExpandPath?
1600 name = ExpandPathName(name);
1601 safeStrCpy(fullname, name, MSG_SIZ );
1606 EnsureOnScreen(int *x, int *y, int minX, int minY)
1613 { // [HGM] args: allows testing if main window is realized from back-end
1614 return xBoardWindow != 0;
1618 PopUpStartupDialog()
1619 { // start menu not implemented in XBoard
1623 ConvertToLine(int argc, char **argv)
1625 static char line[128*1024], buf[1024];
1629 for(i=1; i<argc; i++)
1631 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1632 && argv[i][0] != '{' )
1633 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1635 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1636 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1639 line[strlen(line)-1] = NULLCHAR;
1643 //--------------------------------------------------------------------------------------------
1645 extern Boolean twoBoards, partnerUp;
1648 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1650 #define BoardSize int
1651 void InitDrawingSizes(BoardSize boardSize, int flags)
1652 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1653 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1655 XtGeometryResult gres;
1658 if(!formWidget) return;
1661 * Enable shell resizing.
1663 shellArgs[0].value = (XtArgVal) &w;
1664 shellArgs[1].value = (XtArgVal) &h;
1665 XtGetValues(shellWidget, shellArgs, 2);
1667 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1668 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1669 XtSetValues(shellWidget, &shellArgs[2], 4);
1671 XtSetArg(args[0], XtNdefaultDistance, &sep);
1672 XtGetValues(formWidget, args, 1);
1674 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1675 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1676 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1678 hOffset = boardWidth + 10;
1679 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1680 secondSegments[i] = gridSegments[i];
1681 secondSegments[i].x1 += hOffset;
1682 secondSegments[i].x2 += hOffset;
1685 XtSetArg(args[0], XtNwidth, boardWidth);
1686 XtSetArg(args[1], XtNheight, boardHeight);
1687 XtSetValues(boardWidget, args, 2);
1689 timerWidth = (boardWidth - sep) / 2;
1690 XtSetArg(args[0], XtNwidth, timerWidth);
1691 XtSetValues(whiteTimerWidget, args, 1);
1692 XtSetValues(blackTimerWidget, args, 1);
1694 XawFormDoLayout(formWidget, False);
1696 if (appData.titleInWindow) {
1698 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1699 XtSetArg(args[i], XtNheight, &h); i++;
1700 XtGetValues(titleWidget, args, i);
1702 w = boardWidth - 2*bor;
1704 XtSetArg(args[0], XtNwidth, &w);
1705 XtGetValues(menuBarWidget, args, 1);
1706 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1709 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1710 if (gres != XtGeometryYes && appData.debugMode) {
1712 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1713 programName, gres, w, h, wr, hr);
1717 XawFormDoLayout(formWidget, True);
1720 * Inhibit shell resizing.
1722 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1723 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1724 shellArgs[4].value = shellArgs[2].value = w;
1725 shellArgs[5].value = shellArgs[3].value = h;
1726 XtSetValues(shellWidget, &shellArgs[0], 6);
1728 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1731 for(i=0; i<4; i++) {
1733 for(p=0; p<=(int)WhiteKing; p++)
1734 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1735 if(gameInfo.variant == VariantShogi) {
1736 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1737 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1738 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1739 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1740 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1743 if(gameInfo.variant == VariantGothic) {
1744 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1747 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1748 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1749 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1752 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1753 for(p=0; p<=(int)WhiteKing; p++)
1754 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1755 if(gameInfo.variant == VariantShogi) {
1756 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1757 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1758 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1759 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1760 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1763 if(gameInfo.variant == VariantGothic) {
1764 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1767 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1768 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1769 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1774 for(i=0; i<2; i++) {
1776 for(p=0; p<=(int)WhiteKing; p++)
1777 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1778 if(gameInfo.variant == VariantShogi) {
1779 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1780 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1781 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1782 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1783 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1786 if(gameInfo.variant == VariantGothic) {
1787 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1790 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1791 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1792 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1802 void ParseIcsTextColors()
1803 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1804 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1805 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1806 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1807 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1808 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1809 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1810 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1811 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1812 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1813 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1815 if (appData.colorize) {
1817 _("%s: can't parse color names; disabling colorization\n"),
1820 appData.colorize = FALSE;
1825 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1826 XrmValue vFrom, vTo;
1827 int forceMono = False;
1829 if (!appData.monoMode) {
1830 vFrom.addr = (caddr_t) appData.lightSquareColor;
1831 vFrom.size = strlen(appData.lightSquareColor);
1832 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1833 if (vTo.addr == NULL) {
1834 appData.monoMode = True;
1837 lightSquareColor = *(Pixel *) vTo.addr;
1840 if (!appData.monoMode) {
1841 vFrom.addr = (caddr_t) appData.darkSquareColor;
1842 vFrom.size = strlen(appData.darkSquareColor);
1843 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1844 if (vTo.addr == NULL) {
1845 appData.monoMode = True;
1848 darkSquareColor = *(Pixel *) vTo.addr;
1851 if (!appData.monoMode) {
1852 vFrom.addr = (caddr_t) appData.whitePieceColor;
1853 vFrom.size = strlen(appData.whitePieceColor);
1854 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1855 if (vTo.addr == NULL) {
1856 appData.monoMode = True;
1859 whitePieceColor = *(Pixel *) vTo.addr;
1862 if (!appData.monoMode) {
1863 vFrom.addr = (caddr_t) appData.blackPieceColor;
1864 vFrom.size = strlen(appData.blackPieceColor);
1865 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1866 if (vTo.addr == NULL) {
1867 appData.monoMode = True;
1870 blackPieceColor = *(Pixel *) vTo.addr;
1874 if (!appData.monoMode) {
1875 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1876 vFrom.size = strlen(appData.highlightSquareColor);
1877 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1878 if (vTo.addr == NULL) {
1879 appData.monoMode = True;
1882 highlightSquareColor = *(Pixel *) vTo.addr;
1886 if (!appData.monoMode) {
1887 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1888 vFrom.size = strlen(appData.premoveHighlightColor);
1889 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1890 if (vTo.addr == NULL) {
1891 appData.monoMode = True;
1894 premoveHighlightColor = *(Pixel *) vTo.addr;
1905 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1906 XSetWindowAttributes window_attributes;
1908 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1909 XrmValue vFrom, vTo;
1910 XtGeometryResult gres;
1913 int forceMono = False;
1915 srandom(time(0)); // [HGM] book: make random truly random
1917 setbuf(stdout, NULL);
1918 setbuf(stderr, NULL);
1921 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1922 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1926 programName = strrchr(argv[0], '/');
1927 if (programName == NULL)
1928 programName = argv[0];
1933 XtSetLanguageProc(NULL, NULL, NULL);
1934 bindtextdomain(PACKAGE, LOCALEDIR);
1935 textdomain(PACKAGE);
1939 XtAppInitialize(&appContext, "XBoard", shellOptions,
1940 XtNumber(shellOptions),
1941 &argc, argv, xboardResources, NULL, 0);
1942 appData.boardSize = "";
1943 InitAppData(ConvertToLine(argc, argv));
1945 if (p == NULL) p = "/tmp";
1946 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1947 gameCopyFilename = (char*) malloc(i);
1948 gamePasteFilename = (char*) malloc(i);
1949 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1950 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1952 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1953 clientResources, XtNumber(clientResources),
1956 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1957 static char buf[MSG_SIZ];
1958 EscapeExpand(buf, appData.initString);
1959 appData.initString = strdup(buf);
1960 EscapeExpand(buf, appData.secondInitString);
1961 appData.secondInitString = strdup(buf);
1962 EscapeExpand(buf, appData.firstComputerString);
1963 appData.firstComputerString = strdup(buf);
1964 EscapeExpand(buf, appData.secondComputerString);
1965 appData.secondComputerString = strdup(buf);
1968 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1971 if (chdir(chessDir) != 0) {
1972 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1978 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1979 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1980 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
1981 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1984 setbuf(debugFP, NULL);
1987 /* [HGM,HR] make sure board size is acceptable */
1988 if(appData.NrFiles > BOARD_FILES ||
1989 appData.NrRanks > BOARD_RANKS )
1990 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1993 /* This feature does not work; animation needs a rewrite */
1994 appData.highlightDragging = FALSE;
1998 xDisplay = XtDisplay(shellWidget);
1999 xScreen = DefaultScreen(xDisplay);
2000 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2002 gameInfo.variant = StringToVariant(appData.variant);
2003 InitPosition(FALSE);
2006 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2008 if (isdigit(appData.boardSize[0])) {
2009 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2010 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2011 &fontPxlSize, &smallLayout, &tinyLayout);
2013 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2014 programName, appData.boardSize);
2018 /* Find some defaults; use the nearest known size */
2019 SizeDefaults *szd, *nearest;
2020 int distance = 99999;
2021 nearest = szd = sizeDefaults;
2022 while (szd->name != NULL) {
2023 if (abs(szd->squareSize - squareSize) < distance) {
2025 distance = abs(szd->squareSize - squareSize);
2026 if (distance == 0) break;
2030 if (i < 2) lineGap = nearest->lineGap;
2031 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2032 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2033 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2034 if (i < 6) smallLayout = nearest->smallLayout;
2035 if (i < 7) tinyLayout = nearest->tinyLayout;
2038 SizeDefaults *szd = sizeDefaults;
2039 if (*appData.boardSize == NULLCHAR) {
2040 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2041 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2044 if (szd->name == NULL) szd--;
2045 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2047 while (szd->name != NULL &&
2048 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2049 if (szd->name == NULL) {
2050 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2051 programName, appData.boardSize);
2055 squareSize = szd->squareSize;
2056 lineGap = szd->lineGap;
2057 clockFontPxlSize = szd->clockFontPxlSize;
2058 coordFontPxlSize = szd->coordFontPxlSize;
2059 fontPxlSize = szd->fontPxlSize;
2060 smallLayout = szd->smallLayout;
2061 tinyLayout = szd->tinyLayout;
2062 // [HGM] font: use defaults from settings file if available and not overruled
2064 if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2065 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2066 if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2067 appData.font = fontTable[MESSAGE_FONT][squareSize];
2068 if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2069 appData.coordFont = fontTable[COORD_FONT][squareSize];
2071 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2072 if (strlen(appData.pixmapDirectory) > 0) {
2073 p = ExpandPathName(appData.pixmapDirectory);
2075 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2076 appData.pixmapDirectory);
2079 if (appData.debugMode) {
2080 fprintf(stderr, _("\
2081 XBoard square size (hint): %d\n\
2082 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2084 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2085 if (appData.debugMode) {
2086 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2089 defaultLineGap = lineGap;
2090 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2092 /* [HR] height treated separately (hacked) */
2093 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2094 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2095 if (appData.showJail == 1) {
2096 /* Jail on top and bottom */
2097 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2098 XtSetArg(boardArgs[2], XtNheight,
2099 boardHeight + 2*(lineGap + squareSize));
2100 } else if (appData.showJail == 2) {
2102 XtSetArg(boardArgs[1], XtNwidth,
2103 boardWidth + 2*(lineGap + squareSize));
2104 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2107 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2108 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2112 * Determine what fonts to use.
2114 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2115 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2116 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2117 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2118 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2119 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2120 appData.font = FindFont(appData.font, fontPxlSize);
2121 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2122 countFontStruct = XQueryFont(xDisplay, countFontID);
2123 // appData.font = FindFont(appData.font, fontPxlSize);
2125 xdb = XtDatabase(xDisplay);
2126 XrmPutStringResource(&xdb, "*font", appData.font);
2129 * Detect if there are not enough colors available and adapt.
2131 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2132 appData.monoMode = True;
2135 forceMono = MakeColors();
2138 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2141 if (appData.bitmapDirectory == NULL ||
2142 appData.bitmapDirectory[0] == NULLCHAR)
2143 appData.bitmapDirectory = DEF_BITMAP_DIR;
2146 if (appData.lowTimeWarning && !appData.monoMode) {
2147 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2148 vFrom.size = strlen(appData.lowTimeWarningColor);
2149 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2150 if (vTo.addr == NULL)
2151 appData.monoMode = True;
2153 lowTimeWarningColor = *(Pixel *) vTo.addr;
2156 if (appData.monoMode && appData.debugMode) {
2157 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2158 (unsigned long) XWhitePixel(xDisplay, xScreen),
2159 (unsigned long) XBlackPixel(xDisplay, xScreen));
2162 ParseIcsTextColors();
2163 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2164 textColors[ColorNone].attr = 0;
2166 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2172 layoutName = "tinyLayout";
2173 } else if (smallLayout) {
2174 layoutName = "smallLayout";
2176 layoutName = "normalLayout";
2178 /* Outer layoutWidget is there only to provide a name for use in
2179 resources that depend on the layout style */
2181 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2182 layoutArgs, XtNumber(layoutArgs));
2184 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2185 formArgs, XtNumber(formArgs));
2186 XtSetArg(args[0], XtNdefaultDistance, &sep);
2187 XtGetValues(formWidget, args, 1);
2190 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2191 XtSetArg(args[0], XtNtop, XtChainTop);
2192 XtSetArg(args[1], XtNbottom, XtChainTop);
2193 XtSetArg(args[2], XtNright, XtChainLeft);
2194 XtSetValues(menuBarWidget, args, 3);
2196 widgetList[j++] = whiteTimerWidget =
2197 XtCreateWidget("whiteTime", labelWidgetClass,
2198 formWidget, timerArgs, XtNumber(timerArgs));
2199 XtSetArg(args[0], XtNfont, clockFontStruct);
2200 XtSetArg(args[1], XtNtop, XtChainTop);
2201 XtSetArg(args[2], XtNbottom, XtChainTop);
2202 XtSetValues(whiteTimerWidget, args, 3);
2204 widgetList[j++] = blackTimerWidget =
2205 XtCreateWidget("blackTime", labelWidgetClass,
2206 formWidget, timerArgs, XtNumber(timerArgs));
2207 XtSetArg(args[0], XtNfont, clockFontStruct);
2208 XtSetArg(args[1], XtNtop, XtChainTop);
2209 XtSetArg(args[2], XtNbottom, XtChainTop);
2210 XtSetValues(blackTimerWidget, args, 3);
2212 if (appData.titleInWindow) {
2213 widgetList[j++] = titleWidget =
2214 XtCreateWidget("title", labelWidgetClass, formWidget,
2215 titleArgs, XtNumber(titleArgs));
2216 XtSetArg(args[0], XtNtop, XtChainTop);
2217 XtSetArg(args[1], XtNbottom, XtChainTop);
2218 XtSetValues(titleWidget, args, 2);
2221 if (appData.showButtonBar) {
2222 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2223 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2224 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2225 XtSetArg(args[2], XtNtop, XtChainTop);
2226 XtSetArg(args[3], XtNbottom, XtChainTop);
2227 XtSetValues(buttonBarWidget, args, 4);
2230 widgetList[j++] = messageWidget =
2231 XtCreateWidget("message", labelWidgetClass, formWidget,
2232 messageArgs, XtNumber(messageArgs));
2233 XtSetArg(args[0], XtNtop, XtChainTop);
2234 XtSetArg(args[1], XtNbottom, XtChainTop);
2235 XtSetValues(messageWidget, args, 2);
2237 widgetList[j++] = boardWidget =
2238 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2239 XtNumber(boardArgs));
2241 XtManageChildren(widgetList, j);
2243 timerWidth = (boardWidth - sep) / 2;
2244 XtSetArg(args[0], XtNwidth, timerWidth);
2245 XtSetValues(whiteTimerWidget, args, 1);
2246 XtSetValues(blackTimerWidget, args, 1);
2248 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2249 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2250 XtGetValues(whiteTimerWidget, args, 2);
2252 if (appData.showButtonBar) {
2253 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2254 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2255 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2259 * formWidget uses these constraints but they are stored
2263 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2264 XtSetValues(menuBarWidget, args, i);
2265 if (appData.titleInWindow) {
2268 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2269 XtSetValues(whiteTimerWidget, args, i);
2271 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2272 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2273 XtSetValues(blackTimerWidget, args, i);
2275 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2276 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2277 XtSetValues(titleWidget, args, i);
2279 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2280 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2281 XtSetValues(messageWidget, args, i);
2282 if (appData.showButtonBar) {
2284 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2285 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2286 XtSetValues(buttonBarWidget, args, i);
2290 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2291 XtSetValues(whiteTimerWidget, args, i);
2293 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2294 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2295 XtSetValues(blackTimerWidget, args, i);
2297 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2298 XtSetValues(titleWidget, args, i);
2300 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2301 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2302 XtSetValues(messageWidget, args, i);
2303 if (appData.showButtonBar) {
2305 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2306 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2307 XtSetValues(buttonBarWidget, args, i);
2312 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2313 XtSetValues(whiteTimerWidget, args, i);
2315 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2316 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2317 XtSetValues(blackTimerWidget, args, i);
2319 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2320 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2321 XtSetValues(messageWidget, args, i);
2322 if (appData.showButtonBar) {
2324 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2325 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2326 XtSetValues(buttonBarWidget, args, i);
2330 XtSetArg(args[0], XtNfromVert, messageWidget);
2331 XtSetArg(args[1], XtNtop, XtChainTop);
2332 XtSetArg(args[2], XtNbottom, XtChainBottom);
2333 XtSetArg(args[3], XtNleft, XtChainLeft);
2334 XtSetArg(args[4], XtNright, XtChainRight);
2335 XtSetValues(boardWidget, args, 5);
2337 XtRealizeWidget(shellWidget);
2340 XtSetArg(args[0], XtNx, wpMain.x);
2341 XtSetArg(args[1], XtNy, wpMain.y);
2342 XtSetValues(shellWidget, args, 2);
2346 * Correct the width of the message and title widgets.
2347 * It is not known why some systems need the extra fudge term.
2348 * The value "2" is probably larger than needed.
2350 XawFormDoLayout(formWidget, False);
2352 #define WIDTH_FUDGE 2
2354 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2355 XtSetArg(args[i], XtNheight, &h); i++;
2356 XtGetValues(messageWidget, args, i);
2357 if (appData.showButtonBar) {
2359 XtSetArg(args[i], XtNwidth, &w); i++;
2360 XtGetValues(buttonBarWidget, args, i);
2361 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2363 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2366 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2367 if (gres != XtGeometryYes && appData.debugMode) {
2368 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2369 programName, gres, w, h, wr, hr);
2372 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2373 /* The size used for the child widget in layout lags one resize behind
2374 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2376 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2377 if (gres != XtGeometryYes && appData.debugMode) {
2378 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2379 programName, gres, w, h, wr, hr);
2382 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2383 XtSetArg(args[1], XtNright, XtChainRight);
2384 XtSetValues(messageWidget, args, 2);
2386 if (appData.titleInWindow) {
2388 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2389 XtSetArg(args[i], XtNheight, &h); i++;
2390 XtGetValues(titleWidget, args, i);
2392 w = boardWidth - 2*bor;
2394 XtSetArg(args[0], XtNwidth, &w);
2395 XtGetValues(menuBarWidget, args, 1);
2396 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2399 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2400 if (gres != XtGeometryYes && appData.debugMode) {
2402 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2403 programName, gres, w, h, wr, hr);
2406 XawFormDoLayout(formWidget, True);
2408 xBoardWindow = XtWindow(boardWidget);
2410 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2411 // not need to go into InitDrawingSizes().
2415 * Create X checkmark bitmap and initialize option menu checks.
2417 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2418 checkmark_bits, checkmark_width, checkmark_height);
2419 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2420 #ifndef OPTIONSDIALOG
2421 if (appData.alwaysPromoteToQueen) {
2422 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2425 if (appData.animateDragging) {
2426 XtSetValues(XtNameToWidget(menuBarWidget,
2427 "menuOptions.Animate Dragging"),
2430 if (appData.animate) {
2431 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2434 if (appData.autoCallFlag) {
2435 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2438 if (appData.autoFlipView) {
2439 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2442 if (appData.blindfold) {
2443 XtSetValues(XtNameToWidget(menuBarWidget,
2444 "menuOptions.Blindfold"), args, 1);
2446 if (appData.flashCount > 0) {
2447 XtSetValues(XtNameToWidget(menuBarWidget,
2448 "menuOptions.Flash Moves"),
2452 if (appData.highlightDragging) {
2453 XtSetValues(XtNameToWidget(menuBarWidget,
2454 "menuOptions.Highlight Dragging"),
2458 if (appData.highlightLastMove) {
2459 XtSetValues(XtNameToWidget(menuBarWidget,
2460 "menuOptions.Highlight Last Move"),
2463 if (appData.highlightMoveWithArrow) {
2464 XtSetValues(XtNameToWidget(menuBarWidget,
2465 "menuOptions.Arrow"),
2468 // if (appData.icsAlarm) {
2469 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2472 if (appData.ringBellAfterMoves) {
2473 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2476 if (appData.oneClick) {
2477 XtSetValues(XtNameToWidget(menuBarWidget,
2478 "menuOptions.OneClick"), args, 1);
2480 if (appData.periodicUpdates) {
2481 XtSetValues(XtNameToWidget(menuBarWidget,
2482 "menuOptions.Periodic Updates"), args, 1);
2484 if (appData.ponderNextMove) {
2485 XtSetValues(XtNameToWidget(menuBarWidget,
2486 "menuOptions.Ponder Next Move"), args, 1);
2488 if (appData.popupExitMessage) {
2489 XtSetValues(XtNameToWidget(menuBarWidget,
2490 "menuOptions.Popup Exit Message"), args, 1);
2492 if (appData.popupMoveErrors) {
2493 XtSetValues(XtNameToWidget(menuBarWidget,
2494 "menuOptions.Popup Move Errors"), args, 1);
2496 // if (appData.premove) {
2497 // XtSetValues(XtNameToWidget(menuBarWidget,
2498 // "menuOptions.Premove"), args, 1);
2500 if (appData.showCoords) {
2501 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2504 if (appData.hideThinkingFromHuman) {
2505 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2508 if (appData.testLegality) {
2509 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2513 if (saveSettingsOnExit) {
2514 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2521 ReadBitmap(&wIconPixmap, "icon_white.bm",
2522 icon_white_bits, icon_white_width, icon_white_height);
2523 ReadBitmap(&bIconPixmap, "icon_black.bm",
2524 icon_black_bits, icon_black_width, icon_black_height);
2525 iconPixmap = wIconPixmap;
2527 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2528 XtSetValues(shellWidget, args, i);
2531 * Create a cursor for the board widget.
2533 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2534 XChangeWindowAttributes(xDisplay, xBoardWindow,
2535 CWCursor, &window_attributes);
2538 * Inhibit shell resizing.
2540 shellArgs[0].value = (XtArgVal) &w;
2541 shellArgs[1].value = (XtArgVal) &h;
2542 XtGetValues(shellWidget, shellArgs, 2);
2543 shellArgs[4].value = shellArgs[2].value = w;
2544 shellArgs[5].value = shellArgs[3].value = h;
2545 XtSetValues(shellWidget, &shellArgs[2], 4);
2546 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2547 marginH = h - boardHeight;
2549 CatchDeleteWindow(shellWidget, "QuitProc");
2554 if (appData.bitmapDirectory[0] != NULLCHAR) {
2558 CreateXPMBoard(appData.liteBackTextureFile, 1);
2559 CreateXPMBoard(appData.darkBackTextureFile, 0);
2563 /* Create regular pieces */
2564 if (!useImages) CreatePieces();
2569 if (appData.animate || appData.animateDragging)
2572 XtAugmentTranslations(formWidget,
2573 XtParseTranslationTable(globalTranslations));
2574 XtAugmentTranslations(boardWidget,
2575 XtParseTranslationTable(boardTranslations));
2576 XtAugmentTranslations(whiteTimerWidget,
2577 XtParseTranslationTable(whiteTranslations));
2578 XtAugmentTranslations(blackTimerWidget,
2579 XtParseTranslationTable(blackTranslations));
2581 /* Why is the following needed on some versions of X instead
2582 * of a translation? */
2583 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2584 (XtEventHandler) EventProc, NULL);
2587 /* [AS] Restore layout */
2588 if( wpMoveHistory.visible ) {
2592 if( wpEvalGraph.visible )
2597 if( wpEngineOutput.visible ) {
2598 EngineOutputPopUp();
2603 if (errorExitStatus == -1) {
2604 if (appData.icsActive) {
2605 /* We now wait until we see "login:" from the ICS before
2606 sending the logon script (problems with timestamp otherwise) */
2607 /*ICSInitScript();*/
2608 if (appData.icsInputBox) ICSInputBoxPopUp();
2612 signal(SIGWINCH, TermSizeSigHandler);
2614 signal(SIGINT, IntSigHandler);
2615 signal(SIGTERM, IntSigHandler);
2616 if (*appData.cmailGameName != NULLCHAR) {
2617 signal(SIGUSR1, CmailSigHandler);
2620 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2622 XtSetKeyboardFocus(shellWidget, formWidget);
2624 XtAppMainLoop(appContext);
2625 if (appData.debugMode) fclose(debugFP); // [DM] debug
2632 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2633 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2635 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2636 unlink(gameCopyFilename);
2637 unlink(gamePasteFilename);
2640 RETSIGTYPE TermSizeSigHandler(int sig)
2653 CmailSigHandler(sig)
2659 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2661 /* Activate call-back function CmailSigHandlerCallBack() */
2662 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2664 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2668 CmailSigHandlerCallBack(isr, closure, message, count, error)
2676 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2678 /**** end signal code ****/
2684 /* try to open the icsLogon script, either in the location given
2685 * or in the users HOME directory
2692 f = fopen(appData.icsLogon, "r");
2695 homedir = getenv("HOME");
2696 if (homedir != NULL)
2698 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2699 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2700 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2701 f = fopen(buf, "r");
2706 ProcessICSInitScript(f);
2708 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2717 EditCommentPopDown();
2732 if (!menuBarWidget) return;
2733 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2735 DisplayError("menuEdit.Revert", 0);
2737 XtSetSensitive(w, !grey);
2739 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2741 DisplayError("menuEdit.Annotate", 0);
2743 XtSetSensitive(w, !grey);
2748 SetMenuEnables(enab)
2752 if (!menuBarWidget) return;
2753 while (enab->name != NULL) {
2754 w = XtNameToWidget(menuBarWidget, enab->name);
2756 DisplayError(enab->name, 0);
2758 XtSetSensitive(w, enab->value);
2764 Enables icsEnables[] = {
2765 { "menuFile.Mail Move", False },
2766 { "menuFile.Reload CMail Message", False },
2767 { "menuMode.Machine Black", False },
2768 { "menuMode.Machine White", False },
2769 { "menuMode.Analysis Mode", False },
2770 { "menuMode.Analyze File", False },
2771 { "menuMode.Two Machines", False },
2772 { "menuMode.Machine Match", False },
2774 { "menuEngine.Hint", False },
2775 { "menuEngine.Book", False },
2776 { "menuEngine.Move Now", False },
2777 #ifndef OPTIONSDIALOG
2778 { "menuOptions.Periodic Updates", False },
2779 { "menuOptions.Hide Thinking", False },
2780 { "menuOptions.Ponder Next Move", False },
2782 { "menuEngine.Engine #1 Settings", False },
2784 { "menuEngine.Engine #2 Settings", False },
2785 { "menuEdit.Annotate", False },
2789 Enables ncpEnables[] = {
2790 { "menuFile.Mail Move", False },
2791 { "menuFile.Reload CMail Message", False },
2792 { "menuMode.Machine White", False },
2793 { "menuMode.Machine Black", False },
2794 { "menuMode.Analysis Mode", False },
2795 { "menuMode.Analyze File", False },
2796 { "menuMode.Two Machines", False },
2797 { "menuMode.Machine Match", False },
2798 { "menuMode.ICS Client", False },
2799 { "menuView.ICStex", False },
2800 { "menuView.ICS Input Box", False },
2801 { "Action", False },
2802 { "menuEdit.Revert", False },
2803 { "menuEdit.Annotate", False },
2804 { "menuEngine.Engine #1 Settings", False },
2805 { "menuEngine.Engine #2 Settings", False },
2806 { "menuEngine.Move Now", False },
2807 { "menuEngine.Retract Move", False },
2808 { "menuOptions.ICS", False },
2809 #ifndef OPTIONSDIALOG
2810 { "menuOptions.Auto Flag", False },
2811 { "menuOptions.Auto Flip View", False },
2812 // { "menuOptions.ICS Alarm", False },
2813 { "menuOptions.Move Sound", False },
2814 { "menuOptions.Hide Thinking", False },
2815 { "menuOptions.Periodic Updates", False },
2816 { "menuOptions.Ponder Next Move", False },
2818 { "menuEngine.Hint", False },
2819 { "menuEngine.Book", False },
2823 Enables gnuEnables[] = {
2824 { "menuMode.ICS Client", False },
2825 { "menuView.ICStex", False },
2826 { "menuView.ICS Input Box", False },
2827 { "menuAction.Accept", False },
2828 { "menuAction.Decline", False },
2829 { "menuAction.Rematch", False },
2830 { "menuAction.Adjourn", False },
2831 { "menuAction.Stop Examining", False },
2832 { "menuAction.Stop Observing", False },
2833 { "menuAction.Upload to Examine", False },
2834 { "menuEdit.Revert", False },
2835 { "menuEdit.Annotate", False },
2836 { "menuOptions.ICS", False },
2838 /* The next two options rely on SetCmailMode being called *after* */
2839 /* SetGNUMode so that when GNU is being used to give hints these */
2840 /* menu options are still available */
2842 { "menuFile.Mail Move", False },
2843 { "menuFile.Reload CMail Message", False },
2847 Enables cmailEnables[] = {
2849 { "menuAction.Call Flag", False },
2850 { "menuAction.Draw", True },
2851 { "menuAction.Adjourn", False },
2852 { "menuAction.Abort", False },
2853 { "menuAction.Stop Observing", False },
2854 { "menuAction.Stop Examining", False },
2855 { "menuFile.Mail Move", True },
2856 { "menuFile.Reload CMail Message", True },
2860 Enables trainingOnEnables[] = {
2861 { "menuMode.Edit Comment", False },
2862 { "menuMode.Pause", False },
2863 { "menuEdit.Forward", False },
2864 { "menuEdit.Backward", False },
2865 { "menuEdit.Forward to End", False },
2866 { "menuEdit.Back to Start", False },
2867 { "menuEngine.Move Now", False },
2868 { "menuEdit.Truncate Game", False },
2872 Enables trainingOffEnables[] = {
2873 { "menuMode.Edit Comment", True },
2874 { "menuMode.Pause", True },
2875 { "menuEdit.Forward", True },
2876 { "menuEdit.Backward", True },
2877 { "menuEdit.Forward to End", True },
2878 { "menuEdit.Back to Start", True },
2879 { "menuEngine.Move Now", True },
2880 { "menuEdit.Truncate Game", True },
2884 Enables machineThinkingEnables[] = {
2885 { "menuFile.Load Game", False },
2886 // { "menuFile.Load Next Game", False },
2887 // { "menuFile.Load Previous Game", False },
2888 // { "menuFile.Reload Same Game", False },
2889 { "menuEdit.Paste Game", False },
2890 { "menuFile.Load Position", False },
2891 // { "menuFile.Load Next Position", False },
2892 // { "menuFile.Load Previous Position", False },
2893 // { "menuFile.Reload Same Position", False },
2894 { "menuEdit.Paste Position", False },
2895 { "menuMode.Machine White", False },
2896 { "menuMode.Machine Black", False },
2897 { "menuMode.Two Machines", False },
2898 { "menuMode.Machine Match", False },
2899 { "menuEngine.Retract Move", False },
2903 Enables userThinkingEnables[] = {
2904 { "menuFile.Load Game", True },
2905 // { "menuFile.Load Next Game", True },
2906 // { "menuFile.Load Previous Game", True },
2907 // { "menuFile.Reload Same Game", True },
2908 { "menuEdit.Paste Game", True },
2909 { "menuFile.Load Position", True },
2910 // { "menuFile.Load Next Position", True },
2911 // { "menuFile.Load Previous Position", True },
2912 // { "menuFile.Reload Same Position", True },
2913 { "menuEdit.Paste Position", True },
2914 { "menuMode.Machine White", True },
2915 { "menuMode.Machine Black", True },
2916 { "menuMode.Two Machines", True },
2917 { "menuMode.Machine Match", True },
2918 { "menuEngine.Retract Move", True },
2924 SetMenuEnables(icsEnables);
2927 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2928 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2935 SetMenuEnables(ncpEnables);
2941 SetMenuEnables(gnuEnables);
2947 SetMenuEnables(cmailEnables);
2953 SetMenuEnables(trainingOnEnables);
2954 if (appData.showButtonBar) {
2955 XtSetSensitive(buttonBarWidget, False);
2961 SetTrainingModeOff()
2963 SetMenuEnables(trainingOffEnables);
2964 if (appData.showButtonBar) {
2965 XtSetSensitive(buttonBarWidget, True);
2970 SetUserThinkingEnables()
2972 if (appData.noChessProgram) return;
2973 SetMenuEnables(userThinkingEnables);
2977 SetMachineThinkingEnables()
2979 if (appData.noChessProgram) return;
2980 SetMenuEnables(machineThinkingEnables);
2982 case MachinePlaysBlack:
2983 case MachinePlaysWhite:
2984 case TwoMachinesPlay:
2985 XtSetSensitive(XtNameToWidget(menuBarWidget,
2986 ModeToWidgetName(gameMode)), True);
2993 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2994 #define HISTORY_SIZE 64
2995 static char *history[HISTORY_SIZE];
2996 int histIn = 0, histP = 0;
2999 SaveInHistory(char *cmd)
3001 if (history[histIn] != NULL) {
3002 free(history[histIn]);
3003 history[histIn] = NULL;
3005 if (*cmd == NULLCHAR) return;
3006 history[histIn] = StrSave(cmd);
3007 histIn = (histIn + 1) % HISTORY_SIZE;
3008 if (history[histIn] != NULL) {
3009 free(history[histIn]);
3010 history[histIn] = NULL;
3016 PrevInHistory(char *cmd)
3019 if (histP == histIn) {
3020 if (history[histIn] != NULL) free(history[histIn]);
3021 history[histIn] = StrSave(cmd);
3023 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3024 if (newhp == histIn || history[newhp] == NULL) return NULL;
3026 return history[histP];
3032 if (histP == histIn) return NULL;
3033 histP = (histP + 1) % HISTORY_SIZE;
3034 return history[histP];
3036 // end of borrowed code
3038 #define Abs(n) ((n)<0 ? -(n) : (n))
3041 * Find a font that matches "pattern" that is as close as
3042 * possible to the targetPxlSize. Prefer fonts that are k
3043 * pixels smaller to fonts that are k pixels larger. The
3044 * pattern must be in the X Consortium standard format,
3045 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3046 * The return value should be freed with XtFree when no
3050 FindFont(pattern, targetPxlSize)
3054 char **fonts, *p, *best, *scalable, *scalableTail;
3055 int i, j, nfonts, minerr, err, pxlSize;
3058 char **missing_list;
3060 char *def_string, *base_fnt_lst, strInt[3];
3062 XFontStruct **fnt_list;
3064 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3065 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3066 p = strstr(pattern, "--");
3067 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3068 strcat(base_fnt_lst, strInt);
3069 strcat(base_fnt_lst, strchr(p + 2, '-'));
3071 if ((fntSet = XCreateFontSet(xDisplay,
3075 &def_string)) == NULL) {
3077 fprintf(stderr, _("Unable to create font set.\n"));
3081 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3083 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3085 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3086 programName, pattern);
3094 for (i=0; i<nfonts; i++) {
3097 if (*p != '-') continue;
3099 if (*p == NULLCHAR) break;
3100 if (*p++ == '-') j++;
3102 if (j < 7) continue;
3105 scalable = fonts[i];
3108 err = pxlSize - targetPxlSize;
3109 if (Abs(err) < Abs(minerr) ||
3110 (minerr > 0 && err < 0 && -err == minerr)) {
3116 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3117 /* If the error is too big and there is a scalable font,
3118 use the scalable font. */
3119 int headlen = scalableTail - scalable;
3120 p = (char *) XtMalloc(strlen(scalable) + 10);
3121 while (isdigit(*scalableTail)) scalableTail++;
3122 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3124 p = (char *) XtMalloc(strlen(best) + 2);
3125 safeStrCpy(p, best, strlen(best)+1 );
3127 if (appData.debugMode) {
3128 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3129 pattern, targetPxlSize, p);
3132 if (missing_count > 0)
3133 XFreeStringList(missing_list);
3134 XFreeFontSet(xDisplay, fntSet);
3136 XFreeFontNames(fonts);
3142 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3143 // must be called before all non-first callse to CreateGCs()
3144 XtReleaseGC(shellWidget, highlineGC);
3145 XtReleaseGC(shellWidget, lightSquareGC);
3146 XtReleaseGC(shellWidget, darkSquareGC);
3147 XtReleaseGC(shellWidget, lineGC);
3148 if (appData.monoMode) {
3149 if (DefaultDepth(xDisplay, xScreen) == 1) {
3150 XtReleaseGC(shellWidget, wbPieceGC);
3152 XtReleaseGC(shellWidget, bwPieceGC);
3155 XtReleaseGC(shellWidget, prelineGC);
3156 XtReleaseGC(shellWidget, jailSquareGC);
3157 XtReleaseGC(shellWidget, wdPieceGC);
3158 XtReleaseGC(shellWidget, wlPieceGC);
3159 XtReleaseGC(shellWidget, wjPieceGC);
3160 XtReleaseGC(shellWidget, bdPieceGC);
3161 XtReleaseGC(shellWidget, blPieceGC);
3162 XtReleaseGC(shellWidget, bjPieceGC);
3166 void CreateGCs(int redo)
3168 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3169 | GCBackground | GCFunction | GCPlaneMask;
3170 XGCValues gc_values;
3173 gc_values.plane_mask = AllPlanes;
3174 gc_values.line_width = lineGap;
3175 gc_values.line_style = LineSolid;
3176 gc_values.function = GXcopy;
3179 DeleteGCs(); // called a second time; clean up old GCs first
3180 } else { // [HGM] grid and font GCs created on first call only
3181 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3182 gc_values.background = XWhitePixel(xDisplay, xScreen);
3183 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3184 XSetFont(xDisplay, coordGC, coordFontID);
3186 // [HGM] make font for holdings counts (white on black)
3187 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3188 gc_values.background = XBlackPixel(xDisplay, xScreen);
3189 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3190 XSetFont(xDisplay, countGC, countFontID);
3192 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3193 gc_values.background = XBlackPixel(xDisplay, xScreen);
3194 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3196 if (appData.monoMode) {
3197 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3198 gc_values.background = XWhitePixel(xDisplay, xScreen);
3199 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3201 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3202 gc_values.background = XBlackPixel(xDisplay, xScreen);
3203 lightSquareGC = wbPieceGC
3204 = XtGetGC(shellWidget, value_mask, &gc_values);
3206 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3207 gc_values.background = XWhitePixel(xDisplay, xScreen);
3208 darkSquareGC = bwPieceGC
3209 = XtGetGC(shellWidget, value_mask, &gc_values);
3211 if (DefaultDepth(xDisplay, xScreen) == 1) {
3212 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3213 gc_values.function = GXcopyInverted;
3214 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3215 gc_values.function = GXcopy;
3216 if (XBlackPixel(xDisplay, xScreen) == 1) {
3217 bwPieceGC = darkSquareGC;
3218 wbPieceGC = copyInvertedGC;
3220 bwPieceGC = copyInvertedGC;
3221 wbPieceGC = lightSquareGC;
3225 gc_values.foreground = highlightSquareColor;
3226 gc_values.background = highlightSquareColor;
3227 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3229 gc_values.foreground = premoveHighlightColor;
3230 gc_values.background = premoveHighlightColor;
3231 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3233 gc_values.foreground = lightSquareColor;
3234 gc_values.background = darkSquareColor;
3235 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3237 gc_values.foreground = darkSquareColor;
3238 gc_values.background = lightSquareColor;
3239 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3241 gc_values.foreground = jailSquareColor;
3242 gc_values.background = jailSquareColor;
3243 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3245 gc_values.foreground = whitePieceColor;
3246 gc_values.background = darkSquareColor;
3247 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3249 gc_values.foreground = whitePieceColor;
3250 gc_values.background = lightSquareColor;
3251 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3253 gc_values.foreground = whitePieceColor;
3254 gc_values.background = jailSquareColor;
3255 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3257 gc_values.foreground = blackPieceColor;
3258 gc_values.background = darkSquareColor;
3259 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3261 gc_values.foreground = blackPieceColor;
3262 gc_values.background = lightSquareColor;
3263 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3265 gc_values.foreground = blackPieceColor;
3266 gc_values.background = jailSquareColor;
3267 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3271 void loadXIM(xim, xmask, filename, dest, mask)
3284 fp = fopen(filename, "rb");
3286 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3293 for (y=0; y<h; ++y) {
3294 for (x=0; x<h; ++x) {
3299 XPutPixel(xim, x, y, blackPieceColor);
3301 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3304 XPutPixel(xim, x, y, darkSquareColor);
3306 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3309 XPutPixel(xim, x, y, whitePieceColor);
3311 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3314 XPutPixel(xim, x, y, lightSquareColor);
3316 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3324 /* create Pixmap of piece */
3325 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3327 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3330 /* create Pixmap of clipmask
3331 Note: We assume the white/black pieces have the same
3332 outline, so we make only 6 masks. This is okay
3333 since the XPM clipmask routines do the same. */
3335 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3337 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3340 /* now create the 1-bit version */
3341 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3344 values.foreground = 1;
3345 values.background = 0;
3347 /* Don't use XtGetGC, not read only */
3348 maskGC = XCreateGC(xDisplay, *mask,
3349 GCForeground | GCBackground, &values);
3350 XCopyPlane(xDisplay, temp, *mask, maskGC,
3351 0, 0, squareSize, squareSize, 0, 0, 1);
3352 XFreePixmap(xDisplay, temp);
3357 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3359 void CreateXIMPieces()
3364 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3369 /* The XSynchronize calls were copied from CreatePieces.
3370 Not sure if needed, but can't hurt */
3371 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3374 /* temp needed by loadXIM() */
3375 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3376 0, 0, ss, ss, AllPlanes, XYPixmap);
3378 if (strlen(appData.pixmapDirectory) == 0) {
3382 if (appData.monoMode) {
3383 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3387 fprintf(stderr, _("\nLoading XIMs...\n"));
3389 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3390 fprintf(stderr, "%d", piece+1);
3391 for (kind=0; kind<4; kind++) {
3392 fprintf(stderr, ".");
3393 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3394 ExpandPathName(appData.pixmapDirectory),
3395 piece <= (int) WhiteKing ? "" : "w",
3396 pieceBitmapNames[piece],
3398 ximPieceBitmap[kind][piece] =
3399 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3400 0, 0, ss, ss, AllPlanes, XYPixmap);
3401 if (appData.debugMode)
3402 fprintf(stderr, _("(File:%s:) "), buf);
3403 loadXIM(ximPieceBitmap[kind][piece],
3405 &(xpmPieceBitmap2[kind][piece]),
3406 &(ximMaskPm2[piece]));
3407 if(piece <= (int)WhiteKing)
3408 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3410 fprintf(stderr," ");
3412 /* Load light and dark squares */
3413 /* If the LSQ and DSQ pieces don't exist, we will
3414 draw them with solid squares. */
3415 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3416 if (access(buf, 0) != 0) {
3420 fprintf(stderr, _("light square "));
3422 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3423 0, 0, ss, ss, AllPlanes, XYPixmap);
3424 if (appData.debugMode)
3425 fprintf(stderr, _("(File:%s:) "), buf);
3427 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3428 fprintf(stderr, _("dark square "));
3429 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3430 ExpandPathName(appData.pixmapDirectory), ss);
3431 if (appData.debugMode)
3432 fprintf(stderr, _("(File:%s:) "), buf);
3434 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3435 0, 0, ss, ss, AllPlanes, XYPixmap);
3436 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3437 xpmJailSquare = xpmLightSquare;
3439 fprintf(stderr, _("Done.\n"));
3441 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3445 void CreateXPMBoard(char *s, int kind)
3449 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3450 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3451 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3455 void FreeXPMPieces()
3456 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3457 // thisroutine has to be called t free the old piece pixmaps
3459 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3460 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3462 XFreePixmap(xDisplay, xpmLightSquare);
3463 XFreePixmap(xDisplay, xpmDarkSquare);
3467 void CreateXPMPieces()
3471 u_int ss = squareSize;
3473 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3474 XpmColorSymbol symbols[4];
3475 static int redo = False;
3477 if(redo) FreeXPMPieces(); else redo = 1;
3479 /* The XSynchronize calls were copied from CreatePieces.
3480 Not sure if needed, but can't hurt */
3481 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3483 /* Setup translations so piece colors match square colors */
3484 symbols[0].name = "light_piece";
3485 symbols[0].value = appData.whitePieceColor;
3486 symbols[1].name = "dark_piece";
3487 symbols[1].value = appData.blackPieceColor;
3488 symbols[2].name = "light_square";
3489 symbols[2].value = appData.lightSquareColor;
3490 symbols[3].name = "dark_square";
3491 symbols[3].value = appData.darkSquareColor;
3493 attr.valuemask = XpmColorSymbols;
3494 attr.colorsymbols = symbols;
3495 attr.numsymbols = 4;
3497 if (appData.monoMode) {
3498 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3502 if (strlen(appData.pixmapDirectory) == 0) {
3503 XpmPieces* pieces = builtInXpms;
3506 while (pieces->size != squareSize && pieces->size) pieces++;
3507 if (!pieces->size) {
3508 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3511 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3512 for (kind=0; kind<4; kind++) {
3514 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3515 pieces->xpm[piece][kind],
3516 &(xpmPieceBitmap2[kind][piece]),
3517 NULL, &attr)) != 0) {
3518 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3522 if(piece <= (int) WhiteKing)
3523 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3527 xpmJailSquare = xpmLightSquare;
3531 fprintf(stderr, _("\nLoading XPMs...\n"));
3534 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3535 fprintf(stderr, "%d ", piece+1);
3536 for (kind=0; kind<4; kind++) {
3537 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3538 ExpandPathName(appData.pixmapDirectory),
3539 piece > (int) WhiteKing ? "w" : "",
3540 pieceBitmapNames[piece],
3542 if (appData.debugMode) {
3543 fprintf(stderr, _("(File:%s:) "), buf);
3545 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3546 &(xpmPieceBitmap2[kind][piece]),
3547 NULL, &attr)) != 0) {
3548 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3549 // [HGM] missing: read of unorthodox piece failed; substitute King.
3550 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3551 ExpandPathName(appData.pixmapDirectory),
3553 if (appData.debugMode) {
3554 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3556 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3557 &(xpmPieceBitmap2[kind][piece]),
3561 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3566 if(piece <= (int) WhiteKing)
3567 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3570 /* Load light and dark squares */
3571 /* If the LSQ and DSQ pieces don't exist, we will
3572 draw them with solid squares. */
3573 fprintf(stderr, _("light square "));
3574 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3575 if (access(buf, 0) != 0) {
3579 if (appData.debugMode)
3580 fprintf(stderr, _("(File:%s:) "), buf);
3582 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3583 &xpmLightSquare, NULL, &attr)) != 0) {
3584 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3587 fprintf(stderr, _("dark square "));
3588 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3589 ExpandPathName(appData.pixmapDirectory), ss);
3590 if (appData.debugMode) {
3591 fprintf(stderr, _("(File:%s:) "), buf);
3593 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3594 &xpmDarkSquare, NULL, &attr)) != 0) {
3595 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3599 xpmJailSquare = xpmLightSquare;
3600 fprintf(stderr, _("Done.\n"));
3602 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3605 #endif /* HAVE_LIBXPM */
3608 /* No built-in bitmaps */
3613 u_int ss = squareSize;
3615 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3618 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3619 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3620 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3621 pieceBitmapNames[piece],
3622 ss, kind == SOLID ? 's' : 'o');
3623 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3624 if(piece <= (int)WhiteKing)
3625 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3629 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3633 /* With built-in bitmaps */
3636 BuiltInBits* bib = builtInBits;
3639 u_int ss = squareSize;
3641 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3644 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3646 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3647 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3648 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3649 pieceBitmapNames[piece],
3650 ss, kind == SOLID ? 's' : 'o');
3651 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3652 bib->bits[kind][piece], ss, ss);
3653 if(piece <= (int)WhiteKing)
3654 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3658 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3663 void ReadBitmap(pm, name, bits, wreq, hreq)
3666 unsigned char bits[];
3672 char msg[MSG_SIZ], fullname[MSG_SIZ];
3674 if (*appData.bitmapDirectory != NULLCHAR) {
3675 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3676 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3677 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3678 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3679 &w, &h, pm, &x_hot, &y_hot);
3680 fprintf(stderr, "load %s\n", name);
3681 if (errcode != BitmapSuccess) {
3683 case BitmapOpenFailed:
3684 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3686 case BitmapFileInvalid:
3687 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3689 case BitmapNoMemory:
3690 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3694 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3698 fprintf(stderr, _("%s: %s...using built-in\n"),
3700 } else if (w != wreq || h != hreq) {
3702 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3703 programName, fullname, w, h, wreq, hreq);
3709 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3718 if (lineGap == 0) return;
3720 /* [HR] Split this into 2 loops for non-square boards. */
3722 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3723 gridSegments[i].x1 = 0;
3724 gridSegments[i].x2 =
3725 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3726 gridSegments[i].y1 = gridSegments[i].y2
3727 = lineGap / 2 + (i * (squareSize + lineGap));
3730 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3731 gridSegments[j + i].y1 = 0;
3732 gridSegments[j + i].y2 =
3733 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3734 gridSegments[j + i].x1 = gridSegments[j + i].x2
3735 = lineGap / 2 + (j * (squareSize + lineGap));
3739 static void MenuBarSelect(w, addr, index)
3744 XtActionProc proc = (XtActionProc) addr;
3746 (proc)(NULL, NULL, NULL, NULL);
3749 void CreateMenuBarPopup(parent, name, mb)
3759 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3762 XtSetArg(args[j], XtNleftMargin, 20); j++;
3763 XtSetArg(args[j], XtNrightMargin, 20); j++;
3765 while (mi->string != NULL) {
3766 if (strcmp(mi->string, "----") == 0) {
3767 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3770 XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3771 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3773 XtAddCallback(entry, XtNcallback,
3774 (XtCallbackProc) MenuBarSelect,
3775 (caddr_t) mi->proc);
3781 Widget CreateMenuBar(mb)
3785 Widget anchor, menuBar;
3787 char menuName[MSG_SIZ];
3790 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3791 XtSetArg(args[j], XtNvSpace, 0); j++;
3792 XtSetArg(args[j], XtNborderWidth, 0); j++;
3793 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3794 formWidget, args, j);
3796 while (mb->name != NULL) {
3797 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3798 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3800 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3803 shortName[0] = mb->name[0];
3804 shortName[1] = NULLCHAR;
3805 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3808 XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3811 XtSetArg(args[j], XtNborderWidth, 0); j++;
3812 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3814 CreateMenuBarPopup(menuBar, menuName, mb);
3820 Widget CreateButtonBar(mi)
3824 Widget button, buttonBar;
3828 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3830 XtSetArg(args[j], XtNhSpace, 0); j++;
3832 XtSetArg(args[j], XtNborderWidth, 0); j++;
3833 XtSetArg(args[j], XtNvSpace, 0); j++;
3834 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3835 formWidget, args, j);
3837 while (mi->string != NULL) {
3840 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3841 XtSetArg(args[j], XtNborderWidth, 0); j++;
3843 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3844 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3845 buttonBar, args, j);
3846 XtAddCallback(button, XtNcallback,
3847 (XtCallbackProc) MenuBarSelect,
3848 (caddr_t) mi->proc);
3855 CreatePieceMenu(name, color)
3862 ChessSquare selection;
3864 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3865 boardWidget, args, 0);
3867 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3868 String item = pieceMenuStrings[color][i];
3870 if (strcmp(item, "----") == 0) {
3871 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3874 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3875 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3877 selection = pieceMenuTranslation[color][i];
3878 XtAddCallback(entry, XtNcallback,
3879 (XtCallbackProc) PieceMenuSelect,
3880 (caddr_t) selection);
3881 if (selection == WhitePawn || selection == BlackPawn) {
3882 XtSetArg(args[0], XtNpopupOnEntry, entry);
3883 XtSetValues(menu, args, 1);
3896 ChessSquare selection;
3898 whitePieceMenu = CreatePieceMenu("menuW", 0);
3899 blackPieceMenu = CreatePieceMenu("menuB", 1);
3901 XtRegisterGrabAction(PieceMenuPopup, True,
3902 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3903 GrabModeAsync, GrabModeAsync);
3905 XtSetArg(args[0], XtNlabel, _("Drop"));
3906 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3907 boardWidget, args, 1);
3908 for (i = 0; i < DROP_MENU_SIZE; i++) {
3909 String item = dropMenuStrings[i];
3911 if (strcmp(item, "----") == 0) {
3912 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3915 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3916 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3918 selection = dropMenuTranslation[i];
3919 XtAddCallback(entry, XtNcallback,
3920 (XtCallbackProc) DropMenuSelect,
3921 (caddr_t) selection);
3926 void SetupDropMenu()
3934 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3935 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3936 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3937 dmEnables[i].piece);
3938 XtSetSensitive(entry, p != NULL || !appData.testLegality
3939 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3940 && !appData.icsActive));
3942 while (p && *p++ == dmEnables[i].piece) count++;
3943 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3945 XtSetArg(args[j], XtNlabel, label); j++;
3946 XtSetValues(entry, args, j);
3950 void PieceMenuPopup(w, event, params, num_params)
3954 Cardinal *num_params;
3956 String whichMenu; int menuNr;
3957 if (event->type == ButtonRelease)
3958 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3959 else if (event->type == ButtonPress)
3960 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3962 case 0: whichMenu = params[0]; break;
3963 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3965 case -1: if (errorUp) ErrorPopDown();
3968 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3971 static void PieceMenuSelect(w, piece, junk)
3976 if (pmFromX < 0 || pmFromY < 0) return;
3977 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3980 static void DropMenuSelect(w, piece, junk)
3985 if (pmFromX < 0 || pmFromY < 0) return;
3986 DropMenuEvent(piece, pmFromX, pmFromY);
3989 void WhiteClock(w, event, prms, nprms)
3998 void BlackClock(w, event, prms, nprms)
4009 * If the user selects on a border boundary, return -1; if off the board,
4010 * return -2. Otherwise map the event coordinate to the square.
4012 int EventToSquare(x, limit)
4020 if ((x % (squareSize + lineGap)) >= squareSize)
4022 x /= (squareSize + lineGap);
4028 static void do_flash_delay(msec)
4034 static void drawHighlight(file, rank, gc)
4040 if (lineGap == 0) return;
4043 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4044 (squareSize + lineGap);
4045 y = lineGap/2 + rank * (squareSize + lineGap);
4047 x = lineGap/2 + file * (squareSize + lineGap);
4048 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4049 (squareSize + lineGap);
4052 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4053 squareSize+lineGap, squareSize+lineGap);
4056 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4057 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4060 SetHighlights(fromX, fromY, toX, toY)
4061 int fromX, fromY, toX, toY;
4063 if (hi1X != fromX || hi1Y != fromY) {
4064 if (hi1X >= 0 && hi1Y >= 0) {
4065 drawHighlight(hi1X, hi1Y, lineGC);
4067 } // [HGM] first erase both, then draw new!
4068 if (hi2X != toX || hi2Y != toY) {
4069 if (hi2X >= 0 && hi2Y >= 0) {
4070 drawHighlight(hi2X, hi2Y, lineGC);
4073 if (hi1X != fromX || hi1Y != fromY) {
4074 if (fromX >= 0 && fromY >= 0) {
4075 drawHighlight(fromX, fromY, highlineGC);
4078 if (hi2X != toX || hi2Y != toY) {
4079 if (toX >= 0 && toY >= 0) {
4080 drawHighlight(toX, toY, highlineGC);
4092 SetHighlights(-1, -1, -1, -1);
4097 SetPremoveHighlights(fromX, fromY, toX, toY)
4098 int fromX, fromY, toX, toY;
4100 if (pm1X != fromX || pm1Y != fromY) {
4101 if (pm1X >= 0 && pm1Y >= 0) {
4102 drawHighlight(pm1X, pm1Y, lineGC);
4104 if (fromX >= 0 && fromY >= 0) {
4105 drawHighlight(fromX, fromY, prelineGC);
4108 if (pm2X != toX || pm2Y != toY) {
4109 if (pm2X >= 0 && pm2Y >= 0) {
4110 drawHighlight(pm2X, pm2Y, lineGC);
4112 if (toX >= 0 && toY >= 0) {
4113 drawHighlight(toX, toY, prelineGC);
4123 ClearPremoveHighlights()
4125 SetPremoveHighlights(-1, -1, -1, -1);
4128 static int CutOutSquare(x, y, x0, y0, kind)
4129 int x, y, *x0, *y0, kind;
4131 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4132 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4134 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4135 if(textureW[kind] < W*squareSize)
4136 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4138 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4139 if(textureH[kind] < H*squareSize)
4140 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4142 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4146 static void BlankSquare(x, y, color, piece, dest, fac)
4147 int x, y, color, fac;
4150 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4152 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4153 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4154 squareSize, squareSize, x*fac, y*fac);
4156 if (useImages && useImageSqs) {
4160 pm = xpmLightSquare;
4165 case 2: /* neutral */
4170 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4171 squareSize, squareSize, x*fac, y*fac);
4181 case 2: /* neutral */
4186 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4191 I split out the routines to draw a piece so that I could
4192 make a generic flash routine.
4194 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4196 int square_color, x, y;
4199 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4200 switch (square_color) {
4202 case 2: /* neutral */
4204 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4205 ? *pieceToOutline(piece)
4206 : *pieceToSolid(piece),
4207 dest, bwPieceGC, 0, 0,
4208 squareSize, squareSize, x, y);
4211 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4212 ? *pieceToSolid(piece)
4213 : *pieceToOutline(piece),
4214 dest, wbPieceGC, 0, 0,
4215 squareSize, squareSize, x, y);
4220 static void monoDrawPiece(piece, square_color, x, y, dest)
4222 int square_color, x, y;
4225 switch (square_color) {
4227 case 2: /* neutral */
4229 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4230 ? *pieceToOutline(piece)
4231 : *pieceToSolid(piece),
4232 dest, bwPieceGC, 0, 0,
4233 squareSize, squareSize, x, y, 1);
4236 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4237 ? *pieceToSolid(piece)
4238 : *pieceToOutline(piece),
4239 dest, wbPieceGC, 0, 0,
4240 squareSize, squareSize, x, y, 1);
4245 static void colorDrawPiece(piece, square_color, x, y, dest)
4247 int square_color, x, y;
4250 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4251 switch (square_color) {
4253 XCopyPlane(xDisplay, *pieceToSolid(piece),
4254 dest, (int) piece < (int) BlackPawn
4255 ? wlPieceGC : blPieceGC, 0, 0,
4256 squareSize, squareSize, x, y, 1);
4259 XCopyPlane(xDisplay, *pieceToSolid(piece),
4260 dest, (int) piece < (int) BlackPawn
4261 ? wdPieceGC : bdPieceGC, 0, 0,
4262 squareSize, squareSize, x, y, 1);
4264 case 2: /* neutral */
4266 XCopyPlane(xDisplay, *pieceToSolid(piece),
4267 dest, (int) piece < (int) BlackPawn
4268 ? wjPieceGC : bjPieceGC, 0, 0,
4269 squareSize, squareSize, x, y, 1);
4274 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4276 int square_color, x, y;
4279 int kind, p = piece;
4281 switch (square_color) {
4283 case 2: /* neutral */
4285 if ((int)piece < (int) BlackPawn) {
4293 if ((int)piece < (int) BlackPawn) {
4301 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4302 if(useTexture & square_color+1) {
4303 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4304 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4305 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4306 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4307 XSetClipMask(xDisplay, wlPieceGC, None);
4308 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4310 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4311 dest, wlPieceGC, 0, 0,
4312 squareSize, squareSize, x, y);
4315 typedef void (*DrawFunc)();
4317 DrawFunc ChooseDrawFunc()
4319 if (appData.monoMode) {
4320 if (DefaultDepth(xDisplay, xScreen) == 1) {
4321 return monoDrawPiece_1bit;
4323 return monoDrawPiece;
4327 return colorDrawPieceImage;
4329 return colorDrawPiece;
4333 /* [HR] determine square color depending on chess variant. */
4334 static int SquareColor(row, column)
4339 if (gameInfo.variant == VariantXiangqi) {
4340 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4342 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4344 } else if (row <= 4) {
4350 square_color = ((column + row) % 2) == 1;
4353 /* [hgm] holdings: next line makes all holdings squares light */
4354 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4356 return square_color;
4359 void DrawSquare(row, column, piece, do_flash)
4360 int row, column, do_flash;
4363 int square_color, x, y, direction, font_ascent, font_descent;
4366 XCharStruct overall;
4370 /* Calculate delay in milliseconds (2-delays per complete flash) */
4371 flash_delay = 500 / appData.flashRate;
4374 x = lineGap + ((BOARD_WIDTH-1)-column) *
4375 (squareSize + lineGap);
4376 y = lineGap + row * (squareSize + lineGap);
4378 x = lineGap + column * (squareSize + lineGap);
4379 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4380 (squareSize + lineGap);
4383 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4385 square_color = SquareColor(row, column);
4387 if ( // [HGM] holdings: blank out area between board and holdings
4388 column == BOARD_LEFT-1 || column == BOARD_RGHT
4389 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4390 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4391 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4393 // [HGM] print piece counts next to holdings
4394 string[1] = NULLCHAR;
4395 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4396 string[0] = '0' + piece;
4397 XTextExtents(countFontStruct, string, 1, &direction,
4398 &font_ascent, &font_descent, &overall);
4399 if (appData.monoMode) {
4400 XDrawImageString(xDisplay, xBoardWindow, countGC,
4401 x + squareSize - overall.width - 2,
4402 y + font_ascent + 1, string, 1);
4404 XDrawString(xDisplay, xBoardWindow, countGC,
4405 x + squareSize - overall.width - 2,
4406 y + font_ascent + 1, string, 1);
4409 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4410 string[0] = '0' + piece;
4411 XTextExtents(countFontStruct, string, 1, &direction,
4412 &font_ascent, &font_descent, &overall);
4413 if (appData.monoMode) {
4414 XDrawImageString(xDisplay, xBoardWindow, countGC,
4415 x + 2, y + font_ascent + 1, string, 1);
4417 XDrawString(xDisplay, xBoardWindow, countGC,
4418 x + 2, y + font_ascent + 1, string, 1);
4422 if (piece == EmptySquare || appData.blindfold) {
4423 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4425 drawfunc = ChooseDrawFunc();
4427 if (do_flash && appData.flashCount > 0) {
4428 for (i=0; i<appData.flashCount; ++i) {
4429 drawfunc(piece, square_color, x, y, xBoardWindow);
4430 XSync(xDisplay, False);
4431 do_flash_delay(flash_delay);
4433 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4434 XSync(xDisplay, False);
4435 do_flash_delay(flash_delay);
4438 drawfunc(piece, square_color, x, y, xBoardWindow);
4442 string[1] = NULLCHAR;
4443 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4444 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4445 string[0] = 'a' + column - BOARD_LEFT;
4446 XTextExtents(coordFontStruct, string, 1, &direction,
4447 &font_ascent, &font_descent, &overall);
4448 if (appData.monoMode) {
4449 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4450 x + squareSize - overall.width - 2,
4451 y + squareSize - font_descent - 1, string, 1);
4453 XDrawString(xDisplay, xBoardWindow, coordGC,
4454 x + squareSize - overall.width - 2,
4455 y + squareSize - font_descent - 1, string, 1);
4458 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4459 string[0] = ONE + row;
4460 XTextExtents(coordFontStruct, string, 1, &direction,
4461 &font_ascent, &font_descent, &overall);
4462 if (appData.monoMode) {
4463 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4464 x + 2, y + font_ascent + 1, string, 1);
4466 XDrawString(xDisplay, xBoardWindow, coordGC,
4467 x + 2, y + font_ascent + 1, string, 1);
4470 if(!partnerUp && marker[row][column]) {
4471 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4472 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4477 /* Why is this needed on some versions of X? */
4478 void EventProc(widget, unused, event)
4483 if (!XtIsRealized(widget))
4486 switch (event->type) {
4488 if (event->xexpose.count > 0) return; /* no clipping is done */
4489 XDrawPosition(widget, True, NULL);
4490 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4491 flipView = !flipView; partnerUp = !partnerUp;
4492 XDrawPosition(widget, True, NULL);
4493 flipView = !flipView; partnerUp = !partnerUp;
4497 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4504 void DrawPosition(fullRedraw, board)
4505 /*Boolean*/int fullRedraw;
4508 XDrawPosition(boardWidget, fullRedraw, board);
4511 /* Returns 1 if there are "too many" differences between b1 and b2
4512 (i.e. more than 1 move was made) */
4513 static int too_many_diffs(b1, b2)
4519 for (i=0; i<BOARD_HEIGHT; ++i) {
4520 for (j=0; j<BOARD_WIDTH; ++j) {
4521 if (b1[i][j] != b2[i][j]) {
4522 if (++c > 4) /* Castling causes 4 diffs */
4530 /* Matrix describing castling maneuvers */
4531 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4532 static int castling_matrix[4][5] = {
4533 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4534 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4535 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4536 { 7, 7, 4, 5, 6 } /* 0-0, black */
4539 /* Checks whether castling occurred. If it did, *rrow and *rcol
4540 are set to the destination (row,col) of the rook that moved.
4542 Returns 1 if castling occurred, 0 if not.
4544 Note: Only handles a max of 1 castling move, so be sure
4545 to call too_many_diffs() first.
4547 static int check_castle_draw(newb, oldb, rrow, rcol)
4554 /* For each type of castling... */
4555 for (i=0; i<4; ++i) {
4556 r = castling_matrix[i];
4558 /* Check the 4 squares involved in the castling move */
4560 for (j=1; j<=4; ++j) {
4561 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4568 /* All 4 changed, so it must be a castling move */
4577 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4578 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4580 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4583 void DrawSeekBackground( int left, int top, int right, int bottom )
4585 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4588 void DrawSeekText(char *buf, int x, int y)
4590 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4593 void DrawSeekDot(int x, int y, int colorNr)
4595 int square = colorNr & 0x80;
4598 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4600 XFillRectangle(xDisplay, xBoardWindow, color,
4601 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4603 XFillArc(xDisplay, xBoardWindow, color,
4604 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4607 static int damage[2][BOARD_RANKS][BOARD_FILES];
4610 * event handler for redrawing the board
4612 void XDrawPosition(w, repaint, board)
4614 /*Boolean*/int repaint;
4618 static int lastFlipView = 0;
4619 static int lastBoardValid[2] = {0, 0};
4620 static Board lastBoard[2];
4623 int nr = twoBoards*partnerUp;
4625 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4627 if (board == NULL) {
4628 if (!lastBoardValid[nr]) return;
4629 board = lastBoard[nr];
4631 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4632 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4633 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4638 * It would be simpler to clear the window with XClearWindow()
4639 * but this causes a very distracting flicker.
4642 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4644 if ( lineGap && IsDrawArrowEnabled())
4645 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4646 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4648 /* If too much changes (begin observing new game, etc.), don't
4650 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4652 /* Special check for castling so we don't flash both the king
4653 and the rook (just flash the king). */
4655 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4656 /* Draw rook with NO flashing. King will be drawn flashing later */
4657 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4658 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4662 /* First pass -- Draw (newly) empty squares and repair damage.
4663 This prevents you from having a piece show up twice while it
4664 is flashing on its new square */
4665 for (i = 0; i < BOARD_HEIGHT; i++)
4666 for (j = 0; j < BOARD_WIDTH; j++)
4667 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4668 || damage[nr][i][j]) {
4669 DrawSquare(i, j, board[i][j], 0);
4670 damage[nr][i][j] = False;
4673 /* Second pass -- Draw piece(s) in new position and flash them */
4674 for (i = 0; i < BOARD_HEIGHT; i++)
4675 for (j = 0; j < BOARD_WIDTH; j++)
4676 if (board[i][j] != lastBoard[nr][i][j]) {
4677 DrawSquare(i, j, board[i][j], do_flash);
4681 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4682 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4683 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4685 for (i = 0; i < BOARD_HEIGHT; i++)
4686 for (j = 0; j < BOARD_WIDTH; j++) {
4687 DrawSquare(i, j, board[i][j], 0);
4688 damage[nr][i][j] = False;
4692 CopyBoard(lastBoard[nr], board);
4693 lastBoardValid[nr] = 1;
4694 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4695 lastFlipView = flipView;
4697 /* Draw highlights */
4698 if (pm1X >= 0 && pm1Y >= 0) {
4699 drawHighlight(pm1X, pm1Y, prelineGC);
4701 if (pm2X >= 0 && pm2Y >= 0) {
4702 drawHighlight(pm2X, pm2Y, prelineGC);
4704 if (hi1X >= 0 && hi1Y >= 0) {
4705 drawHighlight(hi1X, hi1Y, highlineGC);
4707 if (hi2X >= 0 && hi2Y >= 0) {
4708 drawHighlight(hi2X, hi2Y, highlineGC);
4710 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4712 /* If piece being dragged around board, must redraw that too */
4715 XSync(xDisplay, False);
4720 * event handler for redrawing the board
4722 void DrawPositionProc(w, event, prms, nprms)
4728 XDrawPosition(w, True, NULL);
4733 * event handler for parsing user moves
4735 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4736 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4737 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4738 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4739 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4740 // and at the end FinishMove() to perform the move after optional promotion popups.
4741 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4742 void HandleUserMove(w, event, prms, nprms)
4748 if (w != boardWidget || errorExitStatus != -1) return;
4749 if(nprms) shiftKey = !strcmp(prms[0], "1");
4752 if (event->type == ButtonPress) {
4753 XtPopdown(promotionShell);
4754 XtDestroyWidget(promotionShell);
4755 promotionUp = False;
4763 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4764 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4765 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4768 void AnimateUserMove (Widget w, XEvent * event,
4769 String * params, Cardinal * nParams)
4771 DragPieceMove(event->xmotion.x, event->xmotion.y);
4774 void HandlePV (Widget w, XEvent * event,
4775 String * params, Cardinal * nParams)
4776 { // [HGM] pv: walk PV
4777 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4780 Widget CommentCreate(name, text, mutable, callback, lines)
4782 int /*Boolean*/ mutable;
4783 XtCallbackProc callback;
4787 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4792 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4793 XtGetValues(boardWidget, args, j);
4796 XtSetArg(args[j], XtNresizable, True); j++;
4799 XtCreatePopupShell(name, topLevelShellWidgetClass,
4800 shellWidget, args, j);
4803 XtCreatePopupShell(name, transientShellWidgetClass,
4804 shellWidget, args, j);
4807 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4808 layoutArgs, XtNumber(layoutArgs));
4810 XtCreateManagedWidget("form", formWidgetClass, layout,
4811 formArgs, XtNumber(formArgs));
4815 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
4816 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
4818 XtSetArg(args[j], XtNstring, text); j++;
4819 XtSetArg(args[j], XtNtop, XtChainTop); j++;
4820 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4821 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4822 XtSetArg(args[j], XtNright, XtChainRight); j++;
4823 XtSetArg(args[j], XtNresizable, True); j++;
4824 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
4825 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4826 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
4827 XtSetArg(args[j], XtNautoFill, True); j++;
4828 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4830 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4831 XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
4835 XtSetArg(args[j], XtNfromVert, edit); j++;
4836 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4837 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4838 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4839 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4841 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4842 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4845 XtSetArg(args[j], XtNfromVert, edit); j++;
4846 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
4847 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4848 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4849 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4850 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4852 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4853 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4856 XtSetArg(args[j], XtNfromVert, edit); j++;
4857 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
4858 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4859 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4860 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4861 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4863 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4864 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4867 XtSetArg(args[j], XtNfromVert, edit); j++;
4868 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4869 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4870 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4871 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4873 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4874 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4877 XtSetArg(args[j], XtNfromVert, edit); j++;
4878 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
4879 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4880 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4881 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4882 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4884 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4885 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4888 XtRealizeWidget(shell);
4890 if (commentX == -1) {
4893 Dimension pw_height;
4894 Dimension ew_height;
4897 XtSetArg(args[j], XtNheight, &ew_height); j++;
4898 XtGetValues(edit, args, j);
4901 XtSetArg(args[j], XtNheight, &pw_height); j++;
4902 XtGetValues(shell, args, j);
4903 commentH = pw_height + (lines - 1) * ew_height;
4904 commentW = bw_width - 16;
4906 XSync(xDisplay, False);
4908 /* This code seems to tickle an X bug if it is executed too soon
4909 after xboard starts up. The coordinates get transformed as if
4910 the main window was positioned at (0, 0).
4912 XtTranslateCoords(shellWidget,
4913 (bw_width - commentW) / 2, 0 - commentH / 2,
4914 &commentX, &commentY);
4916 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4917 RootWindowOfScreen(XtScreen(shellWidget)),
4918 (bw_width - commentW) / 2, 0 - commentH / 2,
4923 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4926 if(wpComment.width > 0) {
4927 commentX = wpComment.x;
4928 commentY = wpComment.y;
4929 commentW = wpComment.width;
4930 commentH = wpComment.height;
4934 XtSetArg(args[j], XtNheight, commentH); j++;
4935 XtSetArg(args[j], XtNwidth, commentW); j++;
4936 XtSetArg(args[j], XtNx, commentX); j++;
4937 XtSetArg(args[j], XtNy, commentY); j++;
4938 XtSetValues(shell, args, j);
4939 XtSetKeyboardFocus(shell, edit);
4944 static int savedIndex; /* gross that this is global */
4946 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4949 XawTextPosition index, dummy;
4952 XawTextGetSelectionPos(w, &index, &dummy);
4953 XtSetArg(arg, XtNstring, &val);
4954 XtGetValues(w, &arg, 1);
4955 ReplaceComment(savedIndex, val);
4956 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4957 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4960 void EditCommentPopUp(index, title, text)
4969 if (text == NULL) text = "";
4971 if (editShell == NULL) {
4973 CommentCreate(title, text, True, EditCommentCallback, 4);
4974 XtRealizeWidget(editShell);
4975 CatchDeleteWindow(editShell, "EditCommentPopDown");
4977 edit = XtNameToWidget(editShell, "*form.text");
4979 XtSetArg(args[j], XtNstring, text); j++;
4980 XtSetValues(edit, args, j);
4982 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
4983 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
4984 XtSetValues(editShell, args, j);
4987 XtPopup(editShell, XtGrabNone);
4991 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4992 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
4994 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
4998 void EditCommentCallback(w, client_data, call_data)
5000 XtPointer client_data, call_data;
5008 XtSetArg(args[j], XtNlabel, &name); j++;
5009 XtGetValues(w, args, j);
5011 if (strcmp(name, _("ok")) == 0) {
5012 edit = XtNameToWidget(editShell, "*form.text");
5014 XtSetArg(args[j], XtNstring, &val); j++;
5015 XtGetValues(edit, args, j);
5016 ReplaceComment(savedIndex, val);
5017 EditCommentPopDown();
5018 } else if (strcmp(name, _("cancel")) == 0) {
5019 EditCommentPopDown();
5020 } else if (strcmp(name, _("clear")) == 0) {
5021 edit = XtNameToWidget(editShell, "*form.text");
5022 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5023 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5027 void EditCommentPopDown()
5032 if (!editUp) return;
5034 XtSetArg(args[j], XtNx, &commentX); j++;
5035 XtSetArg(args[j], XtNy, &commentY); j++;
5036 XtSetArg(args[j], XtNheight, &commentH); j++;
5037 XtSetArg(args[j], XtNwidth, &commentW); j++;
5038 XtGetValues(editShell, args, j);
5039 XtPopdown(editShell);
5042 XtSetArg(args[j], XtNleftBitmap, None); j++;
5043 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
5045 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
5049 void ICSInputBoxPopUp()
5054 extern Option boxOptions[];
5056 void ICSInputSendText()
5063 edit = boxOptions[0].handle;
5065 XtSetArg(args[j], XtNstring, &val); j++;
5066 XtGetValues(edit, args, j);
5068 SendMultiLineToICS(val);
5069 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5070 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5073 void ICSInputBoxPopDown()
5078 void CommentPopUp(title, text)
5085 savedIndex = currentMove; // [HGM] vari
5086 if (commentShell == NULL) {
5088 CommentCreate(title, text, False, CommentCallback, 4);
5089 XtRealizeWidget(commentShell);
5090 CatchDeleteWindow(commentShell, "CommentPopDown");
5092 edit = XtNameToWidget(commentShell, "*form.text");
5094 XtSetArg(args[j], XtNstring, text); j++;
5095 XtSetValues(edit, args, j);
5097 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
5098 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
5099 XtSetValues(commentShell, args, j);
5102 XtPopup(commentShell, XtGrabNone);
5103 XSync(xDisplay, False);
5108 void CommentCallback(w, client_data, call_data)
5110 XtPointer client_data, call_data;
5117 XtSetArg(args[j], XtNlabel, &name); j++;
5118 XtGetValues(w, args, j);
5120 if (strcmp(name, _("close")) == 0) {
5122 } else if (strcmp(name, _("edit")) == 0) {
5129 void CommentPopDown()
5134 if (!commentUp) return;
5136 XtSetArg(args[j], XtNx, &commentX); j++;
5137 XtSetArg(args[j], XtNy, &commentY); j++;
5138 XtSetArg(args[j], XtNwidth, &commentW); j++;
5139 XtSetArg(args[j], XtNheight, &commentH); j++;
5140 XtGetValues(commentShell, args, j);
5141 XtPopdown(commentShell);
5142 XSync(xDisplay, False);
5146 void FileNamePopUp(label, def, proc, openMode)
5152 fileProc = proc; /* I can't see a way not */
5153 fileOpenMode = openMode; /* to use globals here */
5154 { // [HGM] use file-selector dialog stolen from Ghostview
5156 int index; // this is not supported yet
5158 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5159 def, openMode, NULL, &name))
5160 (void) (*fileProc)(f, index=0, name);
5164 void FileNamePopDown()
5166 if (!filenameUp) return;
5167 XtPopdown(fileNameShell);
5168 XtDestroyWidget(fileNameShell);
5173 void FileNameCallback(w, client_data, call_data)
5175 XtPointer client_data, call_data;
5180 XtSetArg(args[0], XtNlabel, &name);
5181 XtGetValues(w, args, 1);
5183 if (strcmp(name, _("cancel")) == 0) {
5188 FileNameAction(w, NULL, NULL, NULL);
5191 void FileNameAction(w, event, prms, nprms)
5203 name = XawDialogGetValueString(w = XtParent(w));
5205 if ((name != NULL) && (*name != NULLCHAR)) {
5206 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5207 XtPopdown(w = XtParent(XtParent(w)));
5211 p = strrchr(buf, ' ');
5218 fullname = ExpandPathName(buf);
5220 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5223 f = fopen(fullname, fileOpenMode);
5225 DisplayError(_("Failed to open file"), errno);
5227 (void) (*fileProc)(f, index, buf);
5234 XtPopdown(w = XtParent(XtParent(w)));
5240 void PromotionPopUp()
5243 Widget dialog, layout;
5245 Dimension bw_width, pw_width;
5249 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5250 XtGetValues(boardWidget, args, j);
5253 XtSetArg(args[j], XtNresizable, True); j++;
5254 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5256 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5257 shellWidget, args, j);
5259 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5260 layoutArgs, XtNumber(layoutArgs));
5263 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5264 XtSetArg(args[j], XtNborderWidth, 0); j++;
5265 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5268 if(gameInfo.variant != VariantShogi) {
5269 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5270 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5271 (XtPointer) dialog);
5272 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5273 (XtPointer) dialog);
5274 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5275 (XtPointer) dialog);
5276 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5277 (XtPointer) dialog);
5279 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5280 (XtPointer) dialog);
5281 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5282 (XtPointer) dialog);
5283 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5284 (XtPointer) dialog);
5285 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5286 (XtPointer) dialog);
5288 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5289 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5290 gameInfo.variant == VariantGiveaway) {
5291 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5292 (XtPointer) dialog);
5294 if(gameInfo.variant == VariantCapablanca ||
5295 gameInfo.variant == VariantGothic ||
5296 gameInfo.variant == VariantCapaRandom) {
5297 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5298 (XtPointer) dialog);
5299 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5300 (XtPointer) dialog);
5302 } else // [HGM] shogi
5304 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5305 (XtPointer) dialog);
5306 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5307 (XtPointer) dialog);
5309 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5310 (XtPointer) dialog);
5312 XtRealizeWidget(promotionShell);
5313 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5316 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5317 XtGetValues(promotionShell, args, j);
5319 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5320 lineGap + squareSize/3 +
5321 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5322 0 : 6*(squareSize + lineGap)), &x, &y);
5325 XtSetArg(args[j], XtNx, x); j++;
5326 XtSetArg(args[j], XtNy, y); j++;
5327 XtSetValues(promotionShell, args, j);
5329 XtPopup(promotionShell, XtGrabNone);
5334 void PromotionPopDown()
5336 if (!promotionUp) return;
5337 XtPopdown(promotionShell);
5338 XtDestroyWidget(promotionShell);
5339 promotionUp = False;
5342 void PromotionCallback(w, client_data, call_data)
5344 XtPointer client_data, call_data;
5350 XtSetArg(args[0], XtNlabel, &name);
5351 XtGetValues(w, args, 1);
5355 if (fromX == -1) return;
5357 if (strcmp(name, _("cancel")) == 0) {
5361 } else if (strcmp(name, _("Knight")) == 0) {
5363 } else if (strcmp(name, _("Promote")) == 0) {
5365 } else if (strcmp(name, _("Defer")) == 0) {
5368 promoChar = ToLower(name[0]);
5371 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5373 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5374 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5379 void ErrorCallback(w, client_data, call_data)
5381 XtPointer client_data, call_data;
5384 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5386 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5392 if (!errorUp) return;
5394 XtPopdown(errorShell);
5395 XtDestroyWidget(errorShell);
5396 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5399 void ErrorPopUp(title, label, modal)
5400 char *title, *label;
5404 Widget dialog, layout;
5408 Dimension bw_width, pw_width;
5409 Dimension pw_height;
5413 XtSetArg(args[i], XtNresizable, True); i++;
5414 XtSetArg(args[i], XtNtitle, title); i++;
5416 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5417 shellWidget, args, i);
5419 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5420 layoutArgs, XtNumber(layoutArgs));
5423 XtSetArg(args[i], XtNlabel, label); i++;
5424 XtSetArg(args[i], XtNborderWidth, 0); i++;
5425 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5428 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5430 XtRealizeWidget(errorShell);
5431 CatchDeleteWindow(errorShell, "ErrorPopDown");
5434 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5435 XtGetValues(boardWidget, args, i);
5437 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5438 XtSetArg(args[i], XtNheight, &pw_height); i++;
5439 XtGetValues(errorShell, args, i);
5442 /* This code seems to tickle an X bug if it is executed too soon
5443 after xboard starts up. The coordinates get transformed as if
5444 the main window was positioned at (0, 0).
5446 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5447 0 - pw_height + squareSize / 3, &x, &y);
5449 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5450 RootWindowOfScreen(XtScreen(boardWidget)),
5451 (bw_width - pw_width) / 2,
5452 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5456 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5459 XtSetArg(args[i], XtNx, x); i++;
5460 XtSetArg(args[i], XtNy, y); i++;
5461 XtSetValues(errorShell, args, i);
5464 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5467 /* Disable all user input other than deleting the window */
5468 static int frozen = 0;
5472 /* Grab by a widget that doesn't accept input */
5473 XtAddGrab(messageWidget, TRUE, FALSE);
5477 /* Undo a FreezeUI */
5480 if (!frozen) return;
5481 XtRemoveGrab(messageWidget);
5485 char *ModeToWidgetName(mode)
5489 case BeginningOfGame:
5490 if (appData.icsActive)
5491 return "menuMode.ICS Client";
5492 else if (appData.noChessProgram ||
5493 *appData.cmailGameName != NULLCHAR)
5494 return "menuMode.Edit Game";
5496 return "menuMode.Machine Black";
5497 case MachinePlaysBlack:
5498 return "menuMode.Machine Black";
5499 case MachinePlaysWhite:
5500 return "menuMode.Machine White";
5502 return "menuMode.Analysis Mode";
5504 return "menuMode.Analyze File";
5505 case TwoMachinesPlay:
5506 return "menuMode.Two Machines";
5508 return "menuMode.Edit Game";
5509 case PlayFromGameFile:
5510 return "menuFile.Load Game";
5512 return "menuMode.Edit Position";
5514 return "menuMode.Training";
5515 case IcsPlayingWhite:
5516 case IcsPlayingBlack:
5520 return "menuMode.ICS Client";
5527 void ModeHighlight()
5530 static int oldPausing = FALSE;
5531 static GameMode oldmode = (GameMode) -1;
5534 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5536 if (pausing != oldPausing) {
5537 oldPausing = pausing;
5539 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5541 XtSetArg(args[0], XtNleftBitmap, None);
5543 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5546 if (appData.showButtonBar) {
5547 /* Always toggle, don't set. Previous code messes up when
5548 invoked while the button is pressed, as releasing it
5549 toggles the state again. */
5552 XtSetArg(args[0], XtNbackground, &oldbg);
5553 XtSetArg(args[1], XtNforeground, &oldfg);
5554 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5556 XtSetArg(args[0], XtNbackground, oldfg);
5557 XtSetArg(args[1], XtNforeground, oldbg);
5559 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5563 wname = ModeToWidgetName(oldmode);
5564 if (wname != NULL) {
5565 XtSetArg(args[0], XtNleftBitmap, None);
5566 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5568 wname = ModeToWidgetName(gameMode);
5569 if (wname != NULL) {
5570 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5571 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5575 /* Maybe all the enables should be handled here, not just this one */
5576 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5577 gameMode == Training || gameMode == PlayFromGameFile);
5582 * Button/menu procedures
5584 void ResetProc(w, event, prms, nprms)
5593 int LoadGamePopUp(f, gameNumber, title)
5598 cmailMsgLoaded = FALSE;
5599 if (gameNumber == 0) {
5600 int error = GameListBuild(f);
5602 DisplayError(_("Cannot build game list"), error);
5603 } else if (!ListEmpty(&gameList) &&
5604 ((ListGame *) gameList.tailPred)->number > 1) {
5605 GameListPopUp(f, title);
5611 return LoadGame(f, gameNumber, title, FALSE);
5614 void LoadGameProc(w, event, prms, nprms)
5620 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5623 FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
5626 void LoadNextGameProc(w, event, prms, nprms)
5635 void LoadPrevGameProc(w, event, prms, nprms)
5644 void ReloadGameProc(w, event, prms, nprms)
5653 void LoadNextPositionProc(w, event, prms, nprms)
5662 void LoadPrevPositionProc(w, event, prms, nprms)
5671 void ReloadPositionProc(w, event, prms, nprms)
5680 void LoadPositionProc(w, event, prms, nprms)
5686 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5689 FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5692 void SaveGameProc(w, event, prms, nprms)
5698 FileNamePopUp(_("Save game file name?"),
5699 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5703 void SavePositionProc(w, event, prms, nprms)
5709 FileNamePopUp(_("Save position file name?"),
5710 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5714 void ReloadCmailMsgProc(w, event, prms, nprms)
5720 ReloadCmailMsgEvent(FALSE);
5723 void MailMoveProc(w, event, prms, nprms)
5732 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5733 char *selected_fen_position=NULL;
5736 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5737 Atom *type_return, XtPointer *value_return,
5738 unsigned long *length_return, int *format_return)
5740 char *selection_tmp;
5742 if (!selected_fen_position) return False; /* should never happen */
5743 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5744 /* note: since no XtSelectionDoneProc was registered, Xt will
5745 * automatically call XtFree on the value returned. So have to
5746 * make a copy of it allocated with XtMalloc */
5747 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5748 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5750 *value_return=selection_tmp;
5751 *length_return=strlen(selection_tmp);
5752 *type_return=*target;
5753 *format_return = 8; /* bits per byte */
5755 } else if (*target == XA_TARGETS(xDisplay)) {
5756 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5757 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5758 targets_tmp[1] = XA_STRING;
5759 *value_return = targets_tmp;
5760 *type_return = XA_ATOM;
5762 *format_return = 8 * sizeof(Atom);
5763 if (*format_return > 32) {
5764 *length_return *= *format_return / 32;
5765 *format_return = 32;
5773 /* note: when called from menu all parameters are NULL, so no clue what the
5774 * Widget which was clicked on was, or what the click event was
5776 void CopyPositionProc(w, event, prms, nprms)
5783 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5784 * have a notion of a position that is selected but not copied.
5785 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5787 if(gameMode == EditPosition) EditPositionDone(TRUE);
5788 if (selected_fen_position) free(selected_fen_position);
5789 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5790 if (!selected_fen_position) return;
5791 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5793 SendPositionSelection,
5794 NULL/* lose_ownership_proc */ ,
5795 NULL/* transfer_done_proc */);
5796 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5798 SendPositionSelection,
5799 NULL/* lose_ownership_proc */ ,
5800 NULL/* transfer_done_proc */);
5803 /* function called when the data to Paste is ready */
5805 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5806 Atom *type, XtPointer value, unsigned long *len, int *format)
5809 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5810 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5811 EditPositionPasteFEN(fenstr);
5815 /* called when Paste Position button is pressed,
5816 * all parameters will be NULL */
5817 void PastePositionProc(w, event, prms, nprms)
5823 XtGetSelectionValue(menuBarWidget,
5824 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5825 /* (XtSelectionCallbackProc) */ PastePositionCB,
5826 NULL, /* client_data passed to PastePositionCB */
5828 /* better to use the time field from the event that triggered the
5829 * call to this function, but that isn't trivial to get
5837 SendGameSelection(Widget w, Atom *selection, Atom *target,
5838 Atom *type_return, XtPointer *value_return,
5839 unsigned long *length_return, int *format_return)
5841 char *selection_tmp;
5843 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5844 FILE* f = fopen(gameCopyFilename, "r");
5847 if (f == NULL) return False;
5851 selection_tmp = XtMalloc(len + 1);
5852 count = fread(selection_tmp, 1, len, f);
5855 XtFree(selection_tmp);
5858 selection_tmp[len] = NULLCHAR;
5859 *value_return = selection_tmp;
5860 *length_return = len;
5861 *type_return = *target;
5862 *format_return = 8; /* bits per byte */
5864 } else if (*target == XA_TARGETS(xDisplay)) {
5865 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5866 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5867 targets_tmp[1] = XA_STRING;
5868 *value_return = targets_tmp;
5869 *type_return = XA_ATOM;
5871 *format_return = 8 * sizeof(Atom);
5872 if (*format_return > 32) {
5873 *length_return *= *format_return / 32;
5874 *format_return = 32;
5882 /* note: when called from menu all parameters are NULL, so no clue what the
5883 * Widget which was clicked on was, or what the click event was
5885 void CopyGameProc(w, event, prms, nprms)
5893 ret = SaveGameToFile(gameCopyFilename, FALSE);
5897 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5898 * have a notion of a game that is selected but not copied.
5899 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5901 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5904 NULL/* lose_ownership_proc */ ,
5905 NULL/* transfer_done_proc */);
5906 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5909 NULL/* lose_ownership_proc */ ,
5910 NULL/* transfer_done_proc */);
5913 /* function called when the data to Paste is ready */
5915 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5916 Atom *type, XtPointer value, unsigned long *len, int *format)
5919 if (value == NULL || *len == 0) {
5920 return; /* nothing had been selected to copy */
5922 f = fopen(gamePasteFilename, "w");
5924 DisplayError(_("Can't open temp file"), errno);
5927 fwrite(value, 1, *len, f);
5930 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5933 /* called when Paste Game button is pressed,
5934 * all parameters will be NULL */
5935 void PasteGameProc(w, event, prms, nprms)
5941 XtGetSelectionValue(menuBarWidget,
5942 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5943 /* (XtSelectionCallbackProc) */ PasteGameCB,
5944 NULL, /* client_data passed to PasteGameCB */
5946 /* better to use the time field from the event that triggered the
5947 * call to this function, but that isn't trivial to get
5957 SaveGameProc(NULL, NULL, NULL, NULL);
5961 void QuitProc(w, event, prms, nprms)
5970 void PauseProc(w, event, prms, nprms)
5980 void MachineBlackProc(w, event, prms, nprms)
5986 MachineBlackEvent();
5989 void MachineWhiteProc(w, event, prms, nprms)
5995 MachineWhiteEvent();
5998 void AnalyzeModeProc(w, event, prms, nprms)
6006 if (!first.analysisSupport) {
6007 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6008 DisplayError(buf, 0);
6011 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
6012 if (appData.icsActive) {
6013 if (gameMode != IcsObserving) {
6014 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
6015 DisplayError(buf, 0);
6017 if (appData.icsEngineAnalyze) {
6018 if (appData.debugMode)
6019 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
6025 /* if enable, use want disable icsEngineAnalyze */
6026 if (appData.icsEngineAnalyze) {
6031 appData.icsEngineAnalyze = TRUE;
6032 if (appData.debugMode)
6033 fprintf(debugFP, _("ICS engine analyze starting... \n"));
6035 #ifndef OPTIONSDIALOG
6036 if (!appData.showThinking)
6037 ShowThinkingProc(w,event,prms,nprms);
6043 void AnalyzeFileProc(w, event, prms, nprms)
6049 if (!first.analysisSupport) {
6051 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6052 DisplayError(buf, 0);
6056 #ifndef OPTIONSDIALOG
6057 if (!appData.showThinking)
6058 ShowThinkingProc(w,event,prms,nprms);
6061 FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
6062 AnalysisPeriodicEvent(1);
6065 void TwoMachinesProc(w, event, prms, nprms)
6074 void MatchProc(w, event, prms, nprms)
6080 if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
6081 matchMode = 2; // This is back-end, really
\r
6082 appData.matchGames = appData.defaultMatchGames;
\r
6084 first.matchWins = second.matchWins = 0;
\r
6088 void IcsClientProc(w, event, prms, nprms)
6097 void EditGameProc(w, event, prms, nprms)
6106 void EditPositionProc(w, event, prms, nprms)
6112 EditPositionEvent();
6115 void TrainingProc(w, event, prms, nprms)
6124 void EditCommentProc(w, event, prms, nprms)
6131 EditCommentPopDown();
6137 void IcsInputBoxProc(w, event, prms, nprms)
6143 if (!PopDown(4)) ICSInputBoxPopUp();
6146 void AcceptProc(w, event, prms, nprms)
6155 void DeclineProc(w, event, prms, nprms)
6164 void RematchProc(w, event, prms, nprms)
6173 void CallFlagProc(w, event, prms, nprms)
6182 void DrawProc(w, event, prms, nprms)
6191 void AbortProc(w, event, prms, nprms)
6200 void AdjournProc(w, event, prms, nprms)
6209 void ResignProc(w, event, prms, nprms)
6218 void AdjuWhiteProc(w, event, prms, nprms)
6224 UserAdjudicationEvent(+1);
6227 void AdjuBlackProc(w, event, prms, nprms)
6233 UserAdjudicationEvent(-1);
6236 void AdjuDrawProc(w, event, prms, nprms)
6242 UserAdjudicationEvent(0);
6245 void EnterKeyProc(w, event, prms, nprms)
6251 if (shellUp[4] == True)
6255 void UpKeyProc(w, event, prms, nprms)
6260 { // [HGM] input: let up-arrow recall previous line from history
6267 if (!shellUp[4]) return;
6268 edit = boxOptions[0].handle;
6270 XtSetArg(args[j], XtNstring, &val); j++;
6271 XtGetValues(edit, args, j);
6272 val = PrevInHistory(val);
6273 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6274 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6276 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6277 XawTextReplace(edit, 0, 0, &t);
6278 XawTextSetInsertionPoint(edit, 9999);
6282 void DownKeyProc(w, event, prms, nprms)
6287 { // [HGM] input: let down-arrow recall next line from history
6292 if (!shellUp[4]) return;
6293 edit = boxOptions[0].handle;
6294 val = NextInHistory();
6295 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6296 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6298 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6299 XawTextReplace(edit, 0, 0, &t);
6300 XawTextSetInsertionPoint(edit, 9999);
6304 void StopObservingProc(w, event, prms, nprms)
6310 StopObservingEvent();
6313 void StopExaminingProc(w, event, prms, nprms)
6319 StopExaminingEvent();
6322 void UploadProc(w, event, prms, nprms)
6332 void ForwardProc(w, event, prms, nprms)
6342 void BackwardProc(w, event, prms, nprms)
6351 void ToStartProc(w, event, prms, nprms)
6360 void ToEndProc(w, event, prms, nprms)
6369 void RevertProc(w, event, prms, nprms)
6378 void AnnotateProc(w, event, prms, nprms)
6387 void TruncateGameProc(w, event, prms, nprms)
6393 TruncateGameEvent();
6395 void RetractMoveProc(w, event, prms, nprms)
6404 void MoveNowProc(w, event, prms, nprms)
6413 void FlipViewProc(w, event, prms, nprms)
6419 flipView = !flipView;
6420 DrawPosition(True, NULL);
6423 void PonderNextMoveProc(w, event, prms, nprms)
6431 PonderNextMoveEvent(!appData.ponderNextMove);
6432 #ifndef OPTIONSDIALOG
6433 if (appData.ponderNextMove) {
6434 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6436 XtSetArg(args[0], XtNleftBitmap, None);
6438 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6443 #ifndef OPTIONSDIALOG
6444 void AlwaysQueenProc(w, event, prms, nprms)
6452 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6454 if (appData.alwaysPromoteToQueen) {
6455 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6457 XtSetArg(args[0], XtNleftBitmap, None);
6459 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6463 void AnimateDraggingProc(w, event, prms, nprms)
6471 appData.animateDragging = !appData.animateDragging;
6473 if (appData.animateDragging) {
6474 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6477 XtSetArg(args[0], XtNleftBitmap, None);
6479 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6483 void AnimateMovingProc(w, event, prms, nprms)
6491 appData.animate = !appData.animate;
6493 if (appData.animate) {
6494 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6497 XtSetArg(args[0], XtNleftBitmap, None);
6499 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6503 void AutoflagProc(w, event, prms, nprms)
6511 appData.autoCallFlag = !appData.autoCallFlag;
6513 if (appData.autoCallFlag) {
6514 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6516 XtSetArg(args[0], XtNleftBitmap, None);
6518 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6522 void AutoflipProc(w, event, prms, nprms)
6530 appData.autoFlipView = !appData.autoFlipView;
6532 if (appData.autoFlipView) {
6533 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6535 XtSetArg(args[0], XtNleftBitmap, None);
6537 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6541 void BlindfoldProc(w, event, prms, nprms)
6549 appData.blindfold = !appData.blindfold;
6551 if (appData.blindfold) {
6552 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6554 XtSetArg(args[0], XtNleftBitmap, None);
6556 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6559 DrawPosition(True, NULL);
6562 void TestLegalityProc(w, event, prms, nprms)
6570 appData.testLegality = !appData.testLegality;
6572 if (appData.testLegality) {
6573 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6575 XtSetArg(args[0], XtNleftBitmap, None);
6577 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6582 void FlashMovesProc(w, event, prms, nprms)
6590 if (appData.flashCount == 0) {
6591 appData.flashCount = 3;
6593 appData.flashCount = -appData.flashCount;
6596 if (appData.flashCount > 0) {
6597 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6599 XtSetArg(args[0], XtNleftBitmap, None);
6601 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6606 void HighlightDraggingProc(w, event, prms, nprms)
6614 appData.highlightDragging = !appData.highlightDragging;
6616 if (appData.highlightDragging) {
6617 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6619 XtSetArg(args[0], XtNleftBitmap, None);
6621 XtSetValues(XtNameToWidget(menuBarWidget,
6622 "menuOptions.Highlight Dragging"), args, 1);
6626 void HighlightLastMoveProc(w, event, prms, nprms)
6634 appData.highlightLastMove = !appData.highlightLastMove;
6636 if (appData.highlightLastMove) {
6637 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6639 XtSetArg(args[0], XtNleftBitmap, None);
6641 XtSetValues(XtNameToWidget(menuBarWidget,
6642 "menuOptions.Highlight Last Move"), args, 1);
6645 void HighlightArrowProc(w, event, prms, nprms)
6653 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6655 if (appData.highlightMoveWithArrow) {
6656 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6658 XtSetArg(args[0], XtNleftBitmap, None);
6660 XtSetValues(XtNameToWidget(menuBarWidget,
6661 "menuOptions.Arrow"), args, 1);
6665 void IcsAlarmProc(w, event, prms, nprms)
6673 appData.icsAlarm = !appData.icsAlarm;
6675 if (appData.icsAlarm) {
6676 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6678 XtSetArg(args[0], XtNleftBitmap, None);
6680 XtSetValues(XtNameToWidget(menuBarWidget,
6681 "menuOptions.ICS Alarm"), args, 1);
6685 void MoveSoundProc(w, event, prms, nprms)
6693 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6695 if (appData.ringBellAfterMoves) {
6696 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6698 XtSetArg(args[0], XtNleftBitmap, None);
6700 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6704 void OneClickProc(w, event, prms, nprms)
6712 appData.oneClick = !appData.oneClick;
6714 if (appData.oneClick) {
6715 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6717 XtSetArg(args[0], XtNleftBitmap, None);
6719 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6723 void PeriodicUpdatesProc(w, event, prms, nprms)
6731 PeriodicUpdatesEvent(!appData.periodicUpdates);
6733 if (appData.periodicUpdates) {
6734 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6736 XtSetArg(args[0], XtNleftBitmap, None);
6738 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6742 void PopupExitMessageProc(w, event, prms, nprms)
6750 appData.popupExitMessage = !appData.popupExitMessage;
6752 if (appData.popupExitMessage) {
6753 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6755 XtSetArg(args[0], XtNleftBitmap, None);
6757 XtSetValues(XtNameToWidget(menuBarWidget,
6758 "menuOptions.Popup Exit Message"), args, 1);
6761 void PopupMoveErrorsProc(w, event, prms, nprms)
6769 appData.popupMoveErrors = !appData.popupMoveErrors;
6771 if (appData.popupMoveErrors) {
6772 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6774 XtSetArg(args[0], XtNleftBitmap, None);
6776 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6781 void PremoveProc(w, event, prms, nprms)
6789 appData.premove = !appData.premove;
6791 if (appData.premove) {
6792 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6794 XtSetArg(args[0], XtNleftBitmap, None);
6796 XtSetValues(XtNameToWidget(menuBarWidget,
6797 "menuOptions.Premove"), args, 1);
6801 void ShowCoordsProc(w, event, prms, nprms)
6809 appData.showCoords = !appData.showCoords;
6811 if (appData.showCoords) {
6812 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6814 XtSetArg(args[0], XtNleftBitmap, None);
6816 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6819 DrawPosition(True, NULL);
6822 void ShowThinkingProc(w, event, prms, nprms)
6828 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6829 ShowThinkingEvent();
6832 void HideThinkingProc(w, event, prms, nprms)
6840 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6841 ShowThinkingEvent();
6843 if (appData.hideThinkingFromHuman) {
6844 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6846 XtSetArg(args[0], XtNleftBitmap, None);
6848 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6853 void SaveOnExitProc(w, event, prms, nprms)
6861 saveSettingsOnExit = !saveSettingsOnExit;
6863 if (saveSettingsOnExit) {
6864 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6866 XtSetArg(args[0], XtNleftBitmap, None);
6868 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6872 void SaveSettingsProc(w, event, prms, nprms)
6878 SaveSettings(settingsFileName);
6881 void InfoProc(w, event, prms, nprms)
6888 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6893 void ManProc(w, event, prms, nprms)
6901 if (nprms && *nprms > 0)
6905 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6909 void HintProc(w, event, prms, nprms)
6918 void BookProc(w, event, prms, nprms)
6927 void AboutProc(w, event, prms, nprms)
6935 char *zippy = " (with Zippy code)";
6939 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6940 programVersion, zippy,
6941 "Copyright 1991 Digital Equipment Corporation",
6942 "Enhancements Copyright 1992-2009 Free Software Foundation",
6943 "Enhancements Copyright 2005 Alessandro Scotti",
6944 PACKAGE, " is free software and carries NO WARRANTY;",
6945 "see the file COPYING for more information.");
6946 ErrorPopUp(_("About XBoard"), buf, FALSE);
6949 void DebugProc(w, event, prms, nprms)
6955 appData.debugMode = !appData.debugMode;
6958 void AboutGameProc(w, event, prms, nprms)
6967 void NothingProc(w, event, prms, nprms)
6976 void Iconify(w, event, prms, nprms)
6985 XtSetArg(args[0], XtNiconic, True);
6986 XtSetValues(shellWidget, args, 1);
6989 void DisplayMessage(message, extMessage)
6990 char *message, *extMessage;
6992 /* display a message in the message widget */
7001 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
7006 message = extMessage;
7010 /* need to test if messageWidget already exists, since this function
7011 can also be called during the startup, if for example a Xresource
7012 is not set up correctly */
7015 XtSetArg(arg, XtNlabel, message);
7016 XtSetValues(messageWidget, &arg, 1);
7022 void DisplayTitle(text)
7027 char title[MSG_SIZ];
7030 if (text == NULL) text = "";
7032 if (appData.titleInWindow) {
7034 XtSetArg(args[i], XtNlabel, text); i++;
7035 XtSetValues(titleWidget, args, i);
7038 if (*text != NULLCHAR) {
7039 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
7040 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
7041 } else if (appData.icsActive) {
7042 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7043 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7044 } else if (appData.cmailGameName[0] != NULLCHAR) {
7045 snprintf(icon, sizeof(icon), "%s", "CMail");
7046 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7048 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7049 } else if (gameInfo.variant == VariantGothic) {
7050 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7051 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
7054 } else if (gameInfo.variant == VariantFalcon) {
7055 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7056 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
7058 } else if (appData.noChessProgram) {
7059 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7060 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
7062 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
7063 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7066 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
7067 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
7068 XtSetValues(shellWidget, args, i);
7073 DisplayError(message, error)
7080 if (appData.debugMode || appData.matchMode) {
7081 fprintf(stderr, "%s: %s\n", programName, message);
7084 if (appData.debugMode || appData.matchMode) {
7085 fprintf(stderr, "%s: %s: %s\n",
7086 programName, message, strerror(error));
7088 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7091 ErrorPopUp(_("Error"), message, FALSE);
7095 void DisplayMoveError(message)
7100 DrawPosition(FALSE, NULL);
7101 if (appData.debugMode || appData.matchMode) {
7102 fprintf(stderr, "%s: %s\n", programName, message);
7104 if (appData.popupMoveErrors) {
7105 ErrorPopUp(_("Error"), message, FALSE);
7107 DisplayMessage(message, "");
7112 void DisplayFatalError(message, error, status)
7118 errorExitStatus = status;
7120 fprintf(stderr, "%s: %s\n", programName, message);
7122 fprintf(stderr, "%s: %s: %s\n",
7123 programName, message, strerror(error));
7124 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7127 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7128 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7134 void DisplayInformation(message)
7138 ErrorPopUp(_("Information"), message, TRUE);
7141 void DisplayNote(message)
7145 ErrorPopUp(_("Note"), message, FALSE);
7149 NullXErrorCheck(dpy, error_event)
7151 XErrorEvent *error_event;
7156 void DisplayIcsInteractionTitle(message)
7159 if (oldICSInteractionTitle == NULL) {
7160 /* Magic to find the old window title, adapted from vim */
7161 char *wina = getenv("WINDOWID");
7163 Window win = (Window) atoi(wina);
7164 Window root, parent, *children;
7165 unsigned int nchildren;
7166 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7168 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7169 if (!XQueryTree(xDisplay, win, &root, &parent,
7170 &children, &nchildren)) break;
7171 if (children) XFree((void *)children);
7172 if (parent == root || parent == 0) break;
7175 XSetErrorHandler(oldHandler);
7177 if (oldICSInteractionTitle == NULL) {
7178 oldICSInteractionTitle = "xterm";
7181 printf("\033]0;%s\007", message);
7185 char pendingReplyPrefix[MSG_SIZ];
7186 ProcRef pendingReplyPR;
7188 void AskQuestionProc(w, event, prms, nprms)
7195 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7199 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7202 void AskQuestionPopDown()
7204 if (!askQuestionUp) return;
7205 XtPopdown(askQuestionShell);
7206 XtDestroyWidget(askQuestionShell);
7207 askQuestionUp = False;
7210 void AskQuestionReplyAction(w, event, prms, nprms)
7220 reply = XawDialogGetValueString(w = XtParent(w));
7221 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7222 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7223 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7224 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7225 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7226 AskQuestionPopDown();
7228 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7231 void AskQuestionCallback(w, client_data, call_data)
7233 XtPointer client_data, call_data;
7238 XtSetArg(args[0], XtNlabel, &name);
7239 XtGetValues(w, args, 1);
7241 if (strcmp(name, _("cancel")) == 0) {
7242 AskQuestionPopDown();
7244 AskQuestionReplyAction(w, NULL, NULL, NULL);
7248 void AskQuestion(title, question, replyPrefix, pr)
7249 char *title, *question, *replyPrefix;
7253 Widget popup, layout, dialog, edit;
7259 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7260 pendingReplyPR = pr;
7263 XtSetArg(args[i], XtNresizable, True); i++;
7264 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7265 askQuestionShell = popup =
7266 XtCreatePopupShell(title, transientShellWidgetClass,
7267 shellWidget, args, i);
7270 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7271 layoutArgs, XtNumber(layoutArgs));
7274 XtSetArg(args[i], XtNlabel, question); i++;
7275 XtSetArg(args[i], XtNvalue, ""); i++;
7276 XtSetArg(args[i], XtNborderWidth, 0); i++;
7277 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7280 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7281 (XtPointer) dialog);
7282 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7283 (XtPointer) dialog);
7285 XtRealizeWidget(popup);
7286 CatchDeleteWindow(popup, "AskQuestionPopDown");
7288 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7289 &x, &y, &win_x, &win_y, &mask);
7291 XtSetArg(args[0], XtNx, x - 10);
7292 XtSetArg(args[1], XtNy, y - 30);
7293 XtSetValues(popup, args, 2);
7295 XtPopup(popup, XtGrabExclusive);
7296 askQuestionUp = True;
7298 edit = XtNameToWidget(dialog, "*value");
7299 XtSetKeyboardFocus(popup, edit);
7307 if (*name == NULLCHAR) {
7309 } else if (strcmp(name, "$") == 0) {
7310 putc(BELLCHAR, stderr);
7313 char *prefix = "", *sep = "";
7314 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7315 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7323 PlaySound(appData.soundMove);
7329 PlaySound(appData.soundIcsWin);
7335 PlaySound(appData.soundIcsLoss);
7341 PlaySound(appData.soundIcsDraw);
7345 PlayIcsUnfinishedSound()
7347 PlaySound(appData.soundIcsUnfinished);
7353 PlaySound(appData.soundIcsAlarm);
7359 system("stty echo");
7365 system("stty -echo");
7369 Colorize(cc, continuation)
7374 int count, outCount, error;
7376 if (textColors[(int)cc].bg > 0) {
7377 if (textColors[(int)cc].fg > 0) {
7378 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7379 textColors[(int)cc].fg, textColors[(int)cc].bg);
7381 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7382 textColors[(int)cc].bg);
7385 if (textColors[(int)cc].fg > 0) {
7386 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7387 textColors[(int)cc].fg);
7389 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7392 count = strlen(buf);
7393 outCount = OutputToProcess(NoProc, buf, count, &error);
7394 if (outCount < count) {
7395 DisplayFatalError(_("Error writing to display"), error, 1);
7398 if (continuation) return;
7401 PlaySound(appData.soundShout);
7404 PlaySound(appData.soundSShout);
7407 PlaySound(appData.soundChannel1);
7410 PlaySound(appData.soundChannel);
7413 PlaySound(appData.soundKibitz);
7416 PlaySound(appData.soundTell);
7418 case ColorChallenge:
7419 PlaySound(appData.soundChallenge);
7422 PlaySound(appData.soundRequest);
7425 PlaySound(appData.soundSeek);
7436 return getpwuid(getuid())->pw_name;
7440 ExpandPathName(path)
7443 static char static_buf[4*MSG_SIZ];
7444 char *d, *s, buf[4*MSG_SIZ];
7450 while (*s && isspace(*s))
7459 if (*(s+1) == '/') {
7460 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7464 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7465 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7466 pwd = getpwnam(buf);
7469 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7473 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7474 strcat(d, strchr(s+1, '/'));
7478 safeStrCpy(d, s, 4*MSG_SIZ );
7485 static char host_name[MSG_SIZ];
7487 #if HAVE_GETHOSTNAME
7488 gethostname(host_name, MSG_SIZ);
7490 #else /* not HAVE_GETHOSTNAME */
7491 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7492 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7494 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7496 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7497 #endif /* not HAVE_GETHOSTNAME */
7500 XtIntervalId delayedEventTimerXID = 0;
7501 DelayedEventCallback delayedEventCallback = 0;
7506 delayedEventTimerXID = 0;
7507 delayedEventCallback();
7511 ScheduleDelayedEvent(cb, millisec)
7512 DelayedEventCallback cb; long millisec;
7514 if(delayedEventTimerXID && delayedEventCallback == cb)
7515 // [HGM] alive: replace, rather than add or flush identical event
7516 XtRemoveTimeOut(delayedEventTimerXID);
7517 delayedEventCallback = cb;
7518 delayedEventTimerXID =
7519 XtAppAddTimeOut(appContext, millisec,
7520 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7523 DelayedEventCallback
7526 if (delayedEventTimerXID) {
7527 return delayedEventCallback;
7534 CancelDelayedEvent()
7536 if (delayedEventTimerXID) {
7537 XtRemoveTimeOut(delayedEventTimerXID);
7538 delayedEventTimerXID = 0;
7542 XtIntervalId loadGameTimerXID = 0;
7544 int LoadGameTimerRunning()
7546 return loadGameTimerXID != 0;
7549 int StopLoadGameTimer()
7551 if (loadGameTimerXID != 0) {
7552 XtRemoveTimeOut(loadGameTimerXID);
7553 loadGameTimerXID = 0;
7561 LoadGameTimerCallback(arg, id)
7565 loadGameTimerXID = 0;
7570 StartLoadGameTimer(millisec)
7574 XtAppAddTimeOut(appContext, millisec,
7575 (XtTimerCallbackProc) LoadGameTimerCallback,
7579 XtIntervalId analysisClockXID = 0;
7582 AnalysisClockCallback(arg, id)
7586 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7587 || appData.icsEngineAnalyze) { // [DM]
7588 AnalysisPeriodicEvent(0);
7589 StartAnalysisClock();
7594 StartAnalysisClock()
7597 XtAppAddTimeOut(appContext, 2000,
7598 (XtTimerCallbackProc) AnalysisClockCallback,
7602 XtIntervalId clockTimerXID = 0;
7604 int ClockTimerRunning()
7606 return clockTimerXID != 0;
7609 int StopClockTimer()
7611 if (clockTimerXID != 0) {
7612 XtRemoveTimeOut(clockTimerXID);
7621 ClockTimerCallback(arg, id)
7630 StartClockTimer(millisec)
7634 XtAppAddTimeOut(appContext, millisec,
7635 (XtTimerCallbackProc) ClockTimerCallback,
7640 DisplayTimerLabel(w, color, timer, highlight)
7649 /* check for low time warning */
7650 Pixel foregroundOrWarningColor = timerForegroundPixel;
7653 appData.lowTimeWarning &&
7654 (timer / 1000) < appData.icsAlarmTime)
7655 foregroundOrWarningColor = lowTimeWarningColor;
7657 if (appData.clockMode) {
7658 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7659 XtSetArg(args[0], XtNlabel, buf);
7661 snprintf(buf, MSG_SIZ, "%s ", color);
7662 XtSetArg(args[0], XtNlabel, buf);
7667 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7668 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7670 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7671 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7674 XtSetValues(w, args, 3);
7678 DisplayWhiteClock(timeRemaining, highlight)
7684 if(appData.noGUI) return;
7685 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7686 if (highlight && iconPixmap == bIconPixmap) {
7687 iconPixmap = wIconPixmap;
7688 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7689 XtSetValues(shellWidget, args, 1);
7694 DisplayBlackClock(timeRemaining, highlight)
7700 if(appData.noGUI) return;
7701 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7702 if (highlight && iconPixmap == wIconPixmap) {
7703 iconPixmap = bIconPixmap;
7704 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7705 XtSetValues(shellWidget, args, 1);
7723 int StartChildProcess(cmdLine, dir, pr)
7730 int to_prog[2], from_prog[2];
7734 if (appData.debugMode) {
7735 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7738 /* We do NOT feed the cmdLine to the shell; we just
7739 parse it into blank-separated arguments in the
7740 most simple-minded way possible.
7743 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7746 while(*p == ' ') p++;
7748 if(*p == '"' || *p == '\'')
7749 p = strchr(++argv[i-1], *p);
7750 else p = strchr(p, ' ');
7751 if (p == NULL) break;
7756 SetUpChildIO(to_prog, from_prog);
7758 if ((pid = fork()) == 0) {
7760 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7761 close(to_prog[1]); // first close the unused pipe ends
7762 close(from_prog[0]);
7763 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7764 dup2(from_prog[1], 1);
7765 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7766 close(from_prog[1]); // and closing again loses one of the pipes!
7767 if(fileno(stderr) >= 2) // better safe than sorry...
7768 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7770 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7775 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7777 execvp(argv[0], argv);
7779 /* If we get here, exec failed */
7784 /* Parent process */
7786 close(from_prog[1]);
7788 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7791 cp->fdFrom = from_prog[0];
7792 cp->fdTo = to_prog[1];
7797 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7798 static RETSIGTYPE AlarmCallBack(int n)
7804 DestroyChildProcess(pr, signalType)
7808 ChildProc *cp = (ChildProc *) pr;
7810 if (cp->kind != CPReal) return;
7812 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7813 signal(SIGALRM, AlarmCallBack);
7815 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7816 kill(cp->pid, SIGKILL); // kill it forcefully
7817 wait((int *) 0); // and wait again
7821 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7823 /* Process is exiting either because of the kill or because of
7824 a quit command sent by the backend; either way, wait for it to die.
7833 InterruptChildProcess(pr)
7836 ChildProc *cp = (ChildProc *) pr;
7838 if (cp->kind != CPReal) return;
7839 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7842 int OpenTelnet(host, port, pr)
7847 char cmdLine[MSG_SIZ];
7849 if (port[0] == NULLCHAR) {
7850 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7852 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7854 return StartChildProcess(cmdLine, "", pr);
7857 int OpenTCP(host, port, pr)
7863 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7864 #else /* !OMIT_SOCKETS */
7866 struct sockaddr_in sa;
7868 unsigned short uport;
7871 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7875 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7876 sa.sin_family = AF_INET;
7877 sa.sin_addr.s_addr = INADDR_ANY;
7878 uport = (unsigned short) 0;
7879 sa.sin_port = htons(uport);
7880 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7884 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7885 if (!(hp = gethostbyname(host))) {
7887 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7888 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7889 hp->h_addrtype = AF_INET;
7891 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7892 hp->h_addr_list[0] = (char *) malloc(4);
7893 hp->h_addr_list[0][0] = b0;
7894 hp->h_addr_list[0][1] = b1;
7895 hp->h_addr_list[0][2] = b2;
7896 hp->h_addr_list[0][3] = b3;
7901 sa.sin_family = hp->h_addrtype;
7902 uport = (unsigned short) atoi(port);
7903 sa.sin_port = htons(uport);
7904 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7906 if (connect(s, (struct sockaddr *) &sa,
7907 sizeof(struct sockaddr_in)) < 0) {
7911 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7918 #endif /* !OMIT_SOCKETS */
7923 int OpenCommPort(name, pr)
7930 fd = open(name, 2, 0);
7931 if (fd < 0) return errno;
7933 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7943 int OpenLoopback(pr)
7949 SetUpChildIO(to, from);
7951 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7954 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7961 int OpenRcmd(host, user, cmd, pr)
7962 char *host, *user, *cmd;
7965 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7969 #define INPUT_SOURCE_BUF_SIZE 8192
7978 char buf[INPUT_SOURCE_BUF_SIZE];
7983 DoInputCallback(closure, source, xid)
7988 InputSource *is = (InputSource *) closure;
7993 if (is->lineByLine) {
7994 count = read(is->fd, is->unused,
7995 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7997 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
8000 is->unused += count;
8002 while (p < is->unused) {
8003 q = memchr(p, '\n', is->unused - p);
8004 if (q == NULL) break;
8006 (is->func)(is, is->closure, p, q - p, 0);
8010 while (p < is->unused) {
8015 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8020 (is->func)(is, is->closure, is->buf, count, error);
8024 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8031 ChildProc *cp = (ChildProc *) pr;
8033 is = (InputSource *) calloc(1, sizeof(InputSource));
8034 is->lineByLine = lineByLine;
8038 is->fd = fileno(stdin);
8040 is->kind = cp->kind;
8041 is->fd = cp->fdFrom;
8044 is->unused = is->buf;
8047 is->xid = XtAppAddInput(appContext, is->fd,
8048 (XtPointer) (XtInputReadMask),
8049 (XtInputCallbackProc) DoInputCallback,
8051 is->closure = closure;
8052 return (InputSourceRef) is;
8056 RemoveInputSource(isr)
8059 InputSource *is = (InputSource *) isr;
8061 if (is->xid == 0) return;
8062 XtRemoveInput(is->xid);
8066 int OutputToProcess(pr, message, count, outError)
8072 static int line = 0;
8073 ChildProc *cp = (ChildProc *) pr;
8078 if (appData.noJoin || !appData.useInternalWrap)
8079 outCount = fwrite(message, 1, count, stdout);
8082 int width = get_term_width();
8083 int len = wrap(NULL, message, count, width, &line);
8084 char *msg = malloc(len);
8088 outCount = fwrite(message, 1, count, stdout);
8091 dbgchk = wrap(msg, message, count, width, &line);
8092 if (dbgchk != len && appData.debugMode)
8093 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8094 outCount = fwrite(msg, 1, dbgchk, stdout);
8100 outCount = write(cp->fdTo, message, count);
8110 /* Output message to process, with "ms" milliseconds of delay
8111 between each character. This is needed when sending the logon
8112 script to ICC, which for some reason doesn't like the
8113 instantaneous send. */
8114 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8121 ChildProc *cp = (ChildProc *) pr;
8126 r = write(cp->fdTo, message++, 1);
8139 /**** Animation code by Hugh Fisher, DCS, ANU.
8141 Known problem: if a window overlapping the board is
8142 moved away while a piece is being animated underneath,
8143 the newly exposed area won't be updated properly.
8144 I can live with this.
8146 Known problem: if you look carefully at the animation
8147 of pieces in mono mode, they are being drawn as solid
8148 shapes without interior detail while moving. Fixing
8149 this would be a major complication for minimal return.
8152 /* Masks for XPM pieces. Black and white pieces can have
8153 different shapes, but in the interest of retaining my
8154 sanity pieces must have the same outline on both light
8155 and dark squares, and all pieces must use the same
8156 background square colors/images. */
8158 static int xpmDone = 0;
8161 CreateAnimMasks (pieceDepth)
8168 unsigned long plane;
8171 /* Need a bitmap just to get a GC with right depth */
8172 buf = XCreatePixmap(xDisplay, xBoardWindow,
8174 values.foreground = 1;
8175 values.background = 0;
8176 /* Don't use XtGetGC, not read only */
8177 maskGC = XCreateGC(xDisplay, buf,
8178 GCForeground | GCBackground, &values);
8179 XFreePixmap(xDisplay, buf);
8181 buf = XCreatePixmap(xDisplay, xBoardWindow,
8182 squareSize, squareSize, pieceDepth);
8183 values.foreground = XBlackPixel(xDisplay, xScreen);
8184 values.background = XWhitePixel(xDisplay, xScreen);
8185 bufGC = XCreateGC(xDisplay, buf,
8186 GCForeground | GCBackground, &values);
8188 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8189 /* Begin with empty mask */
8190 if(!xpmDone) // [HGM] pieces: keep using existing
8191 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8192 squareSize, squareSize, 1);
8193 XSetFunction(xDisplay, maskGC, GXclear);
8194 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8195 0, 0, squareSize, squareSize);
8197 /* Take a copy of the piece */
8202 XSetFunction(xDisplay, bufGC, GXcopy);
8203 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8205 0, 0, squareSize, squareSize, 0, 0);
8207 /* XOR the background (light) over the piece */
8208 XSetFunction(xDisplay, bufGC, GXxor);
8210 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8211 0, 0, squareSize, squareSize, 0, 0);
8213 XSetForeground(xDisplay, bufGC, lightSquareColor);
8214 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8217 /* We now have an inverted piece image with the background
8218 erased. Construct mask by just selecting all the non-zero
8219 pixels - no need to reconstruct the original image. */
8220 XSetFunction(xDisplay, maskGC, GXor);
8222 /* Might be quicker to download an XImage and create bitmap
8223 data from it rather than this N copies per piece, but it
8224 only takes a fraction of a second and there is a much
8225 longer delay for loading the pieces. */
8226 for (n = 0; n < pieceDepth; n ++) {
8227 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8228 0, 0, squareSize, squareSize,
8234 XFreePixmap(xDisplay, buf);
8235 XFreeGC(xDisplay, bufGC);
8236 XFreeGC(xDisplay, maskGC);
8240 InitAnimState (anim, info)
8242 XWindowAttributes * info;
8247 /* Each buffer is square size, same depth as window */
8248 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8249 squareSize, squareSize, info->depth);
8250 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8251 squareSize, squareSize, info->depth);
8253 /* Create a plain GC for blitting */
8254 mask = GCForeground | GCBackground | GCFunction |
8255 GCPlaneMask | GCGraphicsExposures;
8256 values.foreground = XBlackPixel(xDisplay, xScreen);
8257 values.background = XWhitePixel(xDisplay, xScreen);
8258 values.function = GXcopy;
8259 values.plane_mask = AllPlanes;
8260 values.graphics_exposures = False;
8261 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8263 /* Piece will be copied from an existing context at
8264 the start of each new animation/drag. */
8265 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8267 /* Outline will be a read-only copy of an existing */
8268 anim->outlineGC = None;
8274 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8275 XWindowAttributes info;
8277 if (xpmDone && gameInfo.variant == old) return;
8278 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8279 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8281 InitAnimState(&game, &info);
8282 InitAnimState(&player, &info);
8284 /* For XPM pieces, we need bitmaps to use as masks. */
8286 CreateAnimMasks(info.depth);
8292 static Boolean frameWaiting;
8294 static RETSIGTYPE FrameAlarm (sig)
8297 frameWaiting = False;
8298 /* In case System-V style signals. Needed?? */
8299 signal(SIGALRM, FrameAlarm);
8306 struct itimerval delay;
8308 XSync(xDisplay, False);
8311 frameWaiting = True;
8312 signal(SIGALRM, FrameAlarm);
8313 delay.it_interval.tv_sec =
8314 delay.it_value.tv_sec = time / 1000;
8315 delay.it_interval.tv_usec =
8316 delay.it_value.tv_usec = (time % 1000) * 1000;
8317 setitimer(ITIMER_REAL, &delay, NULL);
8318 while (frameWaiting) pause();
8319 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8320 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8321 setitimer(ITIMER_REAL, &delay, NULL);
8331 XSync(xDisplay, False);
8333 usleep(time * 1000);
8338 /* Convert board position to corner of screen rect and color */
8341 ScreenSquare(column, row, pt, color)
8342 int column; int row; XPoint * pt; int * color;
8345 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8346 pt->y = lineGap + row * (squareSize + lineGap);
8348 pt->x = lineGap + column * (squareSize + lineGap);
8349 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8351 *color = SquareColor(row, column);
8354 /* Convert window coords to square */
8357 BoardSquare(x, y, column, row)
8358 int x; int y; int * column; int * row;
8360 *column = EventToSquare(x, BOARD_WIDTH);
8361 if (flipView && *column >= 0)
8362 *column = BOARD_WIDTH - 1 - *column;
8363 *row = EventToSquare(y, BOARD_HEIGHT);
8364 if (!flipView && *row >= 0)
8365 *row = BOARD_HEIGHT - 1 - *row;
8370 #undef Max /* just in case */
8372 #define Max(a, b) ((a) > (b) ? (a) : (b))
8373 #define Min(a, b) ((a) < (b) ? (a) : (b))
8376 SetRect(rect, x, y, width, height)
8377 XRectangle * rect; int x; int y; int width; int height;
8381 rect->width = width;
8382 rect->height = height;
8385 /* Test if two frames overlap. If they do, return
8386 intersection rect within old and location of
8387 that rect within new. */
8390 Intersect(old, new, size, area, pt)
8391 XPoint * old; XPoint * new;
8392 int size; XRectangle * area; XPoint * pt;
8394 if (old->x > new->x + size || new->x > old->x + size ||
8395 old->y > new->y + size || new->y > old->y + size) {
8398 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8399 size - abs(old->x - new->x), size - abs(old->y - new->y));
8400 pt->x = Max(old->x - new->x, 0);
8401 pt->y = Max(old->y - new->y, 0);
8406 /* For two overlapping frames, return the rect(s)
8407 in the old that do not intersect with the new. */
8410 CalcUpdateRects(old, new, size, update, nUpdates)
8411 XPoint * old; XPoint * new; int size;
8412 XRectangle update[]; int * nUpdates;
8416 /* If old = new (shouldn't happen) then nothing to draw */
8417 if (old->x == new->x && old->y == new->y) {
8421 /* Work out what bits overlap. Since we know the rects
8422 are the same size we don't need a full intersect calc. */
8424 /* Top or bottom edge? */
8425 if (new->y > old->y) {
8426 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8428 } else if (old->y > new->y) {
8429 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8430 size, old->y - new->y);
8433 /* Left or right edge - don't overlap any update calculated above. */
8434 if (new->x > old->x) {
8435 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8436 new->x - old->x, size - abs(new->y - old->y));
8438 } else if (old->x > new->x) {
8439 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8440 old->x - new->x, size - abs(new->y - old->y));
8447 /* Generate a series of frame coords from start->mid->finish.
8448 The movement rate doubles until the half way point is
8449 reached, then halves back down to the final destination,
8450 which gives a nice slow in/out effect. The algorithmn
8451 may seem to generate too many intermediates for short
8452 moves, but remember that the purpose is to attract the
8453 viewers attention to the piece about to be moved and
8454 then to where it ends up. Too few frames would be less
8458 Tween(start, mid, finish, factor, frames, nFrames)
8459 XPoint * start; XPoint * mid;
8460 XPoint * finish; int factor;
8461 XPoint frames[]; int * nFrames;
8463 int fraction, n, count;
8467 /* Slow in, stepping 1/16th, then 1/8th, ... */
8469 for (n = 0; n < factor; n++)
8471 for (n = 0; n < factor; n++) {
8472 frames[count].x = start->x + (mid->x - start->x) / fraction;
8473 frames[count].y = start->y + (mid->y - start->y) / fraction;
8475 fraction = fraction / 2;
8479 frames[count] = *mid;
8482 /* Slow out, stepping 1/2, then 1/4, ... */
8484 for (n = 0; n < factor; n++) {
8485 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8486 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8488 fraction = fraction * 2;
8493 /* Draw a piece on the screen without disturbing what's there */
8496 SelectGCMask(piece, clip, outline, mask)
8497 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8501 /* Bitmap for piece being moved. */
8502 if (appData.monoMode) {
8503 *mask = *pieceToSolid(piece);
8504 } else if (useImages) {
8506 *mask = xpmMask[piece];
8508 *mask = ximMaskPm[piece];
8511 *mask = *pieceToSolid(piece);
8514 /* GC for piece being moved. Square color doesn't matter, but
8515 since it gets modified we make a copy of the original. */
8517 if (appData.monoMode)
8522 if (appData.monoMode)
8527 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8529 /* Outline only used in mono mode and is not modified */
8531 *outline = bwPieceGC;
8533 *outline = wbPieceGC;
8537 OverlayPiece(piece, clip, outline, dest)
8538 ChessSquare piece; GC clip; GC outline; Drawable dest;
8543 /* Draw solid rectangle which will be clipped to shape of piece */
8544 XFillRectangle(xDisplay, dest, clip,
8545 0, 0, squareSize, squareSize);
8546 if (appData.monoMode)
8547 /* Also draw outline in contrasting color for black
8548 on black / white on white cases */
8549 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8550 0, 0, squareSize, squareSize, 0, 0, 1);
8552 /* Copy the piece */
8557 if(appData.upsideDown && flipView) kind ^= 2;
8558 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8560 0, 0, squareSize, squareSize,
8565 /* Animate the movement of a single piece */
8568 BeginAnimation(anim, piece, startColor, start)
8576 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8577 /* The old buffer is initialised with the start square (empty) */
8578 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8579 anim->prevFrame = *start;
8581 /* The piece will be drawn using its own bitmap as a matte */
8582 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8583 XSetClipMask(xDisplay, anim->pieceGC, mask);
8587 AnimationFrame(anim, frame, piece)
8592 XRectangle updates[4];
8597 /* Save what we are about to draw into the new buffer */
8598 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8599 frame->x, frame->y, squareSize, squareSize,
8602 /* Erase bits of the previous frame */
8603 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8604 /* Where the new frame overlapped the previous,
8605 the contents in newBuf are wrong. */
8606 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8607 overlap.x, overlap.y,
8608 overlap.width, overlap.height,
8610 /* Repaint the areas in the old that don't overlap new */
8611 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8612 for (i = 0; i < count; i++)
8613 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8614 updates[i].x - anim->prevFrame.x,
8615 updates[i].y - anim->prevFrame.y,
8616 updates[i].width, updates[i].height,
8617 updates[i].x, updates[i].y);
8619 /* Easy when no overlap */
8620 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8621 0, 0, squareSize, squareSize,
8622 anim->prevFrame.x, anim->prevFrame.y);
8625 /* Save this frame for next time round */
8626 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8627 0, 0, squareSize, squareSize,
8629 anim->prevFrame = *frame;
8631 /* Draw piece over original screen contents, not current,
8632 and copy entire rect. Wipes out overlapping piece images. */
8633 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8634 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8635 0, 0, squareSize, squareSize,
8636 frame->x, frame->y);
8640 EndAnimation (anim, finish)
8644 XRectangle updates[4];
8649 /* The main code will redraw the final square, so we
8650 only need to erase the bits that don't overlap. */
8651 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8652 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8653 for (i = 0; i < count; i++)
8654 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8655 updates[i].x - anim->prevFrame.x,
8656 updates[i].y - anim->prevFrame.y,
8657 updates[i].width, updates[i].height,
8658 updates[i].x, updates[i].y);
8660 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8661 0, 0, squareSize, squareSize,
8662 anim->prevFrame.x, anim->prevFrame.y);
8667 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8669 ChessSquare piece; int startColor;
8670 XPoint * start; XPoint * finish;
8671 XPoint frames[]; int nFrames;
8675 BeginAnimation(anim, piece, startColor, start);
8676 for (n = 0; n < nFrames; n++) {
8677 AnimationFrame(anim, &(frames[n]), piece);
8678 FrameDelay(appData.animSpeed);
8680 EndAnimation(anim, finish);
8684 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8687 ChessSquare piece = board[fromY][toY];
8688 board[fromY][toY] = EmptySquare;
8689 DrawPosition(FALSE, board);
8691 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8692 y = lineGap + toY * (squareSize + lineGap);
8694 x = lineGap + toX * (squareSize + lineGap);
8695 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8697 for(i=1; i<4*kFactor; i++) {
8698 int r = squareSize * 9 * i/(20*kFactor - 5);
8699 XFillArc(xDisplay, xBoardWindow, highlineGC,
8700 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8701 FrameDelay(appData.animSpeed);
8703 board[fromY][toY] = piece;
8706 /* Main control logic for deciding what to animate and how */
8709 AnimateMove(board, fromX, fromY, toX, toY)
8718 XPoint start, finish, mid;
8719 XPoint frames[kFactor * 2 + 1];
8720 int nFrames, startColor, endColor;
8722 /* Are we animating? */
8723 if (!appData.animate || appData.blindfold)
8726 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8727 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8728 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8730 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8731 piece = board[fromY][fromX];
8732 if (piece >= EmptySquare) return;
8737 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8740 if (appData.debugMode) {
8741 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8742 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8743 piece, fromX, fromY, toX, toY); }
8745 ScreenSquare(fromX, fromY, &start, &startColor);
8746 ScreenSquare(toX, toY, &finish, &endColor);
8749 /* Knight: make straight movement then diagonal */
8750 if (abs(toY - fromY) < abs(toX - fromX)) {
8751 mid.x = start.x + (finish.x - start.x) / 2;
8755 mid.y = start.y + (finish.y - start.y) / 2;
8758 mid.x = start.x + (finish.x - start.x) / 2;
8759 mid.y = start.y + (finish.y - start.y) / 2;
8762 /* Don't use as many frames for very short moves */
8763 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8764 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8766 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8767 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8768 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8770 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8771 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8774 /* Be sure end square is redrawn */
8775 damage[0][toY][toX] = True;
8779 DragPieceBegin(x, y)
8782 int boardX, boardY, color;
8785 /* Are we animating? */
8786 if (!appData.animateDragging || appData.blindfold)
8789 /* Figure out which square we start in and the
8790 mouse position relative to top left corner. */
8791 BoardSquare(x, y, &boardX, &boardY);
8792 player.startBoardX = boardX;
8793 player.startBoardY = boardY;
8794 ScreenSquare(boardX, boardY, &corner, &color);
8795 player.startSquare = corner;
8796 player.startColor = color;
8797 /* As soon as we start dragging, the piece will jump slightly to
8798 be centered over the mouse pointer. */
8799 player.mouseDelta.x = squareSize/2;
8800 player.mouseDelta.y = squareSize/2;
8801 /* Initialise animation */
8802 player.dragPiece = PieceForSquare(boardX, boardY);
8804 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8805 player.dragActive = True;
8806 BeginAnimation(&player, player.dragPiece, color, &corner);
8807 /* Mark this square as needing to be redrawn. Note that
8808 we don't remove the piece though, since logically (ie
8809 as seen by opponent) the move hasn't been made yet. */
8810 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8811 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8812 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8813 corner.x, corner.y, squareSize, squareSize,
8814 0, 0); // [HGM] zh: unstack in stead of grab
8815 if(gatingPiece != EmptySquare) {
8816 /* Kludge alert: When gating we want the introduced
8817 piece to appear on the from square. To generate an
8818 image of it, we draw it on the board, copy the image,
8819 and draw the original piece again. */
8820 ChessSquare piece = boards[currentMove][boardY][boardX];
8821 DrawSquare(boardY, boardX, gatingPiece, 0);
8822 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8823 corner.x, corner.y, squareSize, squareSize, 0, 0);
8824 DrawSquare(boardY, boardX, piece, 0);
8826 damage[0][boardY][boardX] = True;
8828 player.dragActive = False;
8838 /* Are we animating? */
8839 if (!appData.animateDragging || appData.blindfold)
8843 if (! player.dragActive)
8845 /* Move piece, maintaining same relative position
8846 of mouse within square */
8847 corner.x = x - player.mouseDelta.x;
8848 corner.y = y - player.mouseDelta.y;
8849 AnimationFrame(&player, &corner, player.dragPiece);
8851 if (appData.highlightDragging) {
8853 BoardSquare(x, y, &boardX, &boardY);
8854 SetHighlights(fromX, fromY, boardX, boardY);
8863 int boardX, boardY, color;
8866 /* Are we animating? */
8867 if (!appData.animateDragging || appData.blindfold)
8871 if (! player.dragActive)
8873 /* Last frame in sequence is square piece is
8874 placed on, which may not match mouse exactly. */
8875 BoardSquare(x, y, &boardX, &boardY);
8876 ScreenSquare(boardX, boardY, &corner, &color);
8877 EndAnimation(&player, &corner);
8879 /* Be sure end square is redrawn */
8880 damage[0][boardY][boardX] = True;
8882 /* This prevents weird things happening with fast successive
8883 clicks which on my Sun at least can cause motion events
8884 without corresponding press/release. */
8885 player.dragActive = False;
8888 /* Handle expose event while piece being dragged */
8893 if (!player.dragActive || appData.blindfold)
8896 /* What we're doing: logically, the move hasn't been made yet,
8897 so the piece is still in it's original square. But visually
8898 it's being dragged around the board. So we erase the square
8899 that the piece is on and draw it at the last known drag point. */
8900 BlankSquare(player.startSquare.x, player.startSquare.y,
8901 player.startColor, EmptySquare, xBoardWindow, 1);
8902 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8903 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8906 #include <sys/ioctl.h>
8907 int get_term_width()
8909 int fd, default_width;
8912 default_width = 79; // this is FICS default anyway...
8914 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8916 if (!ioctl(fd, TIOCGSIZE, &win))
8917 default_width = win.ts_cols;
8918 #elif defined(TIOCGWINSZ)
8920 if (!ioctl(fd, TIOCGWINSZ, &win))
8921 default_width = win.ws_col;
8923 return default_width;
8929 static int old_width = 0;
8930 int new_width = get_term_width();
8932 if (old_width != new_width)
8933 ics_printf("set width %d\n", new_width);
8934 old_width = new_width;
8937 void NotifyFrontendLogin()
8942 /* [AS] Arrow highlighting support */
8944 static double A_WIDTH = 5; /* Width of arrow body */
8946 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8947 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8949 static double Sqr( double x )
8954 static int Round( double x )
8956 return (int) (x + 0.5);
8959 void SquareToPos(int rank, int file, int *x, int *y)
8962 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8963 *y = lineGap + rank * (squareSize + lineGap);
8965 *x = lineGap + file * (squareSize + lineGap);
8966 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8970 /* Draw an arrow between two points using current settings */
8971 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8974 double dx, dy, j, k, x, y;
8977 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8979 arrow[0].x = s_x + A_WIDTH + 0.5;
8982 arrow[1].x = s_x + A_WIDTH + 0.5;
8983 arrow[1].y = d_y - h;
8985 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8986 arrow[2].y = d_y - h;
8991 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8992 arrow[5].y = d_y - h;
8994 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8995 arrow[4].y = d_y - h;
8997 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
9000 else if( d_y == s_y ) {
9001 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
9004 arrow[0].y = s_y + A_WIDTH + 0.5;
9006 arrow[1].x = d_x - w;
9007 arrow[1].y = s_y + A_WIDTH + 0.5;
9009 arrow[2].x = d_x - w;
9010 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9015 arrow[5].x = d_x - w;
9016 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
9018 arrow[4].x = d_x - w;
9019 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9022 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
9025 /* [AS] Needed a lot of paper for this! :-) */
9026 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
9027 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
9029 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
9031 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
9036 arrow[0].x = Round(x - j);
9037 arrow[0].y = Round(y + j*dx);
9039 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
9040 arrow[1].y = Round(arrow[0].y - 2*j*dx);
9043 x = (double) d_x - k;
9044 y = (double) d_y - k*dy;
9047 x = (double) d_x + k;
9048 y = (double) d_y + k*dy;
9051 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
9053 arrow[6].x = Round(x - j);
9054 arrow[6].y = Round(y + j*dx);
9056 arrow[2].x = Round(arrow[6].x + 2*j);
9057 arrow[2].y = Round(arrow[6].y - 2*j*dx);
9059 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
9060 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
9065 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9066 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9069 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9070 // Polygon( hdc, arrow, 7 );
9073 /* [AS] Draw an arrow between two squares */
9074 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9076 int s_x, s_y, d_x, d_y, hor, vert, i;
9078 if( s_col == d_col && s_row == d_row ) {
9082 /* Get source and destination points */
9083 SquareToPos( s_row, s_col, &s_x, &s_y);
9084 SquareToPos( d_row, d_col, &d_x, &d_y);
9087 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9089 else if( d_y < s_y ) {
9090 d_y += squareSize / 2 + squareSize / 4;
9093 d_y += squareSize / 2;
9097 d_x += squareSize / 2 - squareSize / 4;
9099 else if( d_x < s_x ) {
9100 d_x += squareSize / 2 + squareSize / 4;
9103 d_x += squareSize / 2;
9106 s_x += squareSize / 2;
9107 s_y += squareSize / 2;
9110 A_WIDTH = squareSize / 14.; //[HGM] make float
9112 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9114 hor = 64*s_col + 32; vert = 64*s_row + 32;
9115 for(i=0; i<= 64; i++) {
9116 damage[0][vert+6>>6][hor+6>>6] = True;
9117 damage[0][vert-6>>6][hor+6>>6] = True;
9118 damage[0][vert+6>>6][hor-6>>6] = True;
9119 damage[0][vert-6>>6][hor-6>>6] = True;
9120 hor += d_col - s_col; vert += d_row - s_row;
9124 Boolean IsDrawArrowEnabled()
9126 return appData.highlightMoveWithArrow && squareSize >= 32;
9129 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9131 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9132 DrawArrowBetweenSquares(fromX, fromY, toX, toY);