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 FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void GameListOptionsPopDown P(());
466 void ShufflePopDown P(());
467 void TimeControlPopDown P(());
468 void SettingsPopDown 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;
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 {"----", NULL, NothingProc},
651 {N_("Tags"), "Show Tags", EditTagsProc},
652 {N_("Comments"), "Show Comments", EditCommentProc},
653 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
654 {"----", NULL, NothingProc},
655 {N_("Board..."), "Board Options", BoardOptionsProc},
656 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
660 MenuItem modeMenu[] = {
661 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
662 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
663 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
664 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
665 {N_("Analyze File Ctrl+F"), "Analyze File", AnalyzeFileProc },
666 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
667 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
668 {N_("Training"), "Training", TrainingProc},
669 {N_("ICS Client"), "ICS Client", IcsClientProc},
670 {"----", NULL, NothingProc},
671 {N_("Machine Match"), "Machine Match", MatchProc},
672 {N_("Pause Pause"), "Pause", PauseProc},
676 MenuItem actionMenu[] = {
677 {N_("Accept F3"), "Accept", AcceptProc},
678 {N_("Decline F4"), "Decline", DeclineProc},
679 {N_("Rematch F12"), "Rematch", RematchProc},
680 {"----", NULL, NothingProc},
681 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
682 {N_("Draw F6"), "Draw", DrawProc},
683 {N_("Adjourn F7"), "Adjourn", AdjournProc},
684 {N_("Abort F8"),"Abort", AbortProc},
685 {N_("Resign F9"), "Resign", ResignProc},
686 {"----", NULL, NothingProc},
687 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
688 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
689 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
690 {"----", NULL, NothingProc},
691 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
692 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
693 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
697 MenuItem engineMenu[] = {
698 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
699 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
700 {"----", NULL, NothingProc},
701 {N_("Hint"), "Hint", HintProc},
702 {N_("Book"), "Book", BookProc},
703 {"----", NULL, NothingProc},
704 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
705 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
709 MenuItem optionsMenu[] = {
710 #define OPTIONSDIALOG
712 {N_("General ..."), "General", OptionsProc},
714 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
715 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
716 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
717 {N_("ICS ..."), "ICS", IcsOptionsProc},
718 {N_("Match ..."), "Match", MatchOptionsProc},
719 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
720 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
721 // {N_(" ..."), "", OptionsProc},
722 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
723 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
724 {"----", NULL, NothingProc},
725 #ifndef OPTIONSDIALOG
726 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
727 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
728 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
729 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
730 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
731 {N_("Blindfold"), "Blindfold", BlindfoldProc},
732 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
734 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
736 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
737 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
738 {N_("Move Sound"), "Move Sound", MoveSoundProc},
739 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
740 {N_("One-Click Moving"), "OneClick", OneClickProc},
741 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
742 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
743 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
744 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
745 // {N_("Premove"), "Premove", PremoveProc},
746 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
747 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
748 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
749 {"----", NULL, NothingProc},
751 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
752 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
756 MenuItem helpMenu[] = {
757 {N_("Info XBoard"), "Info XBoard", InfoProc},
758 {N_("Man XBoard F1"), "Man XBoard", ManProc},
759 {"----", NULL, NothingProc},
760 {N_("About XBoard"), "About XBoard", AboutProc},
765 {N_("File"), "File", fileMenu},
766 {N_("Edit"), "Edit", editMenu},
767 {N_("View"), "View", viewMenu},
768 {N_("Mode"), "Mode", modeMenu},
769 {N_("Action"), "Action", actionMenu},
770 {N_("Engine"), "Engine", engineMenu},
771 {N_("Options"), "Options", optionsMenu},
772 {N_("Help"), "Help", helpMenu},
776 #define PAUSE_BUTTON "P"
777 MenuItem buttonBar[] = {
778 {"<<", "<<", ToStartProc},
779 {"<", "<", BackwardProc},
780 {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
781 {">", ">", ForwardProc},
782 {">>", ">>", ToEndProc},
786 #define PIECE_MENU_SIZE 18
787 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
788 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
789 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
790 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
791 N_("Empty square"), N_("Clear board") },
792 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
793 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
794 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
795 N_("Empty square"), N_("Clear board") }
797 /* must be in same order as PieceMenuStrings! */
798 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
799 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
800 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
801 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
802 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
803 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
804 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
805 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
806 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
809 #define DROP_MENU_SIZE 6
810 String dropMenuStrings[DROP_MENU_SIZE] = {
811 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
813 /* must be in same order as PieceMenuStrings! */
814 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
815 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
816 WhiteRook, WhiteQueen
824 DropMenuEnables dmEnables[] = {
842 { XtNborderWidth, 0 },
843 { XtNdefaultDistance, 0 },
847 { XtNborderWidth, 0 },
848 { XtNresizable, (XtArgVal) True },
852 { XtNborderWidth, 0 },
858 { XtNjustify, (XtArgVal) XtJustifyRight },
859 { XtNlabel, (XtArgVal) "..." },
860 { XtNresizable, (XtArgVal) True },
861 { XtNresize, (XtArgVal) False }
864 Arg messageArgs[] = {
865 { XtNjustify, (XtArgVal) XtJustifyLeft },
866 { XtNlabel, (XtArgVal) "..." },
867 { XtNresizable, (XtArgVal) True },
868 { XtNresize, (XtArgVal) False }
872 { XtNborderWidth, 0 },
873 { XtNjustify, (XtArgVal) XtJustifyLeft }
876 XtResource clientResources[] = {
877 { "flashCount", "flashCount", XtRInt, sizeof(int),
878 XtOffset(AppDataPtr, flashCount), XtRImmediate,
879 (XtPointer) FLASH_COUNT },
882 XrmOptionDescRec shellOptions[] = {
883 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
884 { "-flash", "flashCount", XrmoptionNoArg, "3" },
885 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
888 XtActionsRec boardActions[] = {
889 { "DrawPosition", DrawPositionProc },
890 { "HandleUserMove", HandleUserMove },
891 { "AnimateUserMove", AnimateUserMove },
892 { "HandlePV", HandlePV },
893 { "SelectPV", SelectPV },
894 { "StopPV", StopPV },
895 { "FileNameAction", FileNameAction },
896 { "AskQuestionProc", AskQuestionProc },
897 { "AskQuestionReplyAction", AskQuestionReplyAction },
898 { "PieceMenuPopup", PieceMenuPopup },
899 { "WhiteClock", WhiteClock },
900 { "BlackClock", BlackClock },
901 { "Iconify", Iconify },
902 { "ResetProc", ResetProc },
903 { "NewVariantProc", NewVariantProc },
904 { "LoadGameProc", LoadGameProc },
905 { "LoadNextGameProc", LoadNextGameProc },
906 { "LoadPrevGameProc", LoadPrevGameProc },
907 { "LoadSelectedProc", LoadSelectedProc },
908 { "SetFilterProc", SetFilterProc },
909 { "ReloadGameProc", ReloadGameProc },
910 { "LoadPositionProc", LoadPositionProc },
911 { "LoadNextPositionProc", LoadNextPositionProc },
912 { "LoadPrevPositionProc", LoadPrevPositionProc },
913 { "ReloadPositionProc", ReloadPositionProc },
914 { "CopyPositionProc", CopyPositionProc },
915 { "PastePositionProc", PastePositionProc },
916 { "CopyGameProc", CopyGameProc },
917 { "PasteGameProc", PasteGameProc },
918 { "SaveGameProc", SaveGameProc },
919 { "SavePositionProc", SavePositionProc },
920 { "MailMoveProc", MailMoveProc },
921 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
922 { "QuitProc", QuitProc },
923 { "MachineWhiteProc", MachineWhiteProc },
924 { "MachineBlackProc", MachineBlackProc },
925 { "AnalysisModeProc", AnalyzeModeProc },
926 { "AnalyzeFileProc", AnalyzeFileProc },
927 { "TwoMachinesProc", TwoMachinesProc },
928 { "IcsClientProc", IcsClientProc },
929 { "EditGameProc", EditGameProc },
930 { "EditPositionProc", EditPositionProc },
931 { "TrainingProc", EditPositionProc },
932 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
933 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
934 { "ShowGameListProc", ShowGameListProc },
935 { "ShowMoveListProc", HistoryShowProc},
936 { "EditTagsProc", EditCommentProc },
937 { "EditCommentProc", EditCommentProc },
938 { "IcsInputBoxProc", IcsInputBoxProc },
939 { "PauseProc", PauseProc },
940 { "AcceptProc", AcceptProc },
941 { "DeclineProc", DeclineProc },
942 { "RematchProc", RematchProc },
943 { "CallFlagProc", CallFlagProc },
944 { "DrawProc", DrawProc },
945 { "AdjournProc", AdjournProc },
946 { "AbortProc", AbortProc },
947 { "ResignProc", ResignProc },
948 { "AdjuWhiteProc", AdjuWhiteProc },
949 { "AdjuBlackProc", AdjuBlackProc },
950 { "AdjuDrawProc", AdjuDrawProc },
951 { "EnterKeyProc", EnterKeyProc },
952 { "UpKeyProc", UpKeyProc },
953 { "DownKeyProc", DownKeyProc },
954 { "StopObservingProc", StopObservingProc },
955 { "StopExaminingProc", StopExaminingProc },
956 { "UploadProc", UploadProc },
957 { "BackwardProc", BackwardProc },
958 { "ForwardProc", ForwardProc },
959 { "ToStartProc", ToStartProc },
960 { "ToEndProc", ToEndProc },
961 { "RevertProc", RevertProc },
962 { "AnnotateProc", AnnotateProc },
963 { "TruncateGameProc", TruncateGameProc },
964 { "MoveNowProc", MoveNowProc },
965 { "RetractMoveProc", RetractMoveProc },
966 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
967 { "UciMenuProc", (XtActionProc) UciMenuProc },
968 { "TimeControlProc", (XtActionProc) TimeControlProc },
969 { "FlipViewProc", FlipViewProc },
970 { "PonderNextMoveProc", PonderNextMoveProc },
971 #ifndef OPTIONSDIALOG
972 { "AlwaysQueenProc", AlwaysQueenProc },
973 { "AnimateDraggingProc", AnimateDraggingProc },
974 { "AnimateMovingProc", AnimateMovingProc },
975 { "AutoflagProc", AutoflagProc },
976 { "AutoflipProc", AutoflipProc },
977 { "BlindfoldProc", BlindfoldProc },
978 { "FlashMovesProc", FlashMovesProc },
980 { "HighlightDraggingProc", HighlightDraggingProc },
982 { "HighlightLastMoveProc", HighlightLastMoveProc },
983 // { "IcsAlarmProc", IcsAlarmProc },
984 { "MoveSoundProc", MoveSoundProc },
985 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
986 { "PopupExitMessageProc", PopupExitMessageProc },
987 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
988 // { "PremoveProc", PremoveProc },
989 { "ShowCoordsProc", ShowCoordsProc },
990 { "ShowThinkingProc", ShowThinkingProc },
991 { "HideThinkingProc", HideThinkingProc },
992 { "TestLegalityProc", TestLegalityProc },
994 { "SaveSettingsProc", SaveSettingsProc },
995 { "SaveOnExitProc", SaveOnExitProc },
996 { "InfoProc", InfoProc },
997 { "ManProc", ManProc },
998 { "HintProc", HintProc },
999 { "BookProc", BookProc },
1000 { "AboutGameProc", AboutGameProc },
1001 { "AboutProc", AboutProc },
1002 { "DebugProc", DebugProc },
1003 { "NothingProc", NothingProc },
1004 { "CommentClick", (XtActionProc) CommentClick },
1005 { "CommentPopDown", (XtActionProc) CommentPopDown },
1006 { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
1007 { "TagsPopDown", (XtActionProc) TagsPopDown },
1008 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1009 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1010 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1011 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1012 { "GameListPopDown", (XtActionProc) GameListPopDown },
1013 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1014 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1015 { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1016 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1017 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1018 { "ShufflePopDown", (XtActionProc) ShufflePopDown },
1019 { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
1020 { "SettingsPopDown", (XtActionProc) SettingsPopDown },
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 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2091 /* [HR] height treated separately (hacked) */
2092 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2093 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2094 if (appData.showJail == 1) {
2095 /* Jail on top and bottom */
2096 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2097 XtSetArg(boardArgs[2], XtNheight,
2098 boardHeight + 2*(lineGap + squareSize));
2099 } else if (appData.showJail == 2) {
2101 XtSetArg(boardArgs[1], XtNwidth,
2102 boardWidth + 2*(lineGap + squareSize));
2103 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2106 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2107 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2111 * Determine what fonts to use.
2113 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2114 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2115 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2116 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2117 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2118 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2119 appData.font = FindFont(appData.font, fontPxlSize);
2120 countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2121 countFontStruct = XQueryFont(xDisplay, countFontID);
2122 // appData.font = FindFont(appData.font, fontPxlSize);
2124 xdb = XtDatabase(xDisplay);
2125 XrmPutStringResource(&xdb, "*font", appData.font);
2128 * Detect if there are not enough colors available and adapt.
2130 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2131 appData.monoMode = True;
2134 forceMono = MakeColors();
2137 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2140 if (appData.bitmapDirectory == NULL ||
2141 appData.bitmapDirectory[0] == NULLCHAR)
2142 appData.bitmapDirectory = DEF_BITMAP_DIR;
2145 if (appData.lowTimeWarning && !appData.monoMode) {
2146 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2147 vFrom.size = strlen(appData.lowTimeWarningColor);
2148 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2149 if (vTo.addr == NULL)
2150 appData.monoMode = True;
2152 lowTimeWarningColor = *(Pixel *) vTo.addr;
2155 if (appData.monoMode && appData.debugMode) {
2156 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2157 (unsigned long) XWhitePixel(xDisplay, xScreen),
2158 (unsigned long) XBlackPixel(xDisplay, xScreen));
2161 ParseIcsTextColors();
2162 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2163 textColors[ColorNone].attr = 0;
2165 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2171 layoutName = "tinyLayout";
2172 } else if (smallLayout) {
2173 layoutName = "smallLayout";
2175 layoutName = "normalLayout";
2177 /* Outer layoutWidget is there only to provide a name for use in
2178 resources that depend on the layout style */
2180 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2181 layoutArgs, XtNumber(layoutArgs));
2183 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2184 formArgs, XtNumber(formArgs));
2185 XtSetArg(args[0], XtNdefaultDistance, &sep);
2186 XtGetValues(formWidget, args, 1);
2189 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2190 XtSetArg(args[0], XtNtop, XtChainTop);
2191 XtSetArg(args[1], XtNbottom, XtChainTop);
2192 XtSetArg(args[2], XtNright, XtChainLeft);
2193 XtSetValues(menuBarWidget, args, 3);
2195 widgetList[j++] = whiteTimerWidget =
2196 XtCreateWidget("whiteTime", labelWidgetClass,
2197 formWidget, timerArgs, XtNumber(timerArgs));
2198 XtSetArg(args[0], XtNfont, clockFontStruct);
2199 XtSetArg(args[1], XtNtop, XtChainTop);
2200 XtSetArg(args[2], XtNbottom, XtChainTop);
2201 XtSetValues(whiteTimerWidget, args, 3);
2203 widgetList[j++] = blackTimerWidget =
2204 XtCreateWidget("blackTime", labelWidgetClass,
2205 formWidget, timerArgs, XtNumber(timerArgs));
2206 XtSetArg(args[0], XtNfont, clockFontStruct);
2207 XtSetArg(args[1], XtNtop, XtChainTop);
2208 XtSetArg(args[2], XtNbottom, XtChainTop);
2209 XtSetValues(blackTimerWidget, args, 3);
2211 if (appData.titleInWindow) {
2212 widgetList[j++] = titleWidget =
2213 XtCreateWidget("title", labelWidgetClass, formWidget,
2214 titleArgs, XtNumber(titleArgs));
2215 XtSetArg(args[0], XtNtop, XtChainTop);
2216 XtSetArg(args[1], XtNbottom, XtChainTop);
2217 XtSetValues(titleWidget, args, 2);
2220 if (appData.showButtonBar) {
2221 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2222 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2223 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2224 XtSetArg(args[2], XtNtop, XtChainTop);
2225 XtSetArg(args[3], XtNbottom, XtChainTop);
2226 XtSetValues(buttonBarWidget, args, 4);
2229 widgetList[j++] = messageWidget =
2230 XtCreateWidget("message", labelWidgetClass, formWidget,
2231 messageArgs, XtNumber(messageArgs));
2232 XtSetArg(args[0], XtNtop, XtChainTop);
2233 XtSetArg(args[1], XtNbottom, XtChainTop);
2234 XtSetValues(messageWidget, args, 2);
2236 widgetList[j++] = boardWidget =
2237 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2238 XtNumber(boardArgs));
2240 XtManageChildren(widgetList, j);
2242 timerWidth = (boardWidth - sep) / 2;
2243 XtSetArg(args[0], XtNwidth, timerWidth);
2244 XtSetValues(whiteTimerWidget, args, 1);
2245 XtSetValues(blackTimerWidget, args, 1);
2247 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2248 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2249 XtGetValues(whiteTimerWidget, args, 2);
2251 if (appData.showButtonBar) {
2252 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2253 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2254 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2258 * formWidget uses these constraints but they are stored
2262 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2263 XtSetValues(menuBarWidget, args, i);
2264 if (appData.titleInWindow) {
2267 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2268 XtSetValues(whiteTimerWidget, args, i);
2270 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2271 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2272 XtSetValues(blackTimerWidget, args, i);
2274 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2275 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2276 XtSetValues(titleWidget, args, i);
2278 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2279 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2280 XtSetValues(messageWidget, args, i);
2281 if (appData.showButtonBar) {
2283 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2284 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2285 XtSetValues(buttonBarWidget, args, i);
2289 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2290 XtSetValues(whiteTimerWidget, args, i);
2292 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2293 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2294 XtSetValues(blackTimerWidget, args, i);
2296 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2297 XtSetValues(titleWidget, args, i);
2299 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2300 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2301 XtSetValues(messageWidget, args, i);
2302 if (appData.showButtonBar) {
2304 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2305 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2306 XtSetValues(buttonBarWidget, args, i);
2311 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2312 XtSetValues(whiteTimerWidget, args, i);
2314 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2315 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2316 XtSetValues(blackTimerWidget, args, i);
2318 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2319 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2320 XtSetValues(messageWidget, args, i);
2321 if (appData.showButtonBar) {
2323 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2324 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2325 XtSetValues(buttonBarWidget, args, i);
2329 XtSetArg(args[0], XtNfromVert, messageWidget);
2330 XtSetArg(args[1], XtNtop, XtChainTop);
2331 XtSetArg(args[2], XtNbottom, XtChainBottom);
2332 XtSetArg(args[3], XtNleft, XtChainLeft);
2333 XtSetArg(args[4], XtNright, XtChainRight);
2334 XtSetValues(boardWidget, args, 5);
2336 XtRealizeWidget(shellWidget);
2339 XtSetArg(args[0], XtNx, wpMain.x);
2340 XtSetArg(args[1], XtNy, wpMain.y);
2341 XtSetValues(shellWidget, args, 2);
2345 * Correct the width of the message and title widgets.
2346 * It is not known why some systems need the extra fudge term.
2347 * The value "2" is probably larger than needed.
2349 XawFormDoLayout(formWidget, False);
2351 #define WIDTH_FUDGE 2
2353 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2354 XtSetArg(args[i], XtNheight, &h); i++;
2355 XtGetValues(messageWidget, args, i);
2356 if (appData.showButtonBar) {
2358 XtSetArg(args[i], XtNwidth, &w); i++;
2359 XtGetValues(buttonBarWidget, args, i);
2360 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2362 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2365 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2366 if (gres != XtGeometryYes && appData.debugMode) {
2367 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2368 programName, gres, w, h, wr, hr);
2371 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2372 /* The size used for the child widget in layout lags one resize behind
2373 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2375 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2376 if (gres != XtGeometryYes && appData.debugMode) {
2377 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2378 programName, gres, w, h, wr, hr);
2381 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2382 XtSetArg(args[1], XtNright, XtChainRight);
2383 XtSetValues(messageWidget, args, 2);
2385 if (appData.titleInWindow) {
2387 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2388 XtSetArg(args[i], XtNheight, &h); i++;
2389 XtGetValues(titleWidget, args, i);
2391 w = boardWidth - 2*bor;
2393 XtSetArg(args[0], XtNwidth, &w);
2394 XtGetValues(menuBarWidget, args, 1);
2395 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2398 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2399 if (gres != XtGeometryYes && appData.debugMode) {
2401 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2402 programName, gres, w, h, wr, hr);
2405 XawFormDoLayout(formWidget, True);
2407 xBoardWindow = XtWindow(boardWidget);
2409 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2410 // not need to go into InitDrawingSizes().
2414 * Create X checkmark bitmap and initialize option menu checks.
2416 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2417 checkmark_bits, checkmark_width, checkmark_height);
2418 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2419 #ifndef OPTIONSDIALOG
2420 if (appData.alwaysPromoteToQueen) {
2421 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2424 if (appData.animateDragging) {
2425 XtSetValues(XtNameToWidget(menuBarWidget,
2426 "menuOptions.Animate Dragging"),
2429 if (appData.animate) {
2430 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2433 if (appData.autoCallFlag) {
2434 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2437 if (appData.autoFlipView) {
2438 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2441 if (appData.blindfold) {
2442 XtSetValues(XtNameToWidget(menuBarWidget,
2443 "menuOptions.Blindfold"), args, 1);
2445 if (appData.flashCount > 0) {
2446 XtSetValues(XtNameToWidget(menuBarWidget,
2447 "menuOptions.Flash Moves"),
2451 if (appData.highlightDragging) {
2452 XtSetValues(XtNameToWidget(menuBarWidget,
2453 "menuOptions.Highlight Dragging"),
2457 if (appData.highlightLastMove) {
2458 XtSetValues(XtNameToWidget(menuBarWidget,
2459 "menuOptions.Highlight Last Move"),
2462 if (appData.highlightMoveWithArrow) {
2463 XtSetValues(XtNameToWidget(menuBarWidget,
2464 "menuOptions.Arrow"),
2467 // if (appData.icsAlarm) {
2468 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2471 if (appData.ringBellAfterMoves) {
2472 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2475 if (appData.oneClick) {
2476 XtSetValues(XtNameToWidget(menuBarWidget,
2477 "menuOptions.OneClick"), args, 1);
2479 if (appData.periodicUpdates) {
2480 XtSetValues(XtNameToWidget(menuBarWidget,
2481 "menuOptions.Periodic Updates"), args, 1);
2483 if (appData.ponderNextMove) {
2484 XtSetValues(XtNameToWidget(menuBarWidget,
2485 "menuOptions.Ponder Next Move"), args, 1);
2487 if (appData.popupExitMessage) {
2488 XtSetValues(XtNameToWidget(menuBarWidget,
2489 "menuOptions.Popup Exit Message"), args, 1);
2491 if (appData.popupMoveErrors) {
2492 XtSetValues(XtNameToWidget(menuBarWidget,
2493 "menuOptions.Popup Move Errors"), args, 1);
2495 // if (appData.premove) {
2496 // XtSetValues(XtNameToWidget(menuBarWidget,
2497 // "menuOptions.Premove"), args, 1);
2499 if (appData.showCoords) {
2500 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2503 if (appData.hideThinkingFromHuman) {
2504 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2507 if (appData.testLegality) {
2508 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2512 if (saveSettingsOnExit) {
2513 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2520 ReadBitmap(&wIconPixmap, "icon_white.bm",
2521 icon_white_bits, icon_white_width, icon_white_height);
2522 ReadBitmap(&bIconPixmap, "icon_black.bm",
2523 icon_black_bits, icon_black_width, icon_black_height);
2524 iconPixmap = wIconPixmap;
2526 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2527 XtSetValues(shellWidget, args, i);
2530 * Create a cursor for the board widget.
2532 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2533 XChangeWindowAttributes(xDisplay, xBoardWindow,
2534 CWCursor, &window_attributes);
2537 * Inhibit shell resizing.
2539 shellArgs[0].value = (XtArgVal) &w;
2540 shellArgs[1].value = (XtArgVal) &h;
2541 XtGetValues(shellWidget, shellArgs, 2);
2542 shellArgs[4].value = shellArgs[2].value = w;
2543 shellArgs[5].value = shellArgs[3].value = h;
2544 XtSetValues(shellWidget, &shellArgs[2], 4);
2545 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2546 marginH = h - boardHeight;
2548 CatchDeleteWindow(shellWidget, "QuitProc");
2553 if (appData.bitmapDirectory[0] != NULLCHAR) {
2557 CreateXPMBoard(appData.liteBackTextureFile, 1);
2558 CreateXPMBoard(appData.darkBackTextureFile, 0);
2562 /* Create regular pieces */
2563 if (!useImages) CreatePieces();
2568 if (appData.animate || appData.animateDragging)
2571 XtAugmentTranslations(formWidget,
2572 XtParseTranslationTable(globalTranslations));
2573 XtAugmentTranslations(boardWidget,
2574 XtParseTranslationTable(boardTranslations));
2575 XtAugmentTranslations(whiteTimerWidget,
2576 XtParseTranslationTable(whiteTranslations));
2577 XtAugmentTranslations(blackTimerWidget,
2578 XtParseTranslationTable(blackTranslations));
2580 /* Why is the following needed on some versions of X instead
2581 * of a translation? */
2582 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2583 (XtEventHandler) EventProc, NULL);
2586 /* [AS] Restore layout */
2587 if( wpMoveHistory.visible ) {
2591 if( wpEvalGraph.visible )
2596 if( wpEngineOutput.visible ) {
2597 EngineOutputPopUp();
2602 if (errorExitStatus == -1) {
2603 if (appData.icsActive) {
2604 /* We now wait until we see "login:" from the ICS before
2605 sending the logon script (problems with timestamp otherwise) */
2606 /*ICSInitScript();*/
2607 if (appData.icsInputBox) ICSInputBoxPopUp();
2611 signal(SIGWINCH, TermSizeSigHandler);
2613 signal(SIGINT, IntSigHandler);
2614 signal(SIGTERM, IntSigHandler);
2615 if (*appData.cmailGameName != NULLCHAR) {
2616 signal(SIGUSR1, CmailSigHandler);
2619 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2621 XtSetKeyboardFocus(shellWidget, formWidget);
2623 XtAppMainLoop(appContext);
2624 if (appData.debugMode) fclose(debugFP); // [DM] debug
2631 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2632 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2634 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2635 unlink(gameCopyFilename);
2636 unlink(gamePasteFilename);
2639 RETSIGTYPE TermSizeSigHandler(int sig)
2652 CmailSigHandler(sig)
2658 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2660 /* Activate call-back function CmailSigHandlerCallBack() */
2661 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2663 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2667 CmailSigHandlerCallBack(isr, closure, message, count, error)
2675 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2677 /**** end signal code ****/
2683 /* try to open the icsLogon script, either in the location given
2684 * or in the users HOME directory
2691 f = fopen(appData.icsLogon, "r");
2694 homedir = getenv("HOME");
2695 if (homedir != NULL)
2697 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2698 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2699 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2700 f = fopen(buf, "r");
2705 ProcessICSInitScript(f);
2707 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2716 EditCommentPopDown();
2731 if (!menuBarWidget) return;
2732 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2734 DisplayError("menuEdit.Revert", 0);
2736 XtSetSensitive(w, !grey);
2738 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2740 DisplayError("menuEdit.Annotate", 0);
2742 XtSetSensitive(w, !grey);
2747 SetMenuEnables(enab)
2751 if (!menuBarWidget) return;
2752 while (enab->name != NULL) {
2753 w = XtNameToWidget(menuBarWidget, enab->name);
2755 DisplayError(enab->name, 0);
2757 XtSetSensitive(w, enab->value);
2763 Enables icsEnables[] = {
2764 { "menuFile.Mail Move", False },
2765 { "menuFile.Reload CMail Message", False },
2766 { "menuMode.Machine Black", False },
2767 { "menuMode.Machine White", False },
2768 { "menuMode.Analysis Mode", False },
2769 { "menuMode.Analyze File", False },
2770 { "menuMode.Two Machines", False },
2771 { "menuMode.Machine Match", False },
2773 { "menuEngine.Hint", False },
2774 { "menuEngine.Book", False },
2775 { "menuEngine.Move Now", False },
2776 #ifndef OPTIONSDIALOG
2777 { "menuOptions.Periodic Updates", False },
2778 { "menuOptions.Hide Thinking", False },
2779 { "menuOptions.Ponder Next Move", False },
2781 { "menuEngine.Engine #1 Settings", False },
2783 { "menuEngine.Engine #2 Settings", False },
2784 { "menuEdit.Annotate", False },
2788 Enables ncpEnables[] = {
2789 { "menuFile.Mail Move", False },
2790 { "menuFile.Reload CMail Message", False },
2791 { "menuMode.Machine White", False },
2792 { "menuMode.Machine Black", False },
2793 { "menuMode.Analysis Mode", False },
2794 { "menuMode.Analyze File", False },
2795 { "menuMode.Two Machines", False },
2796 { "menuMode.Machine Match", False },
2797 { "menuMode.ICS Client", False },
2798 { "menuView.ICS Input Box", False },
2799 { "Action", False },
2800 { "menuEdit.Revert", False },
2801 { "menuEdit.Annotate", False },
2802 { "menuEngine.Engine #1 Settings", False },
2803 { "menuEngine.Engine #2 Settings", False },
2804 { "menuEngine.Move Now", False },
2805 { "menuEngine.Retract Move", False },
2806 #ifndef OPTIONSDIALOG
2807 { "menuOptions.Auto Flag", False },
2808 { "menuOptions.Auto Flip View", False },
2809 { "menuOptions.ICS", False },
2810 // { "menuOptions.ICS Alarm", False },
2811 { "menuOptions.Move Sound", False },
2812 { "menuOptions.Hide Thinking", False },
2813 { "menuOptions.Periodic Updates", False },
2814 { "menuOptions.Ponder Next Move", False },
2816 { "menuEngine.Hint", False },
2817 { "menuEngine.Book", False },
2821 Enables gnuEnables[] = {
2822 { "menuMode.ICS Client", False },
2823 { "menuView.ICS Input Box", False },
2824 { "menuAction.Accept", False },
2825 { "menuAction.Decline", False },
2826 { "menuAction.Rematch", False },
2827 { "menuAction.Adjourn", False },
2828 { "menuAction.Stop Examining", False },
2829 { "menuAction.Stop Observing", False },
2830 { "menuAction.Upload to Examine", False },
2831 { "menuEdit.Revert", False },
2832 { "menuEdit.Annotate", False },
2833 { "menuOptions.ICS", False },
2835 /* The next two options rely on SetCmailMode being called *after* */
2836 /* SetGNUMode so that when GNU is being used to give hints these */
2837 /* menu options are still available */
2839 { "menuFile.Mail Move", False },
2840 { "menuFile.Reload CMail Message", False },
2844 Enables cmailEnables[] = {
2846 { "menuAction.Call Flag", False },
2847 { "menuAction.Draw", True },
2848 { "menuAction.Adjourn", False },
2849 { "menuAction.Abort", False },
2850 { "menuAction.Stop Observing", False },
2851 { "menuAction.Stop Examining", False },
2852 { "menuFile.Mail Move", True },
2853 { "menuFile.Reload CMail Message", True },
2857 Enables trainingOnEnables[] = {
2858 { "menuMode.Edit Comment", False },
2859 { "menuMode.Pause", False },
2860 { "menuEdit.Forward", False },
2861 { "menuEdit.Backward", False },
2862 { "menuEdit.Forward to End", False },
2863 { "menuEdit.Back to Start", False },
2864 { "menuEngine.Move Now", False },
2865 { "menuEdit.Truncate Game", False },
2869 Enables trainingOffEnables[] = {
2870 { "menuMode.Edit Comment", True },
2871 { "menuMode.Pause", True },
2872 { "menuEdit.Forward", True },
2873 { "menuEdit.Backward", True },
2874 { "menuEdit.Forward to End", True },
2875 { "menuEdit.Back to Start", True },
2876 { "menuEngine.Move Now", True },
2877 { "menuEdit.Truncate Game", True },
2881 Enables machineThinkingEnables[] = {
2882 { "menuFile.Load Game", False },
2883 // { "menuFile.Load Next Game", False },
2884 // { "menuFile.Load Previous Game", False },
2885 // { "menuFile.Reload Same Game", False },
2886 { "menuEdit.Paste Game", False },
2887 { "menuFile.Load Position", False },
2888 // { "menuFile.Load Next Position", False },
2889 // { "menuFile.Load Previous Position", False },
2890 // { "menuFile.Reload Same Position", False },
2891 { "menuEdit.Paste Position", False },
2892 { "menuMode.Machine White", False },
2893 { "menuMode.Machine Black", False },
2894 { "menuMode.Two Machines", False },
2895 { "menuMode.Machine Match", False },
2896 { "menuEngine.Retract Move", False },
2900 Enables userThinkingEnables[] = {
2901 { "menuFile.Load Game", True },
2902 // { "menuFile.Load Next Game", True },
2903 // { "menuFile.Load Previous Game", True },
2904 // { "menuFile.Reload Same Game", True },
2905 { "menuEdit.Paste Game", True },
2906 { "menuFile.Load Position", True },
2907 // { "menuFile.Load Next Position", True },
2908 // { "menuFile.Load Previous Position", True },
2909 // { "menuFile.Reload Same Position", True },
2910 { "menuEdit.Paste Position", True },
2911 { "menuMode.Machine White", True },
2912 { "menuMode.Machine Black", True },
2913 { "menuMode.Two Machines", True },
2914 { "menuMode.Machine Match", True },
2915 { "menuEngine.Retract Move", True },
2921 SetMenuEnables(icsEnables);
2924 if (appData.zippyPlay && !appData.noChessProgram) /* [DM] icsEngineAnalyze */
2925 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2932 SetMenuEnables(ncpEnables);
2938 SetMenuEnables(gnuEnables);
2944 SetMenuEnables(cmailEnables);
2950 SetMenuEnables(trainingOnEnables);
2951 if (appData.showButtonBar) {
2952 XtSetSensitive(buttonBarWidget, False);
2958 SetTrainingModeOff()
2960 SetMenuEnables(trainingOffEnables);
2961 if (appData.showButtonBar) {
2962 XtSetSensitive(buttonBarWidget, True);
2967 SetUserThinkingEnables()
2969 if (appData.noChessProgram) return;
2970 SetMenuEnables(userThinkingEnables);
2974 SetMachineThinkingEnables()
2976 if (appData.noChessProgram) return;
2977 SetMenuEnables(machineThinkingEnables);
2979 case MachinePlaysBlack:
2980 case MachinePlaysWhite:
2981 case TwoMachinesPlay:
2982 XtSetSensitive(XtNameToWidget(menuBarWidget,
2983 ModeToWidgetName(gameMode)), True);
2990 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2991 #define HISTORY_SIZE 64
2992 static char *history[HISTORY_SIZE];
2993 int histIn = 0, histP = 0;
2996 SaveInHistory(char *cmd)
2998 if (history[histIn] != NULL) {
2999 free(history[histIn]);
3000 history[histIn] = NULL;
3002 if (*cmd == NULLCHAR) return;
3003 history[histIn] = StrSave(cmd);
3004 histIn = (histIn + 1) % HISTORY_SIZE;
3005 if (history[histIn] != NULL) {
3006 free(history[histIn]);
3007 history[histIn] = NULL;
3013 PrevInHistory(char *cmd)
3016 if (histP == histIn) {
3017 if (history[histIn] != NULL) free(history[histIn]);
3018 history[histIn] = StrSave(cmd);
3020 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3021 if (newhp == histIn || history[newhp] == NULL) return NULL;
3023 return history[histP];
3029 if (histP == histIn) return NULL;
3030 histP = (histP + 1) % HISTORY_SIZE;
3031 return history[histP];
3033 // end of borrowed code
3035 #define Abs(n) ((n)<0 ? -(n) : (n))
3038 * Find a font that matches "pattern" that is as close as
3039 * possible to the targetPxlSize. Prefer fonts that are k
3040 * pixels smaller to fonts that are k pixels larger. The
3041 * pattern must be in the X Consortium standard format,
3042 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3043 * The return value should be freed with XtFree when no
3047 FindFont(pattern, targetPxlSize)
3051 char **fonts, *p, *best, *scalable, *scalableTail;
3052 int i, j, nfonts, minerr, err, pxlSize;
3055 char **missing_list;
3057 char *def_string, *base_fnt_lst, strInt[3];
3059 XFontStruct **fnt_list;
3061 base_fnt_lst = calloc(1, strlen(pattern) + 3);
3062 snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3063 p = strstr(pattern, "--");
3064 strncpy(base_fnt_lst, pattern, p - pattern + 2);
3065 strcat(base_fnt_lst, strInt);
3066 strcat(base_fnt_lst, strchr(p + 2, '-'));
3068 if ((fntSet = XCreateFontSet(xDisplay,
3072 &def_string)) == NULL) {
3074 fprintf(stderr, _("Unable to create font set.\n"));
3078 nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3080 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3082 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3083 programName, pattern);
3091 for (i=0; i<nfonts; i++) {
3094 if (*p != '-') continue;
3096 if (*p == NULLCHAR) break;
3097 if (*p++ == '-') j++;
3099 if (j < 7) continue;
3102 scalable = fonts[i];
3105 err = pxlSize - targetPxlSize;
3106 if (Abs(err) < Abs(minerr) ||
3107 (minerr > 0 && err < 0 && -err == minerr)) {
3113 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3114 /* If the error is too big and there is a scalable font,
3115 use the scalable font. */
3116 int headlen = scalableTail - scalable;
3117 p = (char *) XtMalloc(strlen(scalable) + 10);
3118 while (isdigit(*scalableTail)) scalableTail++;
3119 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3121 p = (char *) XtMalloc(strlen(best) + 2);
3122 safeStrCpy(p, best, strlen(best)+1 );
3124 if (appData.debugMode) {
3125 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3126 pattern, targetPxlSize, p);
3129 if (missing_count > 0)
3130 XFreeStringList(missing_list);
3131 XFreeFontSet(xDisplay, fntSet);
3133 XFreeFontNames(fonts);
3139 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3140 // must be called before all non-first callse to CreateGCs()
3141 XtReleaseGC(shellWidget, highlineGC);
3142 XtReleaseGC(shellWidget, lightSquareGC);
3143 XtReleaseGC(shellWidget, darkSquareGC);
3144 if (appData.monoMode) {
3145 if (DefaultDepth(xDisplay, xScreen) == 1) {
3146 XtReleaseGC(shellWidget, wbPieceGC);
3148 XtReleaseGC(shellWidget, bwPieceGC);
3151 XtReleaseGC(shellWidget, prelineGC);
3152 XtReleaseGC(shellWidget, jailSquareGC);
3153 XtReleaseGC(shellWidget, wdPieceGC);
3154 XtReleaseGC(shellWidget, wlPieceGC);
3155 XtReleaseGC(shellWidget, wjPieceGC);
3156 XtReleaseGC(shellWidget, bdPieceGC);
3157 XtReleaseGC(shellWidget, blPieceGC);
3158 XtReleaseGC(shellWidget, bjPieceGC);
3162 void CreateGCs(int redo)
3164 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3165 | GCBackground | GCFunction | GCPlaneMask;
3166 XGCValues gc_values;
3169 gc_values.plane_mask = AllPlanes;
3170 gc_values.line_width = lineGap;
3171 gc_values.line_style = LineSolid;
3172 gc_values.function = GXcopy;
3175 DeleteGCs(); // called a second time; clean up old GCs first
3176 } else { // [HGM] grid and font GCs created on first call only
3177 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3178 gc_values.background = XBlackPixel(xDisplay, xScreen);
3179 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
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 if (appData.monoMode) {
3193 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3194 gc_values.background = XWhitePixel(xDisplay, xScreen);
3195 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3197 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3198 gc_values.background = XBlackPixel(xDisplay, xScreen);
3199 lightSquareGC = wbPieceGC
3200 = XtGetGC(shellWidget, value_mask, &gc_values);
3202 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3203 gc_values.background = XWhitePixel(xDisplay, xScreen);
3204 darkSquareGC = bwPieceGC
3205 = XtGetGC(shellWidget, value_mask, &gc_values);
3207 if (DefaultDepth(xDisplay, xScreen) == 1) {
3208 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3209 gc_values.function = GXcopyInverted;
3210 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3211 gc_values.function = GXcopy;
3212 if (XBlackPixel(xDisplay, xScreen) == 1) {
3213 bwPieceGC = darkSquareGC;
3214 wbPieceGC = copyInvertedGC;
3216 bwPieceGC = copyInvertedGC;
3217 wbPieceGC = lightSquareGC;
3221 gc_values.foreground = highlightSquareColor;
3222 gc_values.background = highlightSquareColor;
3223 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3225 gc_values.foreground = premoveHighlightColor;
3226 gc_values.background = premoveHighlightColor;
3227 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3229 gc_values.foreground = lightSquareColor;
3230 gc_values.background = darkSquareColor;
3231 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3233 gc_values.foreground = darkSquareColor;
3234 gc_values.background = lightSquareColor;
3235 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3237 gc_values.foreground = jailSquareColor;
3238 gc_values.background = jailSquareColor;
3239 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3241 gc_values.foreground = whitePieceColor;
3242 gc_values.background = darkSquareColor;
3243 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3245 gc_values.foreground = whitePieceColor;
3246 gc_values.background = lightSquareColor;
3247 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3249 gc_values.foreground = whitePieceColor;
3250 gc_values.background = jailSquareColor;
3251 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3253 gc_values.foreground = blackPieceColor;
3254 gc_values.background = darkSquareColor;
3255 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3257 gc_values.foreground = blackPieceColor;
3258 gc_values.background = lightSquareColor;
3259 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3261 gc_values.foreground = blackPieceColor;
3262 gc_values.background = jailSquareColor;
3263 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3267 void loadXIM(xim, xmask, filename, dest, mask)
3280 fp = fopen(filename, "rb");
3282 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3289 for (y=0; y<h; ++y) {
3290 for (x=0; x<h; ++x) {
3295 XPutPixel(xim, x, y, blackPieceColor);
3297 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3300 XPutPixel(xim, x, y, darkSquareColor);
3302 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3305 XPutPixel(xim, x, y, whitePieceColor);
3307 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3310 XPutPixel(xim, x, y, lightSquareColor);
3312 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3320 /* create Pixmap of piece */
3321 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3323 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3326 /* create Pixmap of clipmask
3327 Note: We assume the white/black pieces have the same
3328 outline, so we make only 6 masks. This is okay
3329 since the XPM clipmask routines do the same. */
3331 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3333 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3336 /* now create the 1-bit version */
3337 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3340 values.foreground = 1;
3341 values.background = 0;
3343 /* Don't use XtGetGC, not read only */
3344 maskGC = XCreateGC(xDisplay, *mask,
3345 GCForeground | GCBackground, &values);
3346 XCopyPlane(xDisplay, temp, *mask, maskGC,
3347 0, 0, squareSize, squareSize, 0, 0, 1);
3348 XFreePixmap(xDisplay, temp);
3353 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3355 void CreateXIMPieces()
3360 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3365 /* The XSynchronize calls were copied from CreatePieces.
3366 Not sure if needed, but can't hurt */
3367 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3370 /* temp needed by loadXIM() */
3371 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3372 0, 0, ss, ss, AllPlanes, XYPixmap);
3374 if (strlen(appData.pixmapDirectory) == 0) {
3378 if (appData.monoMode) {
3379 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3383 fprintf(stderr, _("\nLoading XIMs...\n"));
3385 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3386 fprintf(stderr, "%d", piece+1);
3387 for (kind=0; kind<4; kind++) {
3388 fprintf(stderr, ".");
3389 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3390 ExpandPathName(appData.pixmapDirectory),
3391 piece <= (int) WhiteKing ? "" : "w",
3392 pieceBitmapNames[piece],
3394 ximPieceBitmap[kind][piece] =
3395 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3396 0, 0, ss, ss, AllPlanes, XYPixmap);
3397 if (appData.debugMode)
3398 fprintf(stderr, _("(File:%s:) "), buf);
3399 loadXIM(ximPieceBitmap[kind][piece],
3401 &(xpmPieceBitmap2[kind][piece]),
3402 &(ximMaskPm2[piece]));
3403 if(piece <= (int)WhiteKing)
3404 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3406 fprintf(stderr," ");
3408 /* Load light and dark squares */
3409 /* If the LSQ and DSQ pieces don't exist, we will
3410 draw them with solid squares. */
3411 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3412 if (access(buf, 0) != 0) {
3416 fprintf(stderr, _("light square "));
3418 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3419 0, 0, ss, ss, AllPlanes, XYPixmap);
3420 if (appData.debugMode)
3421 fprintf(stderr, _("(File:%s:) "), buf);
3423 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3424 fprintf(stderr, _("dark square "));
3425 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3426 ExpandPathName(appData.pixmapDirectory), ss);
3427 if (appData.debugMode)
3428 fprintf(stderr, _("(File:%s:) "), buf);
3430 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3431 0, 0, ss, ss, AllPlanes, XYPixmap);
3432 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3433 xpmJailSquare = xpmLightSquare;
3435 fprintf(stderr, _("Done.\n"));
3437 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3441 void CreateXPMBoard(char *s, int kind)
3445 if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3446 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3447 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3451 void FreeXPMPieces()
3452 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3453 // thisroutine has to be called t free the old piece pixmaps
3455 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3456 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3458 XFreePixmap(xDisplay, xpmLightSquare);
3459 XFreePixmap(xDisplay, xpmDarkSquare);
3463 void CreateXPMPieces()
3467 u_int ss = squareSize;
3469 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3470 XpmColorSymbol symbols[4];
3471 static int redo = False;
3473 if(redo) FreeXPMPieces(); else redo = 1;
3475 /* The XSynchronize calls were copied from CreatePieces.
3476 Not sure if needed, but can't hurt */
3477 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3479 /* Setup translations so piece colors match square colors */
3480 symbols[0].name = "light_piece";
3481 symbols[0].value = appData.whitePieceColor;
3482 symbols[1].name = "dark_piece";
3483 symbols[1].value = appData.blackPieceColor;
3484 symbols[2].name = "light_square";
3485 symbols[2].value = appData.lightSquareColor;
3486 symbols[3].name = "dark_square";
3487 symbols[3].value = appData.darkSquareColor;
3489 attr.valuemask = XpmColorSymbols;
3490 attr.colorsymbols = symbols;
3491 attr.numsymbols = 4;
3493 if (appData.monoMode) {
3494 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3498 if (strlen(appData.pixmapDirectory) == 0) {
3499 XpmPieces* pieces = builtInXpms;
3502 while (pieces->size != squareSize && pieces->size) pieces++;
3503 if (!pieces->size) {
3504 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3507 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3508 for (kind=0; kind<4; kind++) {
3510 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3511 pieces->xpm[piece][kind],
3512 &(xpmPieceBitmap2[kind][piece]),
3513 NULL, &attr)) != 0) {
3514 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3518 if(piece <= (int) WhiteKing)
3519 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3523 xpmJailSquare = xpmLightSquare;
3527 fprintf(stderr, _("\nLoading XPMs...\n"));
3530 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3531 fprintf(stderr, "%d ", piece+1);
3532 for (kind=0; kind<4; kind++) {
3533 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3534 ExpandPathName(appData.pixmapDirectory),
3535 piece > (int) WhiteKing ? "w" : "",
3536 pieceBitmapNames[piece],
3538 if (appData.debugMode) {
3539 fprintf(stderr, _("(File:%s:) "), buf);
3541 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3542 &(xpmPieceBitmap2[kind][piece]),
3543 NULL, &attr)) != 0) {
3544 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3545 // [HGM] missing: read of unorthodox piece failed; substitute King.
3546 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3547 ExpandPathName(appData.pixmapDirectory),
3549 if (appData.debugMode) {
3550 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3552 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3553 &(xpmPieceBitmap2[kind][piece]),
3557 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3562 if(piece <= (int) WhiteKing)
3563 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3566 /* Load light and dark squares */
3567 /* If the LSQ and DSQ pieces don't exist, we will
3568 draw them with solid squares. */
3569 fprintf(stderr, _("light square "));
3570 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3571 if (access(buf, 0) != 0) {
3575 if (appData.debugMode)
3576 fprintf(stderr, _("(File:%s:) "), buf);
3578 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3579 &xpmLightSquare, NULL, &attr)) != 0) {
3580 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3583 fprintf(stderr, _("dark square "));
3584 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3585 ExpandPathName(appData.pixmapDirectory), ss);
3586 if (appData.debugMode) {
3587 fprintf(stderr, _("(File:%s:) "), buf);
3589 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3590 &xpmDarkSquare, NULL, &attr)) != 0) {
3591 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3595 xpmJailSquare = xpmLightSquare;
3596 fprintf(stderr, _("Done.\n"));
3598 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3601 #endif /* HAVE_LIBXPM */
3604 /* No built-in bitmaps */
3609 u_int ss = squareSize;
3611 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3614 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3615 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3616 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3617 pieceBitmapNames[piece],
3618 ss, kind == SOLID ? 's' : 'o');
3619 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3620 if(piece <= (int)WhiteKing)
3621 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3625 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3629 /* With built-in bitmaps */
3632 BuiltInBits* bib = builtInBits;
3635 u_int ss = squareSize;
3637 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3640 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3642 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3643 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3644 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3645 pieceBitmapNames[piece],
3646 ss, kind == SOLID ? 's' : 'o');
3647 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3648 bib->bits[kind][piece], ss, ss);
3649 if(piece <= (int)WhiteKing)
3650 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3654 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3659 void ReadBitmap(pm, name, bits, wreq, hreq)
3662 unsigned char bits[];
3668 char msg[MSG_SIZ], fullname[MSG_SIZ];
3670 if (*appData.bitmapDirectory != NULLCHAR) {
3671 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3672 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3673 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3674 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3675 &w, &h, pm, &x_hot, &y_hot);
3676 fprintf(stderr, "load %s\n", name);
3677 if (errcode != BitmapSuccess) {
3679 case BitmapOpenFailed:
3680 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3682 case BitmapFileInvalid:
3683 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3685 case BitmapNoMemory:
3686 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3690 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3694 fprintf(stderr, _("%s: %s...using built-in\n"),
3696 } else if (w != wreq || h != hreq) {
3698 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3699 programName, fullname, w, h, wreq, hreq);
3705 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3714 if (lineGap == 0) return;
3716 /* [HR] Split this into 2 loops for non-square boards. */
3718 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3719 gridSegments[i].x1 = 0;
3720 gridSegments[i].x2 =
3721 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3722 gridSegments[i].y1 = gridSegments[i].y2
3723 = lineGap / 2 + (i * (squareSize + lineGap));
3726 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3727 gridSegments[j + i].y1 = 0;
3728 gridSegments[j + i].y2 =
3729 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3730 gridSegments[j + i].x1 = gridSegments[j + i].x2
3731 = lineGap / 2 + (j * (squareSize + lineGap));
3735 static void MenuBarSelect(w, addr, index)
3740 XtActionProc proc = (XtActionProc) addr;
3742 (proc)(NULL, NULL, NULL, NULL);
3745 void CreateMenuBarPopup(parent, name, mb)
3755 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3758 XtSetArg(args[j], XtNleftMargin, 20); j++;
3759 XtSetArg(args[j], XtNrightMargin, 20); j++;
3761 while (mi->string != NULL) {
3762 if (strcmp(mi->string, "----") == 0) {
3763 entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3766 XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3767 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3769 XtAddCallback(entry, XtNcallback,
3770 (XtCallbackProc) MenuBarSelect,
3771 (caddr_t) mi->proc);
3777 Widget CreateMenuBar(mb)
3781 Widget anchor, menuBar;
3783 char menuName[MSG_SIZ];
3786 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3787 XtSetArg(args[j], XtNvSpace, 0); j++;
3788 XtSetArg(args[j], XtNborderWidth, 0); j++;
3789 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3790 formWidget, args, j);
3792 while (mb->name != NULL) {
3793 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3794 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3796 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3799 shortName[0] = mb->name[0];
3800 shortName[1] = NULLCHAR;
3801 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3804 XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3807 XtSetArg(args[j], XtNborderWidth, 0); j++;
3808 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3810 CreateMenuBarPopup(menuBar, menuName, mb);
3816 Widget CreateButtonBar(mi)
3820 Widget button, buttonBar;
3824 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3826 XtSetArg(args[j], XtNhSpace, 0); j++;
3828 XtSetArg(args[j], XtNborderWidth, 0); j++;
3829 XtSetArg(args[j], XtNvSpace, 0); j++;
3830 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3831 formWidget, args, j);
3833 while (mi->string != NULL) {
3836 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3837 XtSetArg(args[j], XtNborderWidth, 0); j++;
3839 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3840 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3841 buttonBar, args, j);
3842 XtAddCallback(button, XtNcallback,
3843 (XtCallbackProc) MenuBarSelect,
3844 (caddr_t) mi->proc);
3851 CreatePieceMenu(name, color)
3858 ChessSquare selection;
3860 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3861 boardWidget, args, 0);
3863 for (i = 0; i < PIECE_MENU_SIZE; i++) {
3864 String item = pieceMenuStrings[color][i];
3866 if (strcmp(item, "----") == 0) {
3867 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3870 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3871 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3873 selection = pieceMenuTranslation[color][i];
3874 XtAddCallback(entry, XtNcallback,
3875 (XtCallbackProc) PieceMenuSelect,
3876 (caddr_t) selection);
3877 if (selection == WhitePawn || selection == BlackPawn) {
3878 XtSetArg(args[0], XtNpopupOnEntry, entry);
3879 XtSetValues(menu, args, 1);
3892 ChessSquare selection;
3894 whitePieceMenu = CreatePieceMenu("menuW", 0);
3895 blackPieceMenu = CreatePieceMenu("menuB", 1);
3897 XtRegisterGrabAction(PieceMenuPopup, True,
3898 (unsigned)(ButtonPressMask|ButtonReleaseMask),
3899 GrabModeAsync, GrabModeAsync);
3901 XtSetArg(args[0], XtNlabel, _("Drop"));
3902 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3903 boardWidget, args, 1);
3904 for (i = 0; i < DROP_MENU_SIZE; i++) {
3905 String item = dropMenuStrings[i];
3907 if (strcmp(item, "----") == 0) {
3908 entry = XtCreateManagedWidget(item, smeLineObjectClass,
3911 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3912 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3914 selection = dropMenuTranslation[i];
3915 XtAddCallback(entry, XtNcallback,
3916 (XtCallbackProc) DropMenuSelect,
3917 (caddr_t) selection);
3922 void SetupDropMenu()
3930 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3931 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3932 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3933 dmEnables[i].piece);
3934 XtSetSensitive(entry, p != NULL || !appData.testLegality
3935 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3936 && !appData.icsActive));
3938 while (p && *p++ == dmEnables[i].piece) count++;
3939 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
3941 XtSetArg(args[j], XtNlabel, label); j++;
3942 XtSetValues(entry, args, j);
3946 void PieceMenuPopup(w, event, params, num_params)
3950 Cardinal *num_params;
3952 String whichMenu; int menuNr;
3953 if (event->type == ButtonRelease)
3954 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3955 else if (event->type == ButtonPress)
3956 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3958 case 0: whichMenu = params[0]; break;
3959 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3961 case -1: if (errorUp) ErrorPopDown();
3964 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3967 static void PieceMenuSelect(w, piece, junk)
3972 if (pmFromX < 0 || pmFromY < 0) return;
3973 EditPositionMenuEvent(piece, pmFromX, pmFromY);
3976 static void DropMenuSelect(w, piece, junk)
3981 if (pmFromX < 0 || pmFromY < 0) return;
3982 DropMenuEvent(piece, pmFromX, pmFromY);
3985 void WhiteClock(w, event, prms, nprms)
3994 void BlackClock(w, event, prms, nprms)
4005 * If the user selects on a border boundary, return -1; if off the board,
4006 * return -2. Otherwise map the event coordinate to the square.
4008 int EventToSquare(x, limit)
4016 if ((x % (squareSize + lineGap)) >= squareSize)
4018 x /= (squareSize + lineGap);
4024 static void do_flash_delay(msec)
4030 static void drawHighlight(file, rank, gc)
4036 if (lineGap == 0) return;
4039 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4040 (squareSize + lineGap);
4041 y = lineGap/2 + rank * (squareSize + lineGap);
4043 x = lineGap/2 + file * (squareSize + lineGap);
4044 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4045 (squareSize + lineGap);
4048 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4049 squareSize+lineGap, squareSize+lineGap);
4052 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4053 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4056 SetHighlights(fromX, fromY, toX, toY)
4057 int fromX, fromY, toX, toY;
4059 if (hi1X != fromX || hi1Y != fromY) {
4060 if (hi1X >= 0 && hi1Y >= 0) {
4061 drawHighlight(hi1X, hi1Y, lineGC);
4063 } // [HGM] first erase both, then draw new!
4064 if (hi2X != toX || hi2Y != toY) {
4065 if (hi2X >= 0 && hi2Y >= 0) {
4066 drawHighlight(hi2X, hi2Y, lineGC);
4069 if (hi1X != fromX || hi1Y != fromY) {
4070 if (fromX >= 0 && fromY >= 0) {
4071 drawHighlight(fromX, fromY, highlineGC);
4074 if (hi2X != toX || hi2Y != toY) {
4075 if (toX >= 0 && toY >= 0) {
4076 drawHighlight(toX, toY, highlineGC);
4088 SetHighlights(-1, -1, -1, -1);
4093 SetPremoveHighlights(fromX, fromY, toX, toY)
4094 int fromX, fromY, toX, toY;
4096 if (pm1X != fromX || pm1Y != fromY) {
4097 if (pm1X >= 0 && pm1Y >= 0) {
4098 drawHighlight(pm1X, pm1Y, lineGC);
4100 if (fromX >= 0 && fromY >= 0) {
4101 drawHighlight(fromX, fromY, prelineGC);
4104 if (pm2X != toX || pm2Y != toY) {
4105 if (pm2X >= 0 && pm2Y >= 0) {
4106 drawHighlight(pm2X, pm2Y, lineGC);
4108 if (toX >= 0 && toY >= 0) {
4109 drawHighlight(toX, toY, prelineGC);
4119 ClearPremoveHighlights()
4121 SetPremoveHighlights(-1, -1, -1, -1);
4124 static int CutOutSquare(x, y, x0, y0, kind)
4125 int x, y, *x0, *y0, kind;
4127 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4128 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4130 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4131 if(textureW[kind] < W*squareSize)
4132 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4134 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4135 if(textureH[kind] < H*squareSize)
4136 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4138 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4142 static void BlankSquare(x, y, color, piece, dest, fac)
4143 int x, y, color, fac;
4146 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4148 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4149 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4150 squareSize, squareSize, x*fac, y*fac);
4152 if (useImages && useImageSqs) {
4156 pm = xpmLightSquare;
4161 case 2: /* neutral */
4166 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4167 squareSize, squareSize, x*fac, y*fac);
4177 case 2: /* neutral */
4182 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4187 I split out the routines to draw a piece so that I could
4188 make a generic flash routine.
4190 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4192 int square_color, x, y;
4195 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4196 switch (square_color) {
4198 case 2: /* neutral */
4200 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4201 ? *pieceToOutline(piece)
4202 : *pieceToSolid(piece),
4203 dest, bwPieceGC, 0, 0,
4204 squareSize, squareSize, x, y);
4207 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4208 ? *pieceToSolid(piece)
4209 : *pieceToOutline(piece),
4210 dest, wbPieceGC, 0, 0,
4211 squareSize, squareSize, x, y);
4216 static void monoDrawPiece(piece, square_color, x, y, dest)
4218 int square_color, x, y;
4221 switch (square_color) {
4223 case 2: /* neutral */
4225 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4226 ? *pieceToOutline(piece)
4227 : *pieceToSolid(piece),
4228 dest, bwPieceGC, 0, 0,
4229 squareSize, squareSize, x, y, 1);
4232 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4233 ? *pieceToSolid(piece)
4234 : *pieceToOutline(piece),
4235 dest, wbPieceGC, 0, 0,
4236 squareSize, squareSize, x, y, 1);
4241 static void colorDrawPiece(piece, square_color, x, y, dest)
4243 int square_color, x, y;
4246 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4247 switch (square_color) {
4249 XCopyPlane(xDisplay, *pieceToSolid(piece),
4250 dest, (int) piece < (int) BlackPawn
4251 ? wlPieceGC : blPieceGC, 0, 0,
4252 squareSize, squareSize, x, y, 1);
4255 XCopyPlane(xDisplay, *pieceToSolid(piece),
4256 dest, (int) piece < (int) BlackPawn
4257 ? wdPieceGC : bdPieceGC, 0, 0,
4258 squareSize, squareSize, x, y, 1);
4260 case 2: /* neutral */
4262 XCopyPlane(xDisplay, *pieceToSolid(piece),
4263 dest, (int) piece < (int) BlackPawn
4264 ? wjPieceGC : bjPieceGC, 0, 0,
4265 squareSize, squareSize, x, y, 1);
4270 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4272 int square_color, x, y;
4275 int kind, p = piece;
4277 switch (square_color) {
4279 case 2: /* neutral */
4281 if ((int)piece < (int) BlackPawn) {
4289 if ((int)piece < (int) BlackPawn) {
4297 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4298 if(useTexture & square_color+1) {
4299 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4300 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4301 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4302 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4303 XSetClipMask(xDisplay, wlPieceGC, None);
4304 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4306 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4307 dest, wlPieceGC, 0, 0,
4308 squareSize, squareSize, x, y);
4311 typedef void (*DrawFunc)();
4313 DrawFunc ChooseDrawFunc()
4315 if (appData.monoMode) {
4316 if (DefaultDepth(xDisplay, xScreen) == 1) {
4317 return monoDrawPiece_1bit;
4319 return monoDrawPiece;
4323 return colorDrawPieceImage;
4325 return colorDrawPiece;
4329 /* [HR] determine square color depending on chess variant. */
4330 static int SquareColor(row, column)
4335 if (gameInfo.variant == VariantXiangqi) {
4336 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4338 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4340 } else if (row <= 4) {
4346 square_color = ((column + row) % 2) == 1;
4349 /* [hgm] holdings: next line makes all holdings squares light */
4350 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4352 return square_color;
4355 void DrawSquare(row, column, piece, do_flash)
4356 int row, column, do_flash;
4359 int square_color, x, y, direction, font_ascent, font_descent;
4362 XCharStruct overall;
4366 /* Calculate delay in milliseconds (2-delays per complete flash) */
4367 flash_delay = 500 / appData.flashRate;
4370 x = lineGap + ((BOARD_WIDTH-1)-column) *
4371 (squareSize + lineGap);
4372 y = lineGap + row * (squareSize + lineGap);
4374 x = lineGap + column * (squareSize + lineGap);
4375 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4376 (squareSize + lineGap);
4379 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4381 square_color = SquareColor(row, column);
4383 if ( // [HGM] holdings: blank out area between board and holdings
4384 column == BOARD_LEFT-1 || column == BOARD_RGHT
4385 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4386 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4387 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4389 // [HGM] print piece counts next to holdings
4390 string[1] = NULLCHAR;
4391 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4392 string[0] = '0' + piece;
4393 XTextExtents(countFontStruct, string, 1, &direction,
4394 &font_ascent, &font_descent, &overall);
4395 if (appData.monoMode) {
4396 XDrawImageString(xDisplay, xBoardWindow, countGC,
4397 x + squareSize - overall.width - 2,
4398 y + font_ascent + 1, string, 1);
4400 XDrawString(xDisplay, xBoardWindow, countGC,
4401 x + squareSize - overall.width - 2,
4402 y + font_ascent + 1, string, 1);
4405 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4406 string[0] = '0' + piece;
4407 XTextExtents(countFontStruct, string, 1, &direction,
4408 &font_ascent, &font_descent, &overall);
4409 if (appData.monoMode) {
4410 XDrawImageString(xDisplay, xBoardWindow, countGC,
4411 x + 2, y + font_ascent + 1, string, 1);
4413 XDrawString(xDisplay, xBoardWindow, countGC,
4414 x + 2, y + font_ascent + 1, string, 1);
4418 if (piece == EmptySquare || appData.blindfold) {
4419 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4421 drawfunc = ChooseDrawFunc();
4423 if (do_flash && appData.flashCount > 0) {
4424 for (i=0; i<appData.flashCount; ++i) {
4425 drawfunc(piece, square_color, x, y, xBoardWindow);
4426 XSync(xDisplay, False);
4427 do_flash_delay(flash_delay);
4429 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4430 XSync(xDisplay, False);
4431 do_flash_delay(flash_delay);
4434 drawfunc(piece, square_color, x, y, xBoardWindow);
4438 string[1] = NULLCHAR;
4439 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4440 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4441 string[0] = 'a' + column - BOARD_LEFT;
4442 XTextExtents(coordFontStruct, string, 1, &direction,
4443 &font_ascent, &font_descent, &overall);
4444 if (appData.monoMode) {
4445 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4446 x + squareSize - overall.width - 2,
4447 y + squareSize - font_descent - 1, string, 1);
4449 XDrawString(xDisplay, xBoardWindow, coordGC,
4450 x + squareSize - overall.width - 2,
4451 y + squareSize - font_descent - 1, string, 1);
4454 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4455 string[0] = ONE + row;
4456 XTextExtents(coordFontStruct, string, 1, &direction,
4457 &font_ascent, &font_descent, &overall);
4458 if (appData.monoMode) {
4459 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4460 x + 2, y + font_ascent + 1, string, 1);
4462 XDrawString(xDisplay, xBoardWindow, coordGC,
4463 x + 2, y + font_ascent + 1, string, 1);
4466 if(!partnerUp && marker[row][column]) {
4467 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4468 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4473 /* Why is this needed on some versions of X? */
4474 void EventProc(widget, unused, event)
4479 if (!XtIsRealized(widget))
4482 switch (event->type) {
4484 if (event->xexpose.count > 0) return; /* no clipping is done */
4485 XDrawPosition(widget, True, NULL);
4486 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4487 flipView = !flipView; partnerUp = !partnerUp;
4488 XDrawPosition(widget, True, NULL);
4489 flipView = !flipView; partnerUp = !partnerUp;
4493 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4500 void DrawPosition(fullRedraw, board)
4501 /*Boolean*/int fullRedraw;
4504 XDrawPosition(boardWidget, fullRedraw, board);
4507 /* Returns 1 if there are "too many" differences between b1 and b2
4508 (i.e. more than 1 move was made) */
4509 static int too_many_diffs(b1, b2)
4515 for (i=0; i<BOARD_HEIGHT; ++i) {
4516 for (j=0; j<BOARD_WIDTH; ++j) {
4517 if (b1[i][j] != b2[i][j]) {
4518 if (++c > 4) /* Castling causes 4 diffs */
4526 /* Matrix describing castling maneuvers */
4527 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4528 static int castling_matrix[4][5] = {
4529 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4530 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4531 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4532 { 7, 7, 4, 5, 6 } /* 0-0, black */
4535 /* Checks whether castling occurred. If it did, *rrow and *rcol
4536 are set to the destination (row,col) of the rook that moved.
4538 Returns 1 if castling occurred, 0 if not.
4540 Note: Only handles a max of 1 castling move, so be sure
4541 to call too_many_diffs() first.
4543 static int check_castle_draw(newb, oldb, rrow, rcol)
4550 /* For each type of castling... */
4551 for (i=0; i<4; ++i) {
4552 r = castling_matrix[i];
4554 /* Check the 4 squares involved in the castling move */
4556 for (j=1; j<=4; ++j) {
4557 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4564 /* All 4 changed, so it must be a castling move */
4573 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4574 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4576 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4579 void DrawSeekBackground( int left, int top, int right, int bottom )
4581 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4584 void DrawSeekText(char *buf, int x, int y)
4586 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4589 void DrawSeekDot(int x, int y, int colorNr)
4591 int square = colorNr & 0x80;
4594 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4596 XFillRectangle(xDisplay, xBoardWindow, color,
4597 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4599 XFillArc(xDisplay, xBoardWindow, color,
4600 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4603 static int damage[2][BOARD_RANKS][BOARD_FILES];
4606 * event handler for redrawing the board
4608 void XDrawPosition(w, repaint, board)
4610 /*Boolean*/int repaint;
4614 static int lastFlipView = 0;
4615 static int lastBoardValid[2] = {0, 0};
4616 static Board lastBoard[2];
4619 int nr = twoBoards*partnerUp;
4621 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4623 if (board == NULL) {
4624 if (!lastBoardValid[nr]) return;
4625 board = lastBoard[nr];
4627 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4628 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4629 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4634 * It would be simpler to clear the window with XClearWindow()
4635 * but this causes a very distracting flicker.
4638 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4640 if ( lineGap && IsDrawArrowEnabled())
4641 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4642 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4644 /* If too much changes (begin observing new game, etc.), don't
4646 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4648 /* Special check for castling so we don't flash both the king
4649 and the rook (just flash the king). */
4651 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4652 /* Draw rook with NO flashing. King will be drawn flashing later */
4653 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4654 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4658 /* First pass -- Draw (newly) empty squares and repair damage.
4659 This prevents you from having a piece show up twice while it
4660 is flashing on its new square */
4661 for (i = 0; i < BOARD_HEIGHT; i++)
4662 for (j = 0; j < BOARD_WIDTH; j++)
4663 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4664 || damage[nr][i][j]) {
4665 DrawSquare(i, j, board[i][j], 0);
4666 damage[nr][i][j] = False;
4669 /* Second pass -- Draw piece(s) in new position and flash them */
4670 for (i = 0; i < BOARD_HEIGHT; i++)
4671 for (j = 0; j < BOARD_WIDTH; j++)
4672 if (board[i][j] != lastBoard[nr][i][j]) {
4673 DrawSquare(i, j, board[i][j], do_flash);
4677 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4678 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4679 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4681 for (i = 0; i < BOARD_HEIGHT; i++)
4682 for (j = 0; j < BOARD_WIDTH; j++) {
4683 DrawSquare(i, j, board[i][j], 0);
4684 damage[nr][i][j] = False;
4688 CopyBoard(lastBoard[nr], board);
4689 lastBoardValid[nr] = 1;
4690 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4691 lastFlipView = flipView;
4693 /* Draw highlights */
4694 if (pm1X >= 0 && pm1Y >= 0) {
4695 drawHighlight(pm1X, pm1Y, prelineGC);
4697 if (pm2X >= 0 && pm2Y >= 0) {
4698 drawHighlight(pm2X, pm2Y, prelineGC);
4700 if (hi1X >= 0 && hi1Y >= 0) {
4701 drawHighlight(hi1X, hi1Y, highlineGC);
4703 if (hi2X >= 0 && hi2Y >= 0) {
4704 drawHighlight(hi2X, hi2Y, highlineGC);
4706 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4708 /* If piece being dragged around board, must redraw that too */
4711 XSync(xDisplay, False);
4716 * event handler for redrawing the board
4718 void DrawPositionProc(w, event, prms, nprms)
4724 XDrawPosition(w, True, NULL);
4729 * event handler for parsing user moves
4731 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4732 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4733 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4734 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4735 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4736 // and at the end FinishMove() to perform the move after optional promotion popups.
4737 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4738 void HandleUserMove(w, event, prms, nprms)
4744 if (w != boardWidget || errorExitStatus != -1) return;
4745 if(nprms) shiftKey = !strcmp(prms[0], "1");
4748 if (event->type == ButtonPress) {
4749 XtPopdown(promotionShell);
4750 XtDestroyWidget(promotionShell);
4751 promotionUp = False;
4759 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4760 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4761 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4764 void AnimateUserMove (Widget w, XEvent * event,
4765 String * params, Cardinal * nParams)
4767 DragPieceMove(event->xmotion.x, event->xmotion.y);
4770 void HandlePV (Widget w, XEvent * event,
4771 String * params, Cardinal * nParams)
4772 { // [HGM] pv: walk PV
4773 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4776 Widget CommentCreate(name, text, mutable, callback, lines)
4778 int /*Boolean*/ mutable;
4779 XtCallbackProc callback;
4783 Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4788 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4789 XtGetValues(boardWidget, args, j);
4792 XtSetArg(args[j], XtNresizable, True); j++;
4795 XtCreatePopupShell(name, topLevelShellWidgetClass,
4796 shellWidget, args, j);
4799 XtCreatePopupShell(name, transientShellWidgetClass,
4800 shellWidget, args, j);
4803 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4804 layoutArgs, XtNumber(layoutArgs));
4806 XtCreateManagedWidget("form", formWidgetClass, layout,
4807 formArgs, XtNumber(formArgs));
4811 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
4812 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
4814 XtSetArg(args[j], XtNstring, text); j++;
4815 XtSetArg(args[j], XtNtop, XtChainTop); j++;
4816 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4817 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4818 XtSetArg(args[j], XtNright, XtChainRight); j++;
4819 XtSetArg(args[j], XtNresizable, True); j++;
4820 XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/
4821 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4822 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
4823 XtSetArg(args[j], XtNautoFill, True); j++;
4824 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4826 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4827 XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
4831 XtSetArg(args[j], XtNfromVert, edit); j++;
4832 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4833 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4834 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4835 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4837 XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4838 XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4841 XtSetArg(args[j], XtNfromVert, edit); j++;
4842 XtSetArg(args[j], XtNfromHoriz, b_ok); j++;
4843 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4844 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4845 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4846 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4848 XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4849 XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4852 XtSetArg(args[j], XtNfromVert, edit); j++;
4853 XtSetArg(args[j], XtNfromHoriz, b_cancel); j++;
4854 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4855 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4856 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4857 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4859 XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4860 XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4863 XtSetArg(args[j], XtNfromVert, edit); j++;
4864 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4865 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4866 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4867 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4869 XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4870 XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4873 XtSetArg(args[j], XtNfromVert, edit); j++;
4874 XtSetArg(args[j], XtNfromHoriz, b_close); j++;
4875 XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4876 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4877 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4878 XtSetArg(args[j], XtNright, XtChainLeft); j++;
4880 XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4881 XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4884 XtRealizeWidget(shell);
4886 if (commentX == -1) {
4889 Dimension pw_height;
4890 Dimension ew_height;
4893 XtSetArg(args[j], XtNheight, &ew_height); j++;
4894 XtGetValues(edit, args, j);
4897 XtSetArg(args[j], XtNheight, &pw_height); j++;
4898 XtGetValues(shell, args, j);
4899 commentH = pw_height + (lines - 1) * ew_height;
4900 commentW = bw_width - 16;
4902 XSync(xDisplay, False);
4904 /* This code seems to tickle an X bug if it is executed too soon
4905 after xboard starts up. The coordinates get transformed as if
4906 the main window was positioned at (0, 0).
4908 XtTranslateCoords(shellWidget,
4909 (bw_width - commentW) / 2, 0 - commentH / 2,
4910 &commentX, &commentY);
4912 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4913 RootWindowOfScreen(XtScreen(shellWidget)),
4914 (bw_width - commentW) / 2, 0 - commentH / 2,
4919 if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4922 if(wpComment.width > 0) {
4923 commentX = wpComment.x;
4924 commentY = wpComment.y;
4925 commentW = wpComment.width;
4926 commentH = wpComment.height;
4930 XtSetArg(args[j], XtNheight, commentH); j++;
4931 XtSetArg(args[j], XtNwidth, commentW); j++;
4932 XtSetArg(args[j], XtNx, commentX); j++;
4933 XtSetArg(args[j], XtNy, commentY); j++;
4934 XtSetValues(shell, args, j);
4935 XtSetKeyboardFocus(shell, edit);
4940 /* Used for analysis window and ICS input window */
4941 Widget MiscCreate(name, text, mutable, callback, lines)
4943 int /*Boolean*/ mutable;
4944 XtCallbackProc callback;
4948 Widget shell, layout, form, edit;
4950 Dimension bw_width, pw_height, ew_height, w, h;
4956 XtSetArg(args[j], XtNresizable, True); j++;
4959 XtCreatePopupShell(name, topLevelShellWidgetClass,
4960 shellWidget, args, j);
4963 XtCreatePopupShell(name, transientShellWidgetClass,
4964 shellWidget, args, j);
4967 XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4968 layoutArgs, XtNumber(layoutArgs));
4970 XtCreateManagedWidget("form", formWidgetClass, layout,
4971 formArgs, XtNumber(formArgs));
4975 XtSetArg(args[j], XtNeditType, XawtextEdit); j++;
4976 XtSetArg(args[j], XtNuseStringInPlace, False); j++;
4978 XtSetArg(args[j], XtNstring, text); j++;
4979 XtSetArg(args[j], XtNtop, XtChainTop); j++;
4980 XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4981 XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4982 XtSetArg(args[j], XtNright, XtChainRight); j++;
4983 XtSetArg(args[j], XtNresizable, True); j++;
4984 /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4985 XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++;
4986 XtSetArg(args[j], XtNautoFill, True); j++;
4987 XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4989 XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4991 XtRealizeWidget(shell);
4994 XtSetArg(args[j], XtNwidth, &bw_width); j++;
4995 XtGetValues(boardWidget, args, j);
4998 XtSetArg(args[j], XtNheight, &ew_height); j++;
4999 XtGetValues(edit, args, j);
5002 XtSetArg(args[j], XtNheight, &pw_height); j++;
5003 XtGetValues(shell, args, j);
5004 h = pw_height + (lines - 1) * ew_height;
5007 XSync(xDisplay, False);
5009 /* This code seems to tickle an X bug if it is executed too soon
5010 after xboard starts up. The coordinates get transformed as if
5011 the main window was positioned at (0, 0).
5013 XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
5015 XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
5016 RootWindowOfScreen(XtScreen(shellWidget)),
5017 (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
5021 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5024 XtSetArg(args[j], XtNheight, h); j++;
5025 XtSetArg(args[j], XtNwidth, w); j++;
5026 XtSetArg(args[j], XtNx, x); j++;
5027 XtSetArg(args[j], XtNy, y); j++;
5028 XtSetValues(shell, args, j);
5034 static int savedIndex; /* gross that this is global */
5036 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
5039 XawTextPosition index, dummy;
5042 XawTextGetSelectionPos(w, &index, &dummy);
5043 XtSetArg(arg, XtNstring, &val);
5044 XtGetValues(w, &arg, 1);
5045 ReplaceComment(savedIndex, val);
5046 if(savedIndex != currentMove) ToNrEvent(savedIndex);
5047 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
5050 void EditCommentPopUp(index, title, text)
5059 if (text == NULL) text = "";
5061 if (editShell == NULL) {
5063 CommentCreate(title, text, True, EditCommentCallback, 4);
5064 XtRealizeWidget(editShell);
5065 CatchDeleteWindow(editShell, "EditCommentPopDown");
5067 edit = XtNameToWidget(editShell, "*form.text");
5069 XtSetArg(args[j], XtNstring, text); j++;
5070 XtSetValues(edit, args, j);
5072 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
5073 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
5074 XtSetValues(editShell, args, j);
5077 XtPopup(editShell, XtGrabNone);
5081 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5082 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
5084 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
5088 void EditCommentCallback(w, client_data, call_data)
5090 XtPointer client_data, call_data;
5098 XtSetArg(args[j], XtNlabel, &name); j++;
5099 XtGetValues(w, args, j);
5101 if (strcmp(name, _("ok")) == 0) {
5102 edit = XtNameToWidget(editShell, "*form.text");
5104 XtSetArg(args[j], XtNstring, &val); j++;
5105 XtGetValues(edit, args, j);
5106 ReplaceComment(savedIndex, val);
5107 EditCommentPopDown();
5108 } else if (strcmp(name, _("cancel")) == 0) {
5109 EditCommentPopDown();
5110 } else if (strcmp(name, _("clear")) == 0) {
5111 edit = XtNameToWidget(editShell, "*form.text");
5112 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5113 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5117 void EditCommentPopDown()
5122 if (!editUp) return;
5124 XtSetArg(args[j], XtNx, &commentX); j++;
5125 XtSetArg(args[j], XtNy, &commentY); j++;
5126 XtSetArg(args[j], XtNheight, &commentH); j++;
5127 XtSetArg(args[j], XtNwidth, &commentW); j++;
5128 XtGetValues(editShell, args, j);
5129 XtPopdown(editShell);
5132 XtSetArg(args[j], XtNleftBitmap, None); j++;
5133 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
5135 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
5139 void ICSInputBoxPopUp()
5144 char *title = _("ICS Input");
5147 if (ICSInputShell == NULL) {
5148 ICSInputShell = MiscCreate(title, "", True, NULL, 1);
5149 tr = XtParseTranslationTable(ICSInputTranslations);
5150 edit = XtNameToWidget(ICSInputShell, "*form.text");
5151 XtOverrideTranslations(edit, tr);
5152 XtRealizeWidget(ICSInputShell);
5153 CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
5156 edit = XtNameToWidget(ICSInputShell, "*form.text");
5158 XtSetArg(args[j], XtNstring, ""); j++;
5159 XtSetValues(edit, args, j);
5161 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
5162 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
5163 XtSetValues(ICSInputShell, args, j);
5166 XtPopup(ICSInputShell, XtGrabNone);
5167 XtSetKeyboardFocus(ICSInputShell, edit);
5169 ICSInputBoxUp = True;
5171 XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5172 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.ICS Input Box"),
5176 void ICSInputSendText()
5183 edit = XtNameToWidget(ICSInputShell, "*form.text");
5185 XtSetArg(args[j], XtNstring, &val); j++;
5186 XtGetValues(edit, args, j);
5188 SendMultiLineToICS(val);
5189 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5190 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5193 void ICSInputBoxPopDown()
5198 if (!ICSInputBoxUp) return;
5200 XtPopdown(ICSInputShell);
5201 ICSInputBoxUp = False;
5203 XtSetArg(args[j], XtNleftBitmap, None); j++;
5204 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.ICS Input Box"),
5208 void CommentPopUp(title, text)
5215 savedIndex = currentMove; // [HGM] vari
5216 if (commentShell == NULL) {
5218 CommentCreate(title, text, False, CommentCallback, 4);
5219 XtRealizeWidget(commentShell);
5220 CatchDeleteWindow(commentShell, "CommentPopDown");
5222 edit = XtNameToWidget(commentShell, "*form.text");
5224 XtSetArg(args[j], XtNstring, text); j++;
5225 XtSetValues(edit, args, j);
5227 XtSetArg(args[j], XtNiconName, (XtArgVal) title); j++;
5228 XtSetArg(args[j], XtNtitle, (XtArgVal) title); j++;
5229 XtSetValues(commentShell, args, j);
5232 XtPopup(commentShell, XtGrabNone);
5233 XSync(xDisplay, False);
5238 void CommentCallback(w, client_data, call_data)
5240 XtPointer client_data, call_data;
5247 XtSetArg(args[j], XtNlabel, &name); j++;
5248 XtGetValues(w, args, j);
5250 if (strcmp(name, _("close")) == 0) {
5252 } else if (strcmp(name, _("edit")) == 0) {
5259 void CommentPopDown()
5264 if (!commentUp) return;
5266 XtSetArg(args[j], XtNx, &commentX); j++;
5267 XtSetArg(args[j], XtNy, &commentY); j++;
5268 XtSetArg(args[j], XtNwidth, &commentW); j++;
5269 XtSetArg(args[j], XtNheight, &commentH); j++;
5270 XtGetValues(commentShell, args, j);
5271 XtPopdown(commentShell);
5272 XSync(xDisplay, False);
5276 void FileNamePopUp(label, def, proc, openMode)
5282 fileProc = proc; /* I can't see a way not */
5283 fileOpenMode = openMode; /* to use globals here */
5284 { // [HGM] use file-selector dialog stolen from Ghostview
5286 int index; // this is not supported yet
5288 if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5289 def, openMode, NULL, &name))
5290 (void) (*fileProc)(f, index=0, name);
5294 void FileNamePopDown()
5296 if (!filenameUp) return;
5297 XtPopdown(fileNameShell);
5298 XtDestroyWidget(fileNameShell);
5303 void FileNameCallback(w, client_data, call_data)
5305 XtPointer client_data, call_data;
5310 XtSetArg(args[0], XtNlabel, &name);
5311 XtGetValues(w, args, 1);
5313 if (strcmp(name, _("cancel")) == 0) {
5318 FileNameAction(w, NULL, NULL, NULL);
5321 void FileNameAction(w, event, prms, nprms)
5333 name = XawDialogGetValueString(w = XtParent(w));
5335 if ((name != NULL) && (*name != NULLCHAR)) {
5336 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5337 XtPopdown(w = XtParent(XtParent(w)));
5341 p = strrchr(buf, ' ');
5348 fullname = ExpandPathName(buf);
5350 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5353 f = fopen(fullname, fileOpenMode);
5355 DisplayError(_("Failed to open file"), errno);
5357 (void) (*fileProc)(f, index, buf);
5364 XtPopdown(w = XtParent(XtParent(w)));
5370 void PromotionPopUp()
5373 Widget dialog, layout;
5375 Dimension bw_width, pw_width;
5379 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5380 XtGetValues(boardWidget, args, j);
5383 XtSetArg(args[j], XtNresizable, True); j++;
5384 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5386 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5387 shellWidget, args, j);
5389 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5390 layoutArgs, XtNumber(layoutArgs));
5393 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5394 XtSetArg(args[j], XtNborderWidth, 0); j++;
5395 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5398 if(gameInfo.variant != VariantShogi) {
5399 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5400 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5401 (XtPointer) dialog);
5402 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5403 (XtPointer) dialog);
5404 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5405 (XtPointer) dialog);
5406 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5407 (XtPointer) dialog);
5409 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5410 (XtPointer) dialog);
5411 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5412 (XtPointer) dialog);
5413 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5414 (XtPointer) dialog);
5415 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5416 (XtPointer) dialog);
5418 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5419 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
\r
5420 gameInfo.variant == VariantGiveaway) {
5421 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5422 (XtPointer) dialog);
5424 if(gameInfo.variant == VariantCapablanca ||
5425 gameInfo.variant == VariantGothic ||
5426 gameInfo.variant == VariantCapaRandom) {
5427 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5428 (XtPointer) dialog);
5429 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5430 (XtPointer) dialog);
5432 } else // [HGM] shogi
5434 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5435 (XtPointer) dialog);
5436 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5437 (XtPointer) dialog);
5439 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5440 (XtPointer) dialog);
5442 XtRealizeWidget(promotionShell);
5443 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5446 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5447 XtGetValues(promotionShell, args, j);
5449 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5450 lineGap + squareSize/3 +
5451 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5452 0 : 6*(squareSize + lineGap)), &x, &y);
5455 XtSetArg(args[j], XtNx, x); j++;
5456 XtSetArg(args[j], XtNy, y); j++;
5457 XtSetValues(promotionShell, args, j);
5459 XtPopup(promotionShell, XtGrabNone);
5464 void PromotionPopDown()
5466 if (!promotionUp) return;
5467 XtPopdown(promotionShell);
5468 XtDestroyWidget(promotionShell);
5469 promotionUp = False;
5472 void PromotionCallback(w, client_data, call_data)
5474 XtPointer client_data, call_data;
5480 XtSetArg(args[0], XtNlabel, &name);
5481 XtGetValues(w, args, 1);
5485 if (fromX == -1) return;
5487 if (strcmp(name, _("cancel")) == 0) {
5491 } else if (strcmp(name, _("Knight")) == 0) {
5493 } else if (strcmp(name, _("Promote")) == 0) {
5495 } else if (strcmp(name, _("Defer")) == 0) {
5498 promoChar = ToLower(name[0]);
5501 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5503 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5504 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5509 void ErrorCallback(w, client_data, call_data)
5511 XtPointer client_data, call_data;
5514 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5516 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5522 if (!errorUp) return;
5524 XtPopdown(errorShell);
5525 XtDestroyWidget(errorShell);
5526 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5529 void ErrorPopUp(title, label, modal)
5530 char *title, *label;
5534 Widget dialog, layout;
5538 Dimension bw_width, pw_width;
5539 Dimension pw_height;
5543 XtSetArg(args[i], XtNresizable, True); i++;
5544 XtSetArg(args[i], XtNtitle, title); i++;
5546 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5547 shellWidget, args, i);
5549 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5550 layoutArgs, XtNumber(layoutArgs));
5553 XtSetArg(args[i], XtNlabel, label); i++;
5554 XtSetArg(args[i], XtNborderWidth, 0); i++;
5555 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5558 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5560 XtRealizeWidget(errorShell);
5561 CatchDeleteWindow(errorShell, "ErrorPopDown");
5564 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5565 XtGetValues(boardWidget, args, i);
5567 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5568 XtSetArg(args[i], XtNheight, &pw_height); i++;
5569 XtGetValues(errorShell, args, i);
5572 /* This code seems to tickle an X bug if it is executed too soon
5573 after xboard starts up. The coordinates get transformed as if
5574 the main window was positioned at (0, 0).
5576 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5577 0 - pw_height + squareSize / 3, &x, &y);
5579 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5580 RootWindowOfScreen(XtScreen(boardWidget)),
5581 (bw_width - pw_width) / 2,
5582 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5586 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5589 XtSetArg(args[i], XtNx, x); i++;
5590 XtSetArg(args[i], XtNy, y); i++;
5591 XtSetValues(errorShell, args, i);
5594 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5597 /* Disable all user input other than deleting the window */
5598 static int frozen = 0;
5602 /* Grab by a widget that doesn't accept input */
5603 XtAddGrab(messageWidget, TRUE, FALSE);
5607 /* Undo a FreezeUI */
5610 if (!frozen) return;
5611 XtRemoveGrab(messageWidget);
5615 char *ModeToWidgetName(mode)
5619 case BeginningOfGame:
5620 if (appData.icsActive)
5621 return "menuMode.ICS Client";
5622 else if (appData.noChessProgram ||
5623 *appData.cmailGameName != NULLCHAR)
5624 return "menuMode.Edit Game";
5626 return "menuMode.Machine Black";
5627 case MachinePlaysBlack:
5628 return "menuMode.Machine Black";
5629 case MachinePlaysWhite:
5630 return "menuMode.Machine White";
5632 return "menuMode.Analysis Mode";
5634 return "menuMode.Analyze File";
5635 case TwoMachinesPlay:
5636 return "menuMode.Two Machines";
5638 return "menuMode.Edit Game";
5639 case PlayFromGameFile:
5640 return "menuFile.Load Game";
5642 return "menuMode.Edit Position";
5644 return "menuMode.Training";
5645 case IcsPlayingWhite:
5646 case IcsPlayingBlack:
5650 return "menuMode.ICS Client";
5657 void ModeHighlight()
5660 static int oldPausing = FALSE;
5661 static GameMode oldmode = (GameMode) -1;
5664 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5666 if (pausing != oldPausing) {
5667 oldPausing = pausing;
5669 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5671 XtSetArg(args[0], XtNleftBitmap, None);
5673 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5676 if (appData.showButtonBar) {
5677 /* Always toggle, don't set. Previous code messes up when
5678 invoked while the button is pressed, as releasing it
5679 toggles the state again. */
5682 XtSetArg(args[0], XtNbackground, &oldbg);
5683 XtSetArg(args[1], XtNforeground, &oldfg);
5684 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5686 XtSetArg(args[0], XtNbackground, oldfg);
5687 XtSetArg(args[1], XtNforeground, oldbg);
5689 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5693 wname = ModeToWidgetName(oldmode);
5694 if (wname != NULL) {
5695 XtSetArg(args[0], XtNleftBitmap, None);
5696 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5698 wname = ModeToWidgetName(gameMode);
5699 if (wname != NULL) {
5700 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5701 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5705 /* Maybe all the enables should be handled here, not just this one */
5706 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5707 gameMode == Training || gameMode == PlayFromGameFile);
5712 * Button/menu procedures
5714 void ResetProc(w, event, prms, nprms)
5723 int LoadGamePopUp(f, gameNumber, title)
5728 cmailMsgLoaded = FALSE;
5729 if (gameNumber == 0) {
5730 int error = GameListBuild(f);
5732 DisplayError(_("Cannot build game list"), error);
5733 } else if (!ListEmpty(&gameList) &&
5734 ((ListGame *) gameList.tailPred)->number > 1) {
5735 GameListPopUp(f, title);
5741 return LoadGame(f, gameNumber, title, FALSE);
5744 void LoadGameProc(w, event, prms, nprms)
5750 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5753 FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
5756 void LoadNextGameProc(w, event, prms, nprms)
5765 void LoadPrevGameProc(w, event, prms, nprms)
5774 void ReloadGameProc(w, event, prms, nprms)
5783 void LoadNextPositionProc(w, event, prms, nprms)
5792 void LoadPrevPositionProc(w, event, prms, nprms)
5801 void ReloadPositionProc(w, event, prms, nprms)
5810 void LoadPositionProc(w, event, prms, nprms)
5816 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5819 FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5822 void SaveGameProc(w, event, prms, nprms)
5828 FileNamePopUp(_("Save game file name?"),
5829 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5833 void SavePositionProc(w, event, prms, nprms)
5839 FileNamePopUp(_("Save position file name?"),
5840 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5844 void ReloadCmailMsgProc(w, event, prms, nprms)
5850 ReloadCmailMsgEvent(FALSE);
5853 void MailMoveProc(w, event, prms, nprms)
5862 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5863 char *selected_fen_position=NULL;
5866 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5867 Atom *type_return, XtPointer *value_return,
5868 unsigned long *length_return, int *format_return)
5870 char *selection_tmp;
5872 if (!selected_fen_position) return False; /* should never happen */
5873 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5874 /* note: since no XtSelectionDoneProc was registered, Xt will
5875 * automatically call XtFree on the value returned. So have to
5876 * make a copy of it allocated with XtMalloc */
5877 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5878 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5880 *value_return=selection_tmp;
5881 *length_return=strlen(selection_tmp);
5882 *type_return=*target;
5883 *format_return = 8; /* bits per byte */
5885 } else if (*target == XA_TARGETS(xDisplay)) {
5886 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5887 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5888 targets_tmp[1] = XA_STRING;
5889 *value_return = targets_tmp;
5890 *type_return = XA_ATOM;
5892 *format_return = 8 * sizeof(Atom);
5893 if (*format_return > 32) {
5894 *length_return *= *format_return / 32;
5895 *format_return = 32;
5903 /* note: when called from menu all parameters are NULL, so no clue what the
5904 * Widget which was clicked on was, or what the click event was
5906 void CopyPositionProc(w, event, prms, nprms)
5913 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5914 * have a notion of a position that is selected but not copied.
5915 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5917 if(gameMode == EditPosition) EditPositionDone(TRUE);
5918 if (selected_fen_position) free(selected_fen_position);
5919 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5920 if (!selected_fen_position) return;
5921 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5923 SendPositionSelection,
5924 NULL/* lose_ownership_proc */ ,
5925 NULL/* transfer_done_proc */);
5926 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5928 SendPositionSelection,
5929 NULL/* lose_ownership_proc */ ,
5930 NULL/* transfer_done_proc */);
5933 /* function called when the data to Paste is ready */
5935 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5936 Atom *type, XtPointer value, unsigned long *len, int *format)
5939 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5940 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5941 EditPositionPasteFEN(fenstr);
5945 /* called when Paste Position button is pressed,
5946 * all parameters will be NULL */
5947 void PastePositionProc(w, event, prms, nprms)
5953 XtGetSelectionValue(menuBarWidget,
5954 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5955 /* (XtSelectionCallbackProc) */ PastePositionCB,
5956 NULL, /* client_data passed to PastePositionCB */
5958 /* better to use the time field from the event that triggered the
5959 * call to this function, but that isn't trivial to get
5967 SendGameSelection(Widget w, Atom *selection, Atom *target,
5968 Atom *type_return, XtPointer *value_return,
5969 unsigned long *length_return, int *format_return)
5971 char *selection_tmp;
5973 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5974 FILE* f = fopen(gameCopyFilename, "r");
5977 if (f == NULL) return False;
5981 selection_tmp = XtMalloc(len + 1);
5982 count = fread(selection_tmp, 1, len, f);
5985 XtFree(selection_tmp);
5988 selection_tmp[len] = NULLCHAR;
5989 *value_return = selection_tmp;
5990 *length_return = len;
5991 *type_return = *target;
5992 *format_return = 8; /* bits per byte */
5994 } else if (*target == XA_TARGETS(xDisplay)) {
5995 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5996 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5997 targets_tmp[1] = XA_STRING;
5998 *value_return = targets_tmp;
5999 *type_return = XA_ATOM;
6001 *format_return = 8 * sizeof(Atom);
6002 if (*format_return > 32) {
6003 *length_return *= *format_return / 32;
6004 *format_return = 32;
6012 /* note: when called from menu all parameters are NULL, so no clue what the
6013 * Widget which was clicked on was, or what the click event was
6015 void CopyGameProc(w, event, prms, nprms)
6023 ret = SaveGameToFile(gameCopyFilename, FALSE);
6027 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
6028 * have a notion of a game that is selected but not copied.
6029 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
6031 XtOwnSelection(menuBarWidget, XA_PRIMARY,
6034 NULL/* lose_ownership_proc */ ,
6035 NULL/* transfer_done_proc */);
6036 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
6039 NULL/* lose_ownership_proc */ ,
6040 NULL/* transfer_done_proc */);
6043 /* function called when the data to Paste is ready */
6045 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
6046 Atom *type, XtPointer value, unsigned long *len, int *format)
6049 if (value == NULL || *len == 0) {
6050 return; /* nothing had been selected to copy */
6052 f = fopen(gamePasteFilename, "w");
6054 DisplayError(_("Can't open temp file"), errno);
6057 fwrite(value, 1, *len, f);
6060 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
6063 /* called when Paste Game button is pressed,
6064 * all parameters will be NULL */
6065 void PasteGameProc(w, event, prms, nprms)
6071 XtGetSelectionValue(menuBarWidget,
6072 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
6073 /* (XtSelectionCallbackProc) */ PasteGameCB,
6074 NULL, /* client_data passed to PasteGameCB */
6076 /* better to use the time field from the event that triggered the
6077 * call to this function, but that isn't trivial to get
6087 SaveGameProc(NULL, NULL, NULL, NULL);
6091 void QuitProc(w, event, prms, nprms)
6100 void PauseProc(w, event, prms, nprms)
6110 void MachineBlackProc(w, event, prms, nprms)
6116 MachineBlackEvent();
6119 void MachineWhiteProc(w, event, prms, nprms)
6125 MachineWhiteEvent();
6128 void AnalyzeModeProc(w, event, prms, nprms)
6136 if (!first.analysisSupport) {
6137 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6138 DisplayError(buf, 0);
6141 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
6142 if (appData.icsActive) {
6143 if (gameMode != IcsObserving) {
6144 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
6145 DisplayError(buf, 0);
6147 if (appData.icsEngineAnalyze) {
6148 if (appData.debugMode)
6149 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
6155 /* if enable, use want disable icsEngineAnalyze */
6156 if (appData.icsEngineAnalyze) {
6161 appData.icsEngineAnalyze = TRUE;
6162 if (appData.debugMode)
6163 fprintf(debugFP, _("ICS engine analyze starting... \n"));
6165 #ifndef OPTIONSDIALOG
6166 if (!appData.showThinking)
6167 ShowThinkingProc(w,event,prms,nprms);
6173 void AnalyzeFileProc(w, event, prms, nprms)
6179 if (!first.analysisSupport) {
6181 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6182 DisplayError(buf, 0);
6186 #ifndef OPTIONSDIALOG
6187 if (!appData.showThinking)
6188 ShowThinkingProc(w,event,prms,nprms);
6191 FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
6192 AnalysisPeriodicEvent(1);
6195 void TwoMachinesProc(w, event, prms, nprms)
6204 void MatchProc(w, event, prms, nprms)
6210 if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
6211 matchMode = 2; // This is back-end, really
\r
6212 appData.matchGames = appData.defaultMatchGames;
\r
6214 first.matchWins = second.matchWins = 0;
\r
6218 void IcsClientProc(w, event, prms, nprms)
6227 void EditGameProc(w, event, prms, nprms)
6236 void EditPositionProc(w, event, prms, nprms)
6242 EditPositionEvent();
6245 void TrainingProc(w, event, prms, nprms)
6254 void EditCommentProc(w, event, prms, nprms)
6261 EditCommentPopDown();
6267 void IcsInputBoxProc(w, event, prms, nprms)
6273 if (ICSInputBoxUp) {
6274 ICSInputBoxPopDown();
6280 void AcceptProc(w, event, prms, nprms)
6289 void DeclineProc(w, event, prms, nprms)
6298 void RematchProc(w, event, prms, nprms)
6307 void CallFlagProc(w, event, prms, nprms)
6316 void DrawProc(w, event, prms, nprms)
6325 void AbortProc(w, event, prms, nprms)
6334 void AdjournProc(w, event, prms, nprms)
6343 void ResignProc(w, event, prms, nprms)
6352 void AdjuWhiteProc(w, event, prms, nprms)
6358 UserAdjudicationEvent(+1);
6361 void AdjuBlackProc(w, event, prms, nprms)
6367 UserAdjudicationEvent(-1);
6370 void AdjuDrawProc(w, event, prms, nprms)
6376 UserAdjudicationEvent(0);
6379 void EnterKeyProc(w, event, prms, nprms)
6385 if (ICSInputBoxUp == True)
6389 void UpKeyProc(w, event, prms, nprms)
6394 { // [HGM] input: let up-arrow recall previous line from history
6401 if (!ICSInputBoxUp) return;
6402 edit = XtNameToWidget(ICSInputShell, "*form.text");
6404 XtSetArg(args[j], XtNstring, &val); j++;
6405 XtGetValues(edit, args, j);
6406 val = PrevInHistory(val);
6407 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6408 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6410 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6411 XawTextReplace(edit, 0, 0, &t);
6412 XawTextSetInsertionPoint(edit, 9999);
6416 void DownKeyProc(w, event, prms, nprms)
6421 { // [HGM] input: let down-arrow recall next line from history
6426 if (!ICSInputBoxUp) return;
6427 edit = XtNameToWidget(ICSInputShell, "*form.text");
6428 val = NextInHistory();
6429 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6430 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6432 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6433 XawTextReplace(edit, 0, 0, &t);
6434 XawTextSetInsertionPoint(edit, 9999);
6438 void StopObservingProc(w, event, prms, nprms)
6444 StopObservingEvent();
6447 void StopExaminingProc(w, event, prms, nprms)
6453 StopExaminingEvent();
6456 void UploadProc(w, event, prms, nprms)
6466 void ForwardProc(w, event, prms, nprms)
6476 void BackwardProc(w, event, prms, nprms)
6485 void ToStartProc(w, event, prms, nprms)
6494 void ToEndProc(w, event, prms, nprms)
6503 void RevertProc(w, event, prms, nprms)
6512 void AnnotateProc(w, event, prms, nprms)
6521 void TruncateGameProc(w, event, prms, nprms)
6527 TruncateGameEvent();
6529 void RetractMoveProc(w, event, prms, nprms)
6538 void MoveNowProc(w, event, prms, nprms)
6547 void FlipViewProc(w, event, prms, nprms)
6553 flipView = !flipView;
6554 DrawPosition(True, NULL);
6557 void PonderNextMoveProc(w, event, prms, nprms)
6565 PonderNextMoveEvent(!appData.ponderNextMove);
6566 #ifndef OPTIONSDIALOG
6567 if (appData.ponderNextMove) {
6568 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6570 XtSetArg(args[0], XtNleftBitmap, None);
6572 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6577 #ifndef OPTIONSDIALOG
6578 void AlwaysQueenProc(w, event, prms, nprms)
6586 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6588 if (appData.alwaysPromoteToQueen) {
6589 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6591 XtSetArg(args[0], XtNleftBitmap, None);
6593 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6597 void AnimateDraggingProc(w, event, prms, nprms)
6605 appData.animateDragging = !appData.animateDragging;
6607 if (appData.animateDragging) {
6608 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6611 XtSetArg(args[0], XtNleftBitmap, None);
6613 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6617 void AnimateMovingProc(w, event, prms, nprms)
6625 appData.animate = !appData.animate;
6627 if (appData.animate) {
6628 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6631 XtSetArg(args[0], XtNleftBitmap, None);
6633 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6637 void AutoflagProc(w, event, prms, nprms)
6645 appData.autoCallFlag = !appData.autoCallFlag;
6647 if (appData.autoCallFlag) {
6648 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6650 XtSetArg(args[0], XtNleftBitmap, None);
6652 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6656 void AutoflipProc(w, event, prms, nprms)
6664 appData.autoFlipView = !appData.autoFlipView;
6666 if (appData.autoFlipView) {
6667 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6669 XtSetArg(args[0], XtNleftBitmap, None);
6671 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6675 void BlindfoldProc(w, event, prms, nprms)
6683 appData.blindfold = !appData.blindfold;
6685 if (appData.blindfold) {
6686 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6688 XtSetArg(args[0], XtNleftBitmap, None);
6690 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6693 DrawPosition(True, NULL);
6696 void TestLegalityProc(w, event, prms, nprms)
6704 appData.testLegality = !appData.testLegality;
6706 if (appData.testLegality) {
6707 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6709 XtSetArg(args[0], XtNleftBitmap, None);
6711 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6716 void FlashMovesProc(w, event, prms, nprms)
6724 if (appData.flashCount == 0) {
6725 appData.flashCount = 3;
6727 appData.flashCount = -appData.flashCount;
6730 if (appData.flashCount > 0) {
6731 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6733 XtSetArg(args[0], XtNleftBitmap, None);
6735 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6740 void HighlightDraggingProc(w, event, prms, nprms)
6748 appData.highlightDragging = !appData.highlightDragging;
6750 if (appData.highlightDragging) {
6751 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6753 XtSetArg(args[0], XtNleftBitmap, None);
6755 XtSetValues(XtNameToWidget(menuBarWidget,
6756 "menuOptions.Highlight Dragging"), args, 1);
6760 void HighlightLastMoveProc(w, event, prms, nprms)
6768 appData.highlightLastMove = !appData.highlightLastMove;
6770 if (appData.highlightLastMove) {
6771 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6773 XtSetArg(args[0], XtNleftBitmap, None);
6775 XtSetValues(XtNameToWidget(menuBarWidget,
6776 "menuOptions.Highlight Last Move"), args, 1);
6779 void HighlightArrowProc(w, event, prms, nprms)
6787 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6789 if (appData.highlightMoveWithArrow) {
6790 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6792 XtSetArg(args[0], XtNleftBitmap, None);
6794 XtSetValues(XtNameToWidget(menuBarWidget,
6795 "menuOptions.Arrow"), args, 1);
6799 void IcsAlarmProc(w, event, prms, nprms)
6807 appData.icsAlarm = !appData.icsAlarm;
6809 if (appData.icsAlarm) {
6810 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6812 XtSetArg(args[0], XtNleftBitmap, None);
6814 XtSetValues(XtNameToWidget(menuBarWidget,
6815 "menuOptions.ICS Alarm"), args, 1);
6819 void MoveSoundProc(w, event, prms, nprms)
6827 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6829 if (appData.ringBellAfterMoves) {
6830 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6832 XtSetArg(args[0], XtNleftBitmap, None);
6834 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6838 void OneClickProc(w, event, prms, nprms)
6846 appData.oneClick = !appData.oneClick;
6848 if (appData.oneClick) {
6849 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6851 XtSetArg(args[0], XtNleftBitmap, None);
6853 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6857 void PeriodicUpdatesProc(w, event, prms, nprms)
6865 PeriodicUpdatesEvent(!appData.periodicUpdates);
6867 if (appData.periodicUpdates) {
6868 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6870 XtSetArg(args[0], XtNleftBitmap, None);
6872 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6876 void PopupExitMessageProc(w, event, prms, nprms)
6884 appData.popupExitMessage = !appData.popupExitMessage;
6886 if (appData.popupExitMessage) {
6887 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6889 XtSetArg(args[0], XtNleftBitmap, None);
6891 XtSetValues(XtNameToWidget(menuBarWidget,
6892 "menuOptions.Popup Exit Message"), args, 1);
6895 void PopupMoveErrorsProc(w, event, prms, nprms)
6903 appData.popupMoveErrors = !appData.popupMoveErrors;
6905 if (appData.popupMoveErrors) {
6906 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6908 XtSetArg(args[0], XtNleftBitmap, None);
6910 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6915 void PremoveProc(w, event, prms, nprms)
6923 appData.premove = !appData.premove;
6925 if (appData.premove) {
6926 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6928 XtSetArg(args[0], XtNleftBitmap, None);
6930 XtSetValues(XtNameToWidget(menuBarWidget,
6931 "menuOptions.Premove"), args, 1);
6935 void ShowCoordsProc(w, event, prms, nprms)
6943 appData.showCoords = !appData.showCoords;
6945 if (appData.showCoords) {
6946 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6948 XtSetArg(args[0], XtNleftBitmap, None);
6950 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6953 DrawPosition(True, NULL);
6956 void ShowThinkingProc(w, event, prms, nprms)
6962 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6963 ShowThinkingEvent();
6966 void HideThinkingProc(w, event, prms, nprms)
6974 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6975 ShowThinkingEvent();
6977 if (appData.hideThinkingFromHuman) {
6978 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6980 XtSetArg(args[0], XtNleftBitmap, None);
6982 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6987 void SaveOnExitProc(w, event, prms, nprms)
6995 saveSettingsOnExit = !saveSettingsOnExit;
6997 if (saveSettingsOnExit) {
6998 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
7000 XtSetArg(args[0], XtNleftBitmap, None);
7002 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
7006 void SaveSettingsProc(w, event, prms, nprms)
7012 SaveSettings(settingsFileName);
7015 void InfoProc(w, event, prms, nprms)
7022 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
7027 void ManProc(w, event, prms, nprms)
7035 if (nprms && *nprms > 0)
7039 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
7043 void HintProc(w, event, prms, nprms)
7052 void BookProc(w, event, prms, nprms)
7061 void AboutProc(w, event, prms, nprms)
7069 char *zippy = " (with Zippy code)";
7073 snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
7074 programVersion, zippy,
7075 "Copyright 1991 Digital Equipment Corporation",
7076 "Enhancements Copyright 1992-2009 Free Software Foundation",
7077 "Enhancements Copyright 2005 Alessandro Scotti",
7078 PACKAGE, " is free software and carries NO WARRANTY;",
7079 "see the file COPYING for more information.");
7080 ErrorPopUp(_("About XBoard"), buf, FALSE);
7083 void DebugProc(w, event, prms, nprms)
7089 appData.debugMode = !appData.debugMode;
7092 void AboutGameProc(w, event, prms, nprms)
7101 void NothingProc(w, event, prms, nprms)
7110 void Iconify(w, event, prms, nprms)
7119 XtSetArg(args[0], XtNiconic, True);
7120 XtSetValues(shellWidget, args, 1);
7123 void DisplayMessage(message, extMessage)
7124 char *message, *extMessage;
7126 /* display a message in the message widget */
7135 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
7140 message = extMessage;
7144 /* need to test if messageWidget already exists, since this function
7145 can also be called during the startup, if for example a Xresource
7146 is not set up correctly */
7149 XtSetArg(arg, XtNlabel, message);
7150 XtSetValues(messageWidget, &arg, 1);
7156 void DisplayTitle(text)
7161 char title[MSG_SIZ];
7164 if (text == NULL) text = "";
7166 if (appData.titleInWindow) {
7168 XtSetArg(args[i], XtNlabel, text); i++;
7169 XtSetValues(titleWidget, args, i);
7172 if (*text != NULLCHAR) {
7173 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
7174 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
7175 } else if (appData.icsActive) {
7176 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7177 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7178 } else if (appData.cmailGameName[0] != NULLCHAR) {
7179 snprintf(icon, sizeof(icon), "%s", "CMail");
7180 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7182 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7183 } else if (gameInfo.variant == VariantGothic) {
7184 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7185 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
7188 } else if (gameInfo.variant == VariantFalcon) {
7189 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7190 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
7192 } else if (appData.noChessProgram) {
7193 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7194 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
7196 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
7197 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7200 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
7201 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
7202 XtSetValues(shellWidget, args, i);
7207 DisplayError(message, error)
7214 if (appData.debugMode || appData.matchMode) {
7215 fprintf(stderr, "%s: %s\n", programName, message);
7218 if (appData.debugMode || appData.matchMode) {
7219 fprintf(stderr, "%s: %s: %s\n",
7220 programName, message, strerror(error));
7222 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7225 ErrorPopUp(_("Error"), message, FALSE);
7229 void DisplayMoveError(message)
7234 DrawPosition(FALSE, NULL);
7235 if (appData.debugMode || appData.matchMode) {
7236 fprintf(stderr, "%s: %s\n", programName, message);
7238 if (appData.popupMoveErrors) {
7239 ErrorPopUp(_("Error"), message, FALSE);
7241 DisplayMessage(message, "");
7246 void DisplayFatalError(message, error, status)
7252 errorExitStatus = status;
7254 fprintf(stderr, "%s: %s\n", programName, message);
7256 fprintf(stderr, "%s: %s: %s\n",
7257 programName, message, strerror(error));
7258 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7261 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7262 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7268 void DisplayInformation(message)
7272 ErrorPopUp(_("Information"), message, TRUE);
7275 void DisplayNote(message)
7279 ErrorPopUp(_("Note"), message, FALSE);
7283 NullXErrorCheck(dpy, error_event)
7285 XErrorEvent *error_event;
7290 void DisplayIcsInteractionTitle(message)
7293 if (oldICSInteractionTitle == NULL) {
7294 /* Magic to find the old window title, adapted from vim */
7295 char *wina = getenv("WINDOWID");
7297 Window win = (Window) atoi(wina);
7298 Window root, parent, *children;
7299 unsigned int nchildren;
7300 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7302 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7303 if (!XQueryTree(xDisplay, win, &root, &parent,
7304 &children, &nchildren)) break;
7305 if (children) XFree((void *)children);
7306 if (parent == root || parent == 0) break;
7309 XSetErrorHandler(oldHandler);
7311 if (oldICSInteractionTitle == NULL) {
7312 oldICSInteractionTitle = "xterm";
7315 printf("\033]0;%s\007", message);
7319 char pendingReplyPrefix[MSG_SIZ];
7320 ProcRef pendingReplyPR;
7322 void AskQuestionProc(w, event, prms, nprms)
7329 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7333 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7336 void AskQuestionPopDown()
7338 if (!askQuestionUp) return;
7339 XtPopdown(askQuestionShell);
7340 XtDestroyWidget(askQuestionShell);
7341 askQuestionUp = False;
7344 void AskQuestionReplyAction(w, event, prms, nprms)
7354 reply = XawDialogGetValueString(w = XtParent(w));
7355 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7356 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7357 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7358 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7359 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7360 AskQuestionPopDown();
7362 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7365 void AskQuestionCallback(w, client_data, call_data)
7367 XtPointer client_data, call_data;
7372 XtSetArg(args[0], XtNlabel, &name);
7373 XtGetValues(w, args, 1);
7375 if (strcmp(name, _("cancel")) == 0) {
7376 AskQuestionPopDown();
7378 AskQuestionReplyAction(w, NULL, NULL, NULL);
7382 void AskQuestion(title, question, replyPrefix, pr)
7383 char *title, *question, *replyPrefix;
7387 Widget popup, layout, dialog, edit;
7393 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7394 pendingReplyPR = pr;
7397 XtSetArg(args[i], XtNresizable, True); i++;
7398 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7399 askQuestionShell = popup =
7400 XtCreatePopupShell(title, transientShellWidgetClass,
7401 shellWidget, args, i);
7404 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7405 layoutArgs, XtNumber(layoutArgs));
7408 XtSetArg(args[i], XtNlabel, question); i++;
7409 XtSetArg(args[i], XtNvalue, ""); i++;
7410 XtSetArg(args[i], XtNborderWidth, 0); i++;
7411 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7414 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7415 (XtPointer) dialog);
7416 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7417 (XtPointer) dialog);
7419 XtRealizeWidget(popup);
7420 CatchDeleteWindow(popup, "AskQuestionPopDown");
7422 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7423 &x, &y, &win_x, &win_y, &mask);
7425 XtSetArg(args[0], XtNx, x - 10);
7426 XtSetArg(args[1], XtNy, y - 30);
7427 XtSetValues(popup, args, 2);
7429 XtPopup(popup, XtGrabExclusive);
7430 askQuestionUp = True;
7432 edit = XtNameToWidget(dialog, "*value");
7433 XtSetKeyboardFocus(popup, edit);
7441 if (*name == NULLCHAR) {
7443 } else if (strcmp(name, "$") == 0) {
7444 putc(BELLCHAR, stderr);
7447 char *prefix = "", *sep = "";
7448 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7449 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7457 PlaySound(appData.soundMove);
7463 PlaySound(appData.soundIcsWin);
7469 PlaySound(appData.soundIcsLoss);
7475 PlaySound(appData.soundIcsDraw);
7479 PlayIcsUnfinishedSound()
7481 PlaySound(appData.soundIcsUnfinished);
7487 PlaySound(appData.soundIcsAlarm);
7493 system("stty echo");
7499 system("stty -echo");
7503 Colorize(cc, continuation)
7508 int count, outCount, error;
7510 if (textColors[(int)cc].bg > 0) {
7511 if (textColors[(int)cc].fg > 0) {
7512 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7513 textColors[(int)cc].fg, textColors[(int)cc].bg);
7515 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7516 textColors[(int)cc].bg);
7519 if (textColors[(int)cc].fg > 0) {
7520 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7521 textColors[(int)cc].fg);
7523 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7526 count = strlen(buf);
7527 outCount = OutputToProcess(NoProc, buf, count, &error);
7528 if (outCount < count) {
7529 DisplayFatalError(_("Error writing to display"), error, 1);
7532 if (continuation) return;
7535 PlaySound(appData.soundShout);
7538 PlaySound(appData.soundSShout);
7541 PlaySound(appData.soundChannel1);
7544 PlaySound(appData.soundChannel);
7547 PlaySound(appData.soundKibitz);
7550 PlaySound(appData.soundTell);
7552 case ColorChallenge:
7553 PlaySound(appData.soundChallenge);
7556 PlaySound(appData.soundRequest);
7559 PlaySound(appData.soundSeek);
7570 return getpwuid(getuid())->pw_name;
7574 ExpandPathName(path)
7577 static char static_buf[4*MSG_SIZ];
7578 char *d, *s, buf[4*MSG_SIZ];
7584 while (*s && isspace(*s))
7593 if (*(s+1) == '/') {
7594 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7598 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7599 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7600 pwd = getpwnam(buf);
7603 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7607 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7608 strcat(d, strchr(s+1, '/'));
7612 safeStrCpy(d, s, 4*MSG_SIZ );
7619 static char host_name[MSG_SIZ];
7621 #if HAVE_GETHOSTNAME
7622 gethostname(host_name, MSG_SIZ);
7624 #else /* not HAVE_GETHOSTNAME */
7625 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7626 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7628 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7630 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7631 #endif /* not HAVE_GETHOSTNAME */
7634 XtIntervalId delayedEventTimerXID = 0;
7635 DelayedEventCallback delayedEventCallback = 0;
7640 delayedEventTimerXID = 0;
7641 delayedEventCallback();
7645 ScheduleDelayedEvent(cb, millisec)
7646 DelayedEventCallback cb; long millisec;
7648 if(delayedEventTimerXID && delayedEventCallback == cb)
7649 // [HGM] alive: replace, rather than add or flush identical event
7650 XtRemoveTimeOut(delayedEventTimerXID);
7651 delayedEventCallback = cb;
7652 delayedEventTimerXID =
7653 XtAppAddTimeOut(appContext, millisec,
7654 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7657 DelayedEventCallback
7660 if (delayedEventTimerXID) {
7661 return delayedEventCallback;
7668 CancelDelayedEvent()
7670 if (delayedEventTimerXID) {
7671 XtRemoveTimeOut(delayedEventTimerXID);
7672 delayedEventTimerXID = 0;
7676 XtIntervalId loadGameTimerXID = 0;
7678 int LoadGameTimerRunning()
7680 return loadGameTimerXID != 0;
7683 int StopLoadGameTimer()
7685 if (loadGameTimerXID != 0) {
7686 XtRemoveTimeOut(loadGameTimerXID);
7687 loadGameTimerXID = 0;
7695 LoadGameTimerCallback(arg, id)
7699 loadGameTimerXID = 0;
7704 StartLoadGameTimer(millisec)
7708 XtAppAddTimeOut(appContext, millisec,
7709 (XtTimerCallbackProc) LoadGameTimerCallback,
7713 XtIntervalId analysisClockXID = 0;
7716 AnalysisClockCallback(arg, id)
7720 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7721 || appData.icsEngineAnalyze) { // [DM]
7722 AnalysisPeriodicEvent(0);
7723 StartAnalysisClock();
7728 StartAnalysisClock()
7731 XtAppAddTimeOut(appContext, 2000,
7732 (XtTimerCallbackProc) AnalysisClockCallback,
7736 XtIntervalId clockTimerXID = 0;
7738 int ClockTimerRunning()
7740 return clockTimerXID != 0;
7743 int StopClockTimer()
7745 if (clockTimerXID != 0) {
7746 XtRemoveTimeOut(clockTimerXID);
7755 ClockTimerCallback(arg, id)
7764 StartClockTimer(millisec)
7768 XtAppAddTimeOut(appContext, millisec,
7769 (XtTimerCallbackProc) ClockTimerCallback,
7774 DisplayTimerLabel(w, color, timer, highlight)
7783 /* check for low time warning */
7784 Pixel foregroundOrWarningColor = timerForegroundPixel;
7787 appData.lowTimeWarning &&
7788 (timer / 1000) < appData.icsAlarmTime)
7789 foregroundOrWarningColor = lowTimeWarningColor;
7791 if (appData.clockMode) {
7792 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7793 XtSetArg(args[0], XtNlabel, buf);
7795 snprintf(buf, MSG_SIZ, "%s ", color);
7796 XtSetArg(args[0], XtNlabel, buf);
7801 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7802 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7804 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7805 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7808 XtSetValues(w, args, 3);
7812 DisplayWhiteClock(timeRemaining, highlight)
7818 if(appData.noGUI) return;
7819 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7820 if (highlight && iconPixmap == bIconPixmap) {
7821 iconPixmap = wIconPixmap;
7822 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7823 XtSetValues(shellWidget, args, 1);
7828 DisplayBlackClock(timeRemaining, highlight)
7834 if(appData.noGUI) return;
7835 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7836 if (highlight && iconPixmap == wIconPixmap) {
7837 iconPixmap = bIconPixmap;
7838 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7839 XtSetValues(shellWidget, args, 1);
7857 int StartChildProcess(cmdLine, dir, pr)
7864 int to_prog[2], from_prog[2];
7868 if (appData.debugMode) {
7869 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7872 /* We do NOT feed the cmdLine to the shell; we just
7873 parse it into blank-separated arguments in the
7874 most simple-minded way possible.
7877 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7880 while(*p == ' ') p++;
7882 if(*p == '"' || *p == '\'')
7883 p = strchr(++argv[i-1], *p);
7884 else p = strchr(p, ' ');
7885 if (p == NULL) break;
7890 SetUpChildIO(to_prog, from_prog);
7892 if ((pid = fork()) == 0) {
7894 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7895 close(to_prog[1]); // first close the unused pipe ends
7896 close(from_prog[0]);
7897 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7898 dup2(from_prog[1], 1);
7899 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7900 close(from_prog[1]); // and closing again loses one of the pipes!
7901 if(fileno(stderr) >= 2) // better safe than sorry...
7902 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7904 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7909 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7911 execvp(argv[0], argv);
7913 /* If we get here, exec failed */
7918 /* Parent process */
7920 close(from_prog[1]);
7922 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7925 cp->fdFrom = from_prog[0];
7926 cp->fdTo = to_prog[1];
7931 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7932 static RETSIGTYPE AlarmCallBack(int n)
7938 DestroyChildProcess(pr, signalType)
7942 ChildProc *cp = (ChildProc *) pr;
7944 if (cp->kind != CPReal) return;
7946 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7947 signal(SIGALRM, AlarmCallBack);
7949 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7950 kill(cp->pid, SIGKILL); // kill it forcefully
7951 wait((int *) 0); // and wait again
7955 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7957 /* Process is exiting either because of the kill or because of
7958 a quit command sent by the backend; either way, wait for it to die.
7967 InterruptChildProcess(pr)
7970 ChildProc *cp = (ChildProc *) pr;
7972 if (cp->kind != CPReal) return;
7973 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7976 int OpenTelnet(host, port, pr)
7981 char cmdLine[MSG_SIZ];
7983 if (port[0] == NULLCHAR) {
7984 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7986 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7988 return StartChildProcess(cmdLine, "", pr);
7991 int OpenTCP(host, port, pr)
7997 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7998 #else /* !OMIT_SOCKETS */
8000 struct sockaddr_in sa;
8002 unsigned short uport;
8005 if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
8009 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
8010 sa.sin_family = AF_INET;
8011 sa.sin_addr.s_addr = INADDR_ANY;
8012 uport = (unsigned short) 0;
8013 sa.sin_port = htons(uport);
8014 if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
8018 memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
8019 if (!(hp = gethostbyname(host))) {
8021 if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
8022 hp = (struct hostent *) calloc(1, sizeof(struct hostent));
8023 hp->h_addrtype = AF_INET;
8025 hp->h_addr_list = (char **) calloc(2, sizeof(char *));
8026 hp->h_addr_list[0] = (char *) malloc(4);
8027 hp->h_addr_list[0][0] = b0;
8028 hp->h_addr_list[0][1] = b1;
8029 hp->h_addr_list[0][2] = b2;
8030 hp->h_addr_list[0][3] = b3;
8035 sa.sin_family = hp->h_addrtype;
8036 uport = (unsigned short) atoi(port);
8037 sa.sin_port = htons(uport);
8038 memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
8040 if (connect(s, (struct sockaddr *) &sa,
8041 sizeof(struct sockaddr_in)) < 0) {
8045 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8052 #endif /* !OMIT_SOCKETS */
8057 int OpenCommPort(name, pr)
8064 fd = open(name, 2, 0);
8065 if (fd < 0) return errno;
8067 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8077 int OpenLoopback(pr)
8083 SetUpChildIO(to, from);
8085 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8088 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
8095 int OpenRcmd(host, user, cmd, pr)
8096 char *host, *user, *cmd;
8099 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
8103 #define INPUT_SOURCE_BUF_SIZE 8192
8112 char buf[INPUT_SOURCE_BUF_SIZE];
8117 DoInputCallback(closure, source, xid)
8122 InputSource *is = (InputSource *) closure;
8127 if (is->lineByLine) {
8128 count = read(is->fd, is->unused,
8129 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
8131 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
8134 is->unused += count;
8136 while (p < is->unused) {
8137 q = memchr(p, '\n', is->unused - p);
8138 if (q == NULL) break;
8140 (is->func)(is, is->closure, p, q - p, 0);
8144 while (p < is->unused) {
8149 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8154 (is->func)(is, is->closure, is->buf, count, error);
8158 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8165 ChildProc *cp = (ChildProc *) pr;
8167 is = (InputSource *) calloc(1, sizeof(InputSource));
8168 is->lineByLine = lineByLine;
8172 is->fd = fileno(stdin);
8174 is->kind = cp->kind;
8175 is->fd = cp->fdFrom;
8178 is->unused = is->buf;
8181 is->xid = XtAppAddInput(appContext, is->fd,
8182 (XtPointer) (XtInputReadMask),
8183 (XtInputCallbackProc) DoInputCallback,
8185 is->closure = closure;
8186 return (InputSourceRef) is;
8190 RemoveInputSource(isr)
8193 InputSource *is = (InputSource *) isr;
8195 if (is->xid == 0) return;
8196 XtRemoveInput(is->xid);
8200 int OutputToProcess(pr, message, count, outError)
8206 static int line = 0;
8207 ChildProc *cp = (ChildProc *) pr;
8212 if (appData.noJoin || !appData.useInternalWrap)
8213 outCount = fwrite(message, 1, count, stdout);
8216 int width = get_term_width();
8217 int len = wrap(NULL, message, count, width, &line);
8218 char *msg = malloc(len);
8222 outCount = fwrite(message, 1, count, stdout);
8225 dbgchk = wrap(msg, message, count, width, &line);
8226 if (dbgchk != len && appData.debugMode)
8227 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8228 outCount = fwrite(msg, 1, dbgchk, stdout);
8234 outCount = write(cp->fdTo, message, count);
8244 /* Output message to process, with "ms" milliseconds of delay
8245 between each character. This is needed when sending the logon
8246 script to ICC, which for some reason doesn't like the
8247 instantaneous send. */
8248 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8255 ChildProc *cp = (ChildProc *) pr;
8260 r = write(cp->fdTo, message++, 1);
8273 /**** Animation code by Hugh Fisher, DCS, ANU.
8275 Known problem: if a window overlapping the board is
8276 moved away while a piece is being animated underneath,
8277 the newly exposed area won't be updated properly.
8278 I can live with this.
8280 Known problem: if you look carefully at the animation
8281 of pieces in mono mode, they are being drawn as solid
8282 shapes without interior detail while moving. Fixing
8283 this would be a major complication for minimal return.
8286 /* Masks for XPM pieces. Black and white pieces can have
8287 different shapes, but in the interest of retaining my
8288 sanity pieces must have the same outline on both light
8289 and dark squares, and all pieces must use the same
8290 background square colors/images. */
8292 static int xpmDone = 0;
8295 CreateAnimMasks (pieceDepth)
8302 unsigned long plane;
8305 /* Need a bitmap just to get a GC with right depth */
8306 buf = XCreatePixmap(xDisplay, xBoardWindow,
8308 values.foreground = 1;
8309 values.background = 0;
8310 /* Don't use XtGetGC, not read only */
8311 maskGC = XCreateGC(xDisplay, buf,
8312 GCForeground | GCBackground, &values);
8313 XFreePixmap(xDisplay, buf);
8315 buf = XCreatePixmap(xDisplay, xBoardWindow,
8316 squareSize, squareSize, pieceDepth);
8317 values.foreground = XBlackPixel(xDisplay, xScreen);
8318 values.background = XWhitePixel(xDisplay, xScreen);
8319 bufGC = XCreateGC(xDisplay, buf,
8320 GCForeground | GCBackground, &values);
8322 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8323 /* Begin with empty mask */
8324 if(!xpmDone) // [HGM] pieces: keep using existing
8325 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8326 squareSize, squareSize, 1);
8327 XSetFunction(xDisplay, maskGC, GXclear);
8328 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8329 0, 0, squareSize, squareSize);
8331 /* Take a copy of the piece */
8336 XSetFunction(xDisplay, bufGC, GXcopy);
8337 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8339 0, 0, squareSize, squareSize, 0, 0);
8341 /* XOR the background (light) over the piece */
8342 XSetFunction(xDisplay, bufGC, GXxor);
8344 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8345 0, 0, squareSize, squareSize, 0, 0);
8347 XSetForeground(xDisplay, bufGC, lightSquareColor);
8348 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8351 /* We now have an inverted piece image with the background
8352 erased. Construct mask by just selecting all the non-zero
8353 pixels - no need to reconstruct the original image. */
8354 XSetFunction(xDisplay, maskGC, GXor);
8356 /* Might be quicker to download an XImage and create bitmap
8357 data from it rather than this N copies per piece, but it
8358 only takes a fraction of a second and there is a much
8359 longer delay for loading the pieces. */
8360 for (n = 0; n < pieceDepth; n ++) {
8361 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8362 0, 0, squareSize, squareSize,
8368 XFreePixmap(xDisplay, buf);
8369 XFreeGC(xDisplay, bufGC);
8370 XFreeGC(xDisplay, maskGC);
8374 InitAnimState (anim, info)
8376 XWindowAttributes * info;
8381 /* Each buffer is square size, same depth as window */
8382 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8383 squareSize, squareSize, info->depth);
8384 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8385 squareSize, squareSize, info->depth);
8387 /* Create a plain GC for blitting */
8388 mask = GCForeground | GCBackground | GCFunction |
8389 GCPlaneMask | GCGraphicsExposures;
8390 values.foreground = XBlackPixel(xDisplay, xScreen);
8391 values.background = XWhitePixel(xDisplay, xScreen);
8392 values.function = GXcopy;
8393 values.plane_mask = AllPlanes;
8394 values.graphics_exposures = False;
8395 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8397 /* Piece will be copied from an existing context at
8398 the start of each new animation/drag. */
8399 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8401 /* Outline will be a read-only copy of an existing */
8402 anim->outlineGC = None;
8408 static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8409 XWindowAttributes info;
8411 if (xpmDone && gameInfo.variant == old) return;
8412 if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8413 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8415 InitAnimState(&game, &info);
8416 InitAnimState(&player, &info);
8418 /* For XPM pieces, we need bitmaps to use as masks. */
8420 CreateAnimMasks(info.depth);
8426 static Boolean frameWaiting;
8428 static RETSIGTYPE FrameAlarm (sig)
8431 frameWaiting = False;
8432 /* In case System-V style signals. Needed?? */
8433 signal(SIGALRM, FrameAlarm);
8440 struct itimerval delay;
8442 XSync(xDisplay, False);
8445 frameWaiting = True;
8446 signal(SIGALRM, FrameAlarm);
8447 delay.it_interval.tv_sec =
8448 delay.it_value.tv_sec = time / 1000;
8449 delay.it_interval.tv_usec =
8450 delay.it_value.tv_usec = (time % 1000) * 1000;
8451 setitimer(ITIMER_REAL, &delay, NULL);
8452 while (frameWaiting) pause();
8453 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8454 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8455 setitimer(ITIMER_REAL, &delay, NULL);
8465 XSync(xDisplay, False);
8467 usleep(time * 1000);
8472 /* Convert board position to corner of screen rect and color */
8475 ScreenSquare(column, row, pt, color)
8476 int column; int row; XPoint * pt; int * color;
8479 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8480 pt->y = lineGap + row * (squareSize + lineGap);
8482 pt->x = lineGap + column * (squareSize + lineGap);
8483 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8485 *color = SquareColor(row, column);
8488 /* Convert window coords to square */
8491 BoardSquare(x, y, column, row)
8492 int x; int y; int * column; int * row;
8494 *column = EventToSquare(x, BOARD_WIDTH);
8495 if (flipView && *column >= 0)
8496 *column = BOARD_WIDTH - 1 - *column;
8497 *row = EventToSquare(y, BOARD_HEIGHT);
8498 if (!flipView && *row >= 0)
8499 *row = BOARD_HEIGHT - 1 - *row;
8504 #undef Max /* just in case */
8506 #define Max(a, b) ((a) > (b) ? (a) : (b))
8507 #define Min(a, b) ((a) < (b) ? (a) : (b))
8510 SetRect(rect, x, y, width, height)
8511 XRectangle * rect; int x; int y; int width; int height;
8515 rect->width = width;
8516 rect->height = height;
8519 /* Test if two frames overlap. If they do, return
8520 intersection rect within old and location of
8521 that rect within new. */
8524 Intersect(old, new, size, area, pt)
8525 XPoint * old; XPoint * new;
8526 int size; XRectangle * area; XPoint * pt;
8528 if (old->x > new->x + size || new->x > old->x + size ||
8529 old->y > new->y + size || new->y > old->y + size) {
8532 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8533 size - abs(old->x - new->x), size - abs(old->y - new->y));
8534 pt->x = Max(old->x - new->x, 0);
8535 pt->y = Max(old->y - new->y, 0);
8540 /* For two overlapping frames, return the rect(s)
8541 in the old that do not intersect with the new. */
8544 CalcUpdateRects(old, new, size, update, nUpdates)
8545 XPoint * old; XPoint * new; int size;
8546 XRectangle update[]; int * nUpdates;
8550 /* If old = new (shouldn't happen) then nothing to draw */
8551 if (old->x == new->x && old->y == new->y) {
8555 /* Work out what bits overlap. Since we know the rects
8556 are the same size we don't need a full intersect calc. */
8558 /* Top or bottom edge? */
8559 if (new->y > old->y) {
8560 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8562 } else if (old->y > new->y) {
8563 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8564 size, old->y - new->y);
8567 /* Left or right edge - don't overlap any update calculated above. */
8568 if (new->x > old->x) {
8569 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8570 new->x - old->x, size - abs(new->y - old->y));
8572 } else if (old->x > new->x) {
8573 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8574 old->x - new->x, size - abs(new->y - old->y));
8581 /* Generate a series of frame coords from start->mid->finish.
8582 The movement rate doubles until the half way point is
8583 reached, then halves back down to the final destination,
8584 which gives a nice slow in/out effect. The algorithmn
8585 may seem to generate too many intermediates for short
8586 moves, but remember that the purpose is to attract the
8587 viewers attention to the piece about to be moved and
8588 then to where it ends up. Too few frames would be less
8592 Tween(start, mid, finish, factor, frames, nFrames)
8593 XPoint * start; XPoint * mid;
8594 XPoint * finish; int factor;
8595 XPoint frames[]; int * nFrames;
8597 int fraction, n, count;
8601 /* Slow in, stepping 1/16th, then 1/8th, ... */
8603 for (n = 0; n < factor; n++)
8605 for (n = 0; n < factor; n++) {
8606 frames[count].x = start->x + (mid->x - start->x) / fraction;
8607 frames[count].y = start->y + (mid->y - start->y) / fraction;
8609 fraction = fraction / 2;
8613 frames[count] = *mid;
8616 /* Slow out, stepping 1/2, then 1/4, ... */
8618 for (n = 0; n < factor; n++) {
8619 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8620 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8622 fraction = fraction * 2;
8627 /* Draw a piece on the screen without disturbing what's there */
8630 SelectGCMask(piece, clip, outline, mask)
8631 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8635 /* Bitmap for piece being moved. */
8636 if (appData.monoMode) {
8637 *mask = *pieceToSolid(piece);
8638 } else if (useImages) {
8640 *mask = xpmMask[piece];
8642 *mask = ximMaskPm[piece];
8645 *mask = *pieceToSolid(piece);
8648 /* GC for piece being moved. Square color doesn't matter, but
8649 since it gets modified we make a copy of the original. */
8651 if (appData.monoMode)
8656 if (appData.monoMode)
8661 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8663 /* Outline only used in mono mode and is not modified */
8665 *outline = bwPieceGC;
8667 *outline = wbPieceGC;
8671 OverlayPiece(piece, clip, outline, dest)
8672 ChessSquare piece; GC clip; GC outline; Drawable dest;
8677 /* Draw solid rectangle which will be clipped to shape of piece */
8678 XFillRectangle(xDisplay, dest, clip,
8679 0, 0, squareSize, squareSize);
8680 if (appData.monoMode)
8681 /* Also draw outline in contrasting color for black
8682 on black / white on white cases */
8683 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8684 0, 0, squareSize, squareSize, 0, 0, 1);
8686 /* Copy the piece */
8691 if(appData.upsideDown && flipView) kind ^= 2;
8692 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8694 0, 0, squareSize, squareSize,
8699 /* Animate the movement of a single piece */
8702 BeginAnimation(anim, piece, startColor, start)
8710 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8711 /* The old buffer is initialised with the start square (empty) */
8712 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8713 anim->prevFrame = *start;
8715 /* The piece will be drawn using its own bitmap as a matte */
8716 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8717 XSetClipMask(xDisplay, anim->pieceGC, mask);
8721 AnimationFrame(anim, frame, piece)
8726 XRectangle updates[4];
8731 /* Save what we are about to draw into the new buffer */
8732 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8733 frame->x, frame->y, squareSize, squareSize,
8736 /* Erase bits of the previous frame */
8737 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8738 /* Where the new frame overlapped the previous,
8739 the contents in newBuf are wrong. */
8740 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8741 overlap.x, overlap.y,
8742 overlap.width, overlap.height,
8744 /* Repaint the areas in the old that don't overlap new */
8745 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8746 for (i = 0; i < count; i++)
8747 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8748 updates[i].x - anim->prevFrame.x,
8749 updates[i].y - anim->prevFrame.y,
8750 updates[i].width, updates[i].height,
8751 updates[i].x, updates[i].y);
8753 /* Easy when no overlap */
8754 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8755 0, 0, squareSize, squareSize,
8756 anim->prevFrame.x, anim->prevFrame.y);
8759 /* Save this frame for next time round */
8760 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8761 0, 0, squareSize, squareSize,
8763 anim->prevFrame = *frame;
8765 /* Draw piece over original screen contents, not current,
8766 and copy entire rect. Wipes out overlapping piece images. */
8767 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8768 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8769 0, 0, squareSize, squareSize,
8770 frame->x, frame->y);
8774 EndAnimation (anim, finish)
8778 XRectangle updates[4];
8783 /* The main code will redraw the final square, so we
8784 only need to erase the bits that don't overlap. */
8785 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8786 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8787 for (i = 0; i < count; i++)
8788 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8789 updates[i].x - anim->prevFrame.x,
8790 updates[i].y - anim->prevFrame.y,
8791 updates[i].width, updates[i].height,
8792 updates[i].x, updates[i].y);
8794 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8795 0, 0, squareSize, squareSize,
8796 anim->prevFrame.x, anim->prevFrame.y);
8801 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8803 ChessSquare piece; int startColor;
8804 XPoint * start; XPoint * finish;
8805 XPoint frames[]; int nFrames;
8809 BeginAnimation(anim, piece, startColor, start);
8810 for (n = 0; n < nFrames; n++) {
8811 AnimationFrame(anim, &(frames[n]), piece);
8812 FrameDelay(appData.animSpeed);
8814 EndAnimation(anim, finish);
8818 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8821 ChessSquare piece = board[fromY][toY];
8822 board[fromY][toY] = EmptySquare;
8823 DrawPosition(FALSE, board);
8825 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8826 y = lineGap + toY * (squareSize + lineGap);
8828 x = lineGap + toX * (squareSize + lineGap);
8829 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8831 for(i=1; i<4*kFactor; i++) {
8832 int r = squareSize * 9 * i/(20*kFactor - 5);
8833 XFillArc(xDisplay, xBoardWindow, highlineGC,
8834 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8835 FrameDelay(appData.animSpeed);
8837 board[fromY][toY] = piece;
8840 /* Main control logic for deciding what to animate and how */
8843 AnimateMove(board, fromX, fromY, toX, toY)
8852 XPoint start, finish, mid;
8853 XPoint frames[kFactor * 2 + 1];
8854 int nFrames, startColor, endColor;
8856 /* Are we animating? */
8857 if (!appData.animate || appData.blindfold)
8860 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8861 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8862 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8864 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8865 piece = board[fromY][fromX];
8866 if (piece >= EmptySquare) return;
8871 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8874 if (appData.debugMode) {
8875 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8876 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8877 piece, fromX, fromY, toX, toY); }
8879 ScreenSquare(fromX, fromY, &start, &startColor);
8880 ScreenSquare(toX, toY, &finish, &endColor);
8883 /* Knight: make straight movement then diagonal */
8884 if (abs(toY - fromY) < abs(toX - fromX)) {
8885 mid.x = start.x + (finish.x - start.x) / 2;
8889 mid.y = start.y + (finish.y - start.y) / 2;
8892 mid.x = start.x + (finish.x - start.x) / 2;
8893 mid.y = start.y + (finish.y - start.y) / 2;
8896 /* Don't use as many frames for very short moves */
8897 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8898 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8900 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8901 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8902 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8904 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8905 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8908 /* Be sure end square is redrawn */
8909 damage[0][toY][toX] = True;
8913 DragPieceBegin(x, y)
8916 int boardX, boardY, color;
8919 /* Are we animating? */
8920 if (!appData.animateDragging || appData.blindfold)
8923 /* Figure out which square we start in and the
8924 mouse position relative to top left corner. */
8925 BoardSquare(x, y, &boardX, &boardY);
8926 player.startBoardX = boardX;
8927 player.startBoardY = boardY;
8928 ScreenSquare(boardX, boardY, &corner, &color);
8929 player.startSquare = corner;
8930 player.startColor = color;
8931 /* As soon as we start dragging, the piece will jump slightly to
8932 be centered over the mouse pointer. */
8933 player.mouseDelta.x = squareSize/2;
8934 player.mouseDelta.y = squareSize/2;
8935 /* Initialise animation */
8936 player.dragPiece = PieceForSquare(boardX, boardY);
8938 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8939 player.dragActive = True;
8940 BeginAnimation(&player, player.dragPiece, color, &corner);
8941 /* Mark this square as needing to be redrawn. Note that
8942 we don't remove the piece though, since logically (ie
8943 as seen by opponent) the move hasn't been made yet. */
8944 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8945 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8946 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8947 corner.x, corner.y, squareSize, squareSize,
8948 0, 0); // [HGM] zh: unstack in stead of grab
8949 if(gatingPiece != EmptySquare) {
8950 /* Kludge alert: When gating we want the introduced
8951 piece to appear on the from square. To generate an
8952 image of it, we draw it on the board, copy the image,
8953 and draw the original piece again. */
8954 ChessSquare piece = boards[currentMove][boardY][boardX];
8955 DrawSquare(boardY, boardX, gatingPiece, 0);
8956 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8957 corner.x, corner.y, squareSize, squareSize, 0, 0);
8958 DrawSquare(boardY, boardX, piece, 0);
8960 damage[0][boardY][boardX] = True;
8962 player.dragActive = False;
8972 /* Are we animating? */
8973 if (!appData.animateDragging || appData.blindfold)
8977 if (! player.dragActive)
8979 /* Move piece, maintaining same relative position
8980 of mouse within square */
8981 corner.x = x - player.mouseDelta.x;
8982 corner.y = y - player.mouseDelta.y;
8983 AnimationFrame(&player, &corner, player.dragPiece);
8985 if (appData.highlightDragging) {
8987 BoardSquare(x, y, &boardX, &boardY);
8988 SetHighlights(fromX, fromY, boardX, boardY);
8997 int boardX, boardY, color;
9000 /* Are we animating? */
9001 if (!appData.animateDragging || appData.blindfold)
9005 if (! player.dragActive)
9007 /* Last frame in sequence is square piece is
9008 placed on, which may not match mouse exactly. */
9009 BoardSquare(x, y, &boardX, &boardY);
9010 ScreenSquare(boardX, boardY, &corner, &color);
9011 EndAnimation(&player, &corner);
9013 /* Be sure end square is redrawn */
9014 damage[0][boardY][boardX] = True;
9016 /* This prevents weird things happening with fast successive
9017 clicks which on my Sun at least can cause motion events
9018 without corresponding press/release. */
9019 player.dragActive = False;
9022 /* Handle expose event while piece being dragged */
9027 if (!player.dragActive || appData.blindfold)
9030 /* What we're doing: logically, the move hasn't been made yet,
9031 so the piece is still in it's original square. But visually
9032 it's being dragged around the board. So we erase the square
9033 that the piece is on and draw it at the last known drag point. */
9034 BlankSquare(player.startSquare.x, player.startSquare.y,
9035 player.startColor, EmptySquare, xBoardWindow, 1);
9036 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
9037 damage[0][player.startBoardY][player.startBoardX] = TRUE;
9040 #include <sys/ioctl.h>
9041 int get_term_width()
9043 int fd, default_width;
9046 default_width = 79; // this is FICS default anyway...
9048 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
9050 if (!ioctl(fd, TIOCGSIZE, &win))
9051 default_width = win.ts_cols;
9052 #elif defined(TIOCGWINSZ)
9054 if (!ioctl(fd, TIOCGWINSZ, &win))
9055 default_width = win.ws_col;
9057 return default_width;
9063 static int old_width = 0;
9064 int new_width = get_term_width();
9066 if (old_width != new_width)
9067 ics_printf("set width %d\n", new_width);
9068 old_width = new_width;
9071 void NotifyFrontendLogin()
9076 /* [AS] Arrow highlighting support */
9078 static double A_WIDTH = 5; /* Width of arrow body */
9080 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
9081 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
9083 static double Sqr( double x )
9088 static int Round( double x )
9090 return (int) (x + 0.5);
9093 void SquareToPos(int rank, int file, int *x, int *y)
9096 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
9097 *y = lineGap + rank * (squareSize + lineGap);
9099 *x = lineGap + file * (squareSize + lineGap);
9100 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
9104 /* Draw an arrow between two points using current settings */
9105 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
9108 double dx, dy, j, k, x, y;
9111 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
9113 arrow[0].x = s_x + A_WIDTH + 0.5;
9116 arrow[1].x = s_x + A_WIDTH + 0.5;
9117 arrow[1].y = d_y - h;
9119 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9120 arrow[2].y = d_y - h;
9125 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
9126 arrow[5].y = d_y - h;
9128 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9129 arrow[4].y = d_y - h;
9131 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
9134 else if( d_y == s_y ) {
9135 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
9138 arrow[0].y = s_y + A_WIDTH + 0.5;
9140 arrow[1].x = d_x - w;
9141 arrow[1].y = s_y + A_WIDTH + 0.5;
9143 arrow[2].x = d_x - w;
9144 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9149 arrow[5].x = d_x - w;
9150 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
9152 arrow[4].x = d_x - w;
9153 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9156 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
9159 /* [AS] Needed a lot of paper for this! :-) */
9160 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
9161 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
9163 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
9165 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
9170 arrow[0].x = Round(x - j);
9171 arrow[0].y = Round(y + j*dx);
9173 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
9174 arrow[1].y = Round(arrow[0].y - 2*j*dx);
9177 x = (double) d_x - k;
9178 y = (double) d_y - k*dy;
9181 x = (double) d_x + k;
9182 y = (double) d_y + k*dy;
9185 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
9187 arrow[6].x = Round(x - j);
9188 arrow[6].y = Round(y + j*dx);
9190 arrow[2].x = Round(arrow[6].x + 2*j);
9191 arrow[2].y = Round(arrow[6].y - 2*j*dx);
9193 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
9194 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
9199 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9200 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9203 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9204 // Polygon( hdc, arrow, 7 );
9207 /* [AS] Draw an arrow between two squares */
9208 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9210 int s_x, s_y, d_x, d_y, hor, vert, i;
9212 if( s_col == d_col && s_row == d_row ) {
9216 /* Get source and destination points */
9217 SquareToPos( s_row, s_col, &s_x, &s_y);
9218 SquareToPos( d_row, d_col, &d_x, &d_y);
9221 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9223 else if( d_y < s_y ) {
9224 d_y += squareSize / 2 + squareSize / 4;
9227 d_y += squareSize / 2;
9231 d_x += squareSize / 2 - squareSize / 4;
9233 else if( d_x < s_x ) {
9234 d_x += squareSize / 2 + squareSize / 4;
9237 d_x += squareSize / 2;
9240 s_x += squareSize / 2;
9241 s_y += squareSize / 2;
9244 A_WIDTH = squareSize / 14.; //[HGM] make float
9246 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9248 hor = 64*s_col + 32; vert = 64*s_row + 32;
9249 for(i=0; i<= 64; i++) {
9250 damage[0][vert+6>>6][hor+6>>6] = True;
9251 damage[0][vert-6>>6][hor+6>>6] = True;
9252 damage[0][vert+6>>6][hor-6>>6] = True;
9253 damage[0][vert-6>>6][hor-6>>6] = True;
9254 hor += d_col - s_col; vert += d_row - s_row;
9258 Boolean IsDrawArrowEnabled()
9260 return appData.highlightMoveWithArrow && squareSize >= 32;
9263 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9265 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9266 DrawArrowBetweenSquares(fromX, fromY, toX, toY);