2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
243 int main P((int argc, char **argv));
244 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
245 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
246 RETSIGTYPE CmailSigHandler P((int sig));
247 RETSIGTYPE IntSigHandler P((int sig));
248 RETSIGTYPE TermSizeSigHandler P((int sig));
249 void CreateGCs P((int redo));
250 void CreateAnyPieces P((void));
251 void CreateXIMPieces P((void));
252 void CreateXPMPieces P((void));
253 void CreateXPMBoard P((char *s, int n));
254 void CreatePieces P((void));
255 void CreatePieceMenus P((void));
256 Widget CreateMenuBar P((Menu *mb));
257 Widget CreateButtonBar P ((MenuItem *mi));
259 char *InsertPxlSize P((char *pattern, int targetPxlSize));
260 XFontSet CreateFontSet P((char *base_fnt_lst));
262 char *FindFont P((char *pattern, int targetPxlSize));
264 void PieceMenuPopup P((Widget w, XEvent *event,
265 String *params, Cardinal *num_params));
266 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
267 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
268 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
269 u_int wreq, u_int hreq));
270 void CreateGrid P((void));
271 int EventToSquare P((int x, int limit));
272 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
273 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
274 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
275 void HandleUserMove P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AnimateUserMove P((Widget w, XEvent * event,
278 String * params, Cardinal * nParams));
279 void HandlePV P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void SelectPV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void StopPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void WhiteClock P((Widget w, XEvent *event,
286 String *prms, Cardinal *nprms));
287 void BlackClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void DrawPositionProc P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
293 void CommentClick P((Widget w, XEvent * event,
294 String * params, Cardinal * nParams));
295 void CommentPopUp P((char *title, char *label));
296 void CommentPopDown P((void));
297 void ICSInputBoxPopUp P((void));
298 void ICSInputBoxPopDown P((void));
299 void FileNamePopUp P((char *label, char *def, char *filter,
300 FileProc proc, char *openMode));
301 void FileNamePopDown P((void));
302 void FileNameCallback P((Widget w, XtPointer client_data,
303 XtPointer call_data));
304 void FileNameAction P((Widget w, XEvent *event,
305 String *prms, Cardinal *nprms));
306 void AskQuestionReplyAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionProc P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionPopDown P((void));
311 void PromotionPopDown P((void));
312 void PromotionCallback P((Widget w, XtPointer client_data,
313 XtPointer call_data));
314 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
315 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
316 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
317 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
319 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
321 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
323 void LoadPositionProc P((Widget w, XEvent *event,
324 String *prms, Cardinal *nprms));
325 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
327 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
329 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
331 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
333 void PastePositionProc P((Widget w, XEvent *event, String *prms,
335 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
337 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void SavePositionProc P((Widget w, XEvent *event,
340 String *prms, Cardinal *nprms));
341 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
344 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
348 void MachineWhiteProc P((Widget w, XEvent *event,
349 String *prms, Cardinal *nprms));
350 void AnalyzeModeProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeFileProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
356 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void IcsClientProc P((Widget w, XEvent *event, String *prms,
360 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void EditPositionProc P((Widget w, XEvent *event,
362 String *prms, Cardinal *nprms));
363 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void EditCommentProc P((Widget w, XEvent *event,
365 String *prms, Cardinal *nprms));
366 void IcsInputBoxProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void StopObservingProc P((Widget w, XEvent *event, String *prms,
385 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
387 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
388 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
389 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 Boolean TempBackwardActive = False;
393 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
395 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
396 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
399 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
401 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
402 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
404 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
406 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
408 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
409 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
413 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
414 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
416 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
418 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
420 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
421 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
422 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
425 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
427 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
429 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
431 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
434 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
436 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
438 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
440 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void DisplayMove P((int moveNumber));
451 void DisplayTitle P((char *title));
452 void ICSInitScript P((void));
453 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
454 void ErrorPopUp P((char *title, char *text, int modal));
455 void ErrorPopDown P((void));
456 static char *ExpandPathName P((char *path));
457 static void CreateAnimVars P((void));
458 static void DragPieceMove P((int x, int y));
459 static void DrawDragPiece P((void));
460 char *ModeToWidgetName P((GameMode mode));
461 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
477 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
478 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
479 void GameListOptionsPopDown P(());
480 void GenericPopDown P(());
481 void update_ics_width P(());
482 int get_term_width P(());
483 int CopyMemoProc P(());
484 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
485 Boolean IsDrawArrowEnabled P(());
488 * XBoard depends on Xt R4 or higher
490 int xtVersion = XtSpecificationRelease;
495 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
496 jailSquareColor, highlightSquareColor, premoveHighlightColor;
497 Pixel lowTimeWarningColor;
498 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
499 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
500 wjPieceGC, bjPieceGC, prelineGC, countGC;
501 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
502 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
503 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
504 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
505 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
506 ICSInputShell, fileNameShell, askQuestionShell;
507 Widget historyShell, evalGraphShell, gameListShell;
508 int hOffset; // [HGM] dual
509 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
510 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
511 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
513 XFontSet fontSet, clockFontSet;
516 XFontStruct *clockFontStruct;
518 Font coordFontID, countFontID;
519 XFontStruct *coordFontStruct, *countFontStruct;
520 XtAppContext appContext;
522 char *oldICSInteractionTitle;
526 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
528 Position commentX = -1, commentY = -1;
529 Dimension commentW, commentH;
530 typedef unsigned int BoardSize;
532 Boolean chessProgram;
534 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
535 int squareSize, smallLayout = 0, tinyLayout = 0,
536 marginW, marginH, // [HGM] for run-time resizing
537 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
538 ICSInputBoxUp = False, askQuestionUp = False,
539 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
540 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
541 Pixel timerForegroundPixel, timerBackgroundPixel;
542 Pixel buttonForegroundPixel, buttonBackgroundPixel;
543 char *chessDir, *programName, *programVersion,
544 *gameCopyFilename, *gamePasteFilename;
545 Boolean alwaysOnTop = False;
546 Boolean saveSettingsOnExit;
547 char *settingsFileName;
548 char *icsTextMenuString;
550 char *firstChessProgramNames;
551 char *secondChessProgramNames;
553 WindowPlacement wpMain;
554 WindowPlacement wpConsole;
555 WindowPlacement wpComment;
556 WindowPlacement wpMoveHistory;
557 WindowPlacement wpEvalGraph;
558 WindowPlacement wpEngineOutput;
559 WindowPlacement wpGameList;
560 WindowPlacement wpTags;
562 extern Widget shells[];
563 extern Boolean shellUp[];
567 Pixmap pieceBitmap[2][(int)BlackPawn];
568 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
569 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
570 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
571 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
572 Pixmap xpmBoardBitmap[2];
573 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
574 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
575 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
576 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
577 XImage *ximLightSquare, *ximDarkSquare;
580 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
581 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
583 #define White(piece) ((int)(piece) < (int)BlackPawn)
585 /* Variables for doing smooth animation. This whole thing
586 would be much easier if the board was double-buffered,
587 but that would require a fairly major rewrite. */
592 GC blitGC, pieceGC, outlineGC;
593 XPoint startSquare, prevFrame, mouseDelta;
597 int startBoardX, startBoardY;
600 /* There can be two pieces being animated at once: a player
601 can begin dragging a piece before the remote opponent has moved. */
603 static AnimState game, player;
605 /* Bitmaps for use as masks when drawing XPM pieces.
606 Need one for each black and white piece. */
607 static Pixmap xpmMask[BlackKing + 1];
609 /* This magic number is the number of intermediate frames used
610 in each half of the animation. For short moves it's reduced
611 by 1. The total number of frames will be factor * 2 + 1. */
614 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
616 MenuItem fileMenu[] = {
617 {N_("New Game Ctrl+N"), "New Game", ResetProc},
618 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
619 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
620 {"----", NULL, NothingProc},
621 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
622 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
623 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
624 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
625 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
626 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
627 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
628 {"----", NULL, NothingProc},
629 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
630 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
631 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
632 {"----", NULL, NothingProc},
633 {N_("Mail Move"), "Mail Move", MailMoveProc},
634 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
635 {"----", NULL, NothingProc},
636 {N_("Quit Ctr+Q"), "Exit", QuitProc},
640 MenuItem editMenu[] = {
641 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
642 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
643 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
644 {"----", NULL, NothingProc},
645 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
646 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
647 {"----", NULL, NothingProc},
648 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
649 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
650 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
651 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
652 {N_("Edit Book"), "Edit Book", EditBookProc},
653 {"----", NULL, NothingProc},
654 {N_("Revert Home"), "Revert", RevertProc},
655 {N_("Annotate"), "Annotate", AnnotateProc},
656 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
657 {"----", NULL, NothingProc},
658 {N_("Backward Alt+Left"), "Backward", BackwardProc},
659 {N_("Forward Alt+Right"), "Forward", ForwardProc},
660 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
661 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
665 MenuItem viewMenu[] = {
666 {N_("Flip View F2"), "Flip View", FlipViewProc},
667 {"----", NULL, NothingProc},
668 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
669 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
670 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
671 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
672 {N_("ICS text menu"), "ICStex", IcsTextProc},
673 {"----", NULL, NothingProc},
674 {N_("Tags"), "Show Tags", EditTagsProc},
675 {N_("Comments"), "Show Comments", EditCommentProc},
676 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
677 {"----", NULL, NothingProc},
678 {N_("Board..."), "Board Options", BoardOptionsProc},
679 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
683 MenuItem modeMenu[] = {
684 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
685 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
686 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
687 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
688 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
689 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
690 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
691 {N_("Training"), "Training", TrainingProc},
692 {N_("ICS Client"), "ICS Client", IcsClientProc},
693 {"----", NULL, NothingProc},
694 {N_("Machine Match"), "Machine Match", MatchProc},
695 {N_("Pause Pause"), "Pause", PauseProc},
699 MenuItem actionMenu[] = {
700 {N_("Accept F3"), "Accept", AcceptProc},
701 {N_("Decline F4"), "Decline", DeclineProc},
702 {N_("Rematch F12"), "Rematch", RematchProc},
703 {"----", NULL, NothingProc},
704 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
705 {N_("Draw F6"), "Draw", DrawProc},
706 {N_("Adjourn F7"), "Adjourn", AdjournProc},
707 {N_("Abort F8"),"Abort", AbortProc},
708 {N_("Resign F9"), "Resign", ResignProc},
709 {"----", NULL, NothingProc},
710 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
711 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
712 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
713 {"----", NULL, NothingProc},
714 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
715 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
716 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
720 MenuItem engineMenu[] = {
721 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
722 {"----", NULL, NothingProc},
723 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
724 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
725 {"----", NULL, NothingProc},
726 {N_("Hint"), "Hint", HintProc},
727 {N_("Book"), "Book", BookProc},
728 {"----", NULL, NothingProc},
729 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
730 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
734 MenuItem optionsMenu[] = {
735 #define OPTIONSDIALOG
737 {N_("General ..."), "General", OptionsProc},
739 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
740 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
741 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
742 {N_("ICS ..."), "ICS", IcsOptionsProc},
743 {N_("Match ..."), "Match", MatchOptionsProc},
744 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
745 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
746 // {N_(" ..."), "", OptionsProc},
747 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
748 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
749 {"----", NULL, NothingProc},
750 #ifndef OPTIONSDIALOG
751 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
752 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
753 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
754 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
755 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
756 {N_("Blindfold"), "Blindfold", BlindfoldProc},
757 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
759 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
761 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
762 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
763 {N_("Move Sound"), "Move Sound", MoveSoundProc},
764 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
765 {N_("One-Click Moving"), "OneClick", OneClickProc},
766 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
767 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
768 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
769 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
770 // {N_("Premove"), "Premove", PremoveProc},
771 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
772 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
773 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
774 {"----", NULL, NothingProc},
776 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
777 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
781 MenuItem helpMenu[] = {
782 {N_("Info XBoard"), "Info XBoard", InfoProc},
783 {N_("Man XBoard F1"), "Man XBoard", ManProc},
784 {"----", NULL, NothingProc},
785 {N_("About XBoard"), "About XBoard", AboutProc},
790 {N_("File"), "File", fileMenu},
791 {N_("Edit"), "Edit", editMenu},
792 {N_("View"), "View", viewMenu},
793 {N_("Mode"), "Mode", modeMenu},
794 {N_("Action"), "Action", actionMenu},
795 {N_("Engine"), "Engine", engineMenu},
796 {N_("Options"), "Options", optionsMenu},
797 {N_("Help"), "Help", helpMenu},
801 #define PAUSE_BUTTON "P"
802 MenuItem buttonBar[] = {
803 {"<<", "<<", ToStartProc},
804 {"<", "<", BackwardProc},
805 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
806 {">", ">", ForwardProc},
807 {">>", ">>", ToEndProc},
811 #define PIECE_MENU_SIZE 18
812 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
813 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
814 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
815 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
816 N_("Empty square"), N_("Clear board") },
817 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
818 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
819 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
820 N_("Empty square"), N_("Clear board") }
822 /* must be in same order as pieceMenuStrings! */
823 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
824 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
825 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
826 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
827 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
828 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
829 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
830 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
831 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
834 #define DROP_MENU_SIZE 6
835 String dropMenuStrings[DROP_MENU_SIZE] = {
836 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
838 /* must be in same order as dropMenuStrings! */
839 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
840 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
841 WhiteRook, WhiteQueen
849 DropMenuEnables dmEnables[] = {
867 { XtNborderWidth, 0 },
868 { XtNdefaultDistance, 0 },
872 { XtNborderWidth, 0 },
873 { XtNresizable, (XtArgVal) True },
877 { XtNborderWidth, 0 },
883 { XtNjustify, (XtArgVal) XtJustifyRight },
884 { XtNlabel, (XtArgVal) "..." },
885 { XtNresizable, (XtArgVal) True },
886 { XtNresize, (XtArgVal) False }
889 Arg messageArgs[] = {
890 { XtNjustify, (XtArgVal) XtJustifyLeft },
891 { XtNlabel, (XtArgVal) "..." },
892 { XtNresizable, (XtArgVal) True },
893 { XtNresize, (XtArgVal) False }
897 { XtNborderWidth, 0 },
898 { XtNjustify, (XtArgVal) XtJustifyLeft }
901 XtResource clientResources[] = {
902 { "flashCount", "flashCount", XtRInt, sizeof(int),
903 XtOffset(AppDataPtr, flashCount), XtRImmediate,
904 (XtPointer) FLASH_COUNT },
907 XrmOptionDescRec shellOptions[] = {
908 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
909 { "-flash", "flashCount", XrmoptionNoArg, "3" },
910 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
913 XtActionsRec boardActions[] = {
914 { "DrawPosition", DrawPositionProc },
915 { "HandleUserMove", HandleUserMove },
916 { "AnimateUserMove", AnimateUserMove },
917 { "HandlePV", HandlePV },
918 { "SelectPV", SelectPV },
919 { "StopPV", StopPV },
920 { "FileNameAction", FileNameAction },
921 { "AskQuestionProc", AskQuestionProc },
922 { "AskQuestionReplyAction", AskQuestionReplyAction },
923 { "PieceMenuPopup", PieceMenuPopup },
924 { "WhiteClock", WhiteClock },
925 { "BlackClock", BlackClock },
926 { "ResetProc", ResetProc },
927 { "NewVariantProc", NewVariantProc },
928 { "LoadGameProc", LoadGameProc },
929 { "LoadNextGameProc", LoadNextGameProc },
930 { "LoadPrevGameProc", LoadPrevGameProc },
931 { "LoadSelectedProc", LoadSelectedProc },
932 { "SetFilterProc", SetFilterProc },
933 { "ReloadGameProc", ReloadGameProc },
934 { "LoadPositionProc", LoadPositionProc },
935 { "LoadNextPositionProc", LoadNextPositionProc },
936 { "LoadPrevPositionProc", LoadPrevPositionProc },
937 { "ReloadPositionProc", ReloadPositionProc },
938 { "CopyPositionProc", CopyPositionProc },
939 { "PastePositionProc", PastePositionProc },
940 { "CopyGameProc", CopyGameProc },
941 { "CopyGameListProc", CopyGameListProc },
942 { "PasteGameProc", PasteGameProc },
943 { "SaveGameProc", SaveGameProc },
944 { "SavePositionProc", SavePositionProc },
945 { "MailMoveProc", MailMoveProc },
946 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
947 { "QuitProc", QuitProc },
948 { "MachineWhiteProc", MachineWhiteProc },
949 { "MachineBlackProc", MachineBlackProc },
950 { "AnalysisModeProc", AnalyzeModeProc },
951 { "AnalyzeFileProc", AnalyzeFileProc },
952 { "TwoMachinesProc", TwoMachinesProc },
953 { "IcsClientProc", IcsClientProc },
954 { "EditGameProc", EditGameProc },
955 { "EditPositionProc", EditPositionProc },
956 { "TrainingProc", EditPositionProc },
957 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
958 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
959 { "ShowGameListProc", ShowGameListProc },
960 { "ShowMoveListProc", HistoryShowProc},
961 { "EditTagsProc", EditCommentProc },
962 { "EditBookProc", EditBookProc },
963 { "EditCommentProc", EditCommentProc },
964 { "IcsInputBoxProc", IcsInputBoxProc },
965 { "PauseProc", PauseProc },
966 { "AcceptProc", AcceptProc },
967 { "DeclineProc", DeclineProc },
968 { "RematchProc", RematchProc },
969 { "CallFlagProc", CallFlagProc },
970 { "DrawProc", DrawProc },
971 { "AdjournProc", AdjournProc },
972 { "AbortProc", AbortProc },
973 { "ResignProc", ResignProc },
974 { "AdjuWhiteProc", AdjuWhiteProc },
975 { "AdjuBlackProc", AdjuBlackProc },
976 { "AdjuDrawProc", AdjuDrawProc },
977 { "TypeInProc", TypeInProc },
978 { "EnterKeyProc", EnterKeyProc },
979 { "UpKeyProc", UpKeyProc },
980 { "DownKeyProc", DownKeyProc },
981 { "StopObservingProc", StopObservingProc },
982 { "StopExaminingProc", StopExaminingProc },
983 { "UploadProc", UploadProc },
984 { "BackwardProc", BackwardProc },
985 { "ForwardProc", ForwardProc },
986 { "TempBackwardProc", TempBackwardProc },
987 { "TempForwardProc", TempForwardProc },
988 { "ToStartProc", ToStartProc },
989 { "ToEndProc", ToEndProc },
990 { "RevertProc", RevertProc },
991 { "AnnotateProc", AnnotateProc },
992 { "TruncateGameProc", TruncateGameProc },
993 { "MoveNowProc", MoveNowProc },
994 { "RetractMoveProc", RetractMoveProc },
995 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
996 { "UciMenuProc", (XtActionProc) UciMenuProc },
997 { "TimeControlProc", (XtActionProc) TimeControlProc },
998 { "FlipViewProc", FlipViewProc },
999 { "PonderNextMoveProc", PonderNextMoveProc },
1000 #ifndef OPTIONSDIALOG
1001 { "AlwaysQueenProc", AlwaysQueenProc },
1002 { "AnimateDraggingProc", AnimateDraggingProc },
1003 { "AnimateMovingProc", AnimateMovingProc },
1004 { "AutoflagProc", AutoflagProc },
1005 { "AutoflipProc", AutoflipProc },
1006 { "BlindfoldProc", BlindfoldProc },
1007 { "FlashMovesProc", FlashMovesProc },
1009 { "HighlightDraggingProc", HighlightDraggingProc },
1011 { "HighlightLastMoveProc", HighlightLastMoveProc },
1012 // { "IcsAlarmProc", IcsAlarmProc },
1013 { "MoveSoundProc", MoveSoundProc },
1014 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1015 { "PopupExitMessageProc", PopupExitMessageProc },
1016 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1017 // { "PremoveProc", PremoveProc },
1018 { "ShowCoordsProc", ShowCoordsProc },
1019 { "ShowThinkingProc", ShowThinkingProc },
1020 { "HideThinkingProc", HideThinkingProc },
1021 { "TestLegalityProc", TestLegalityProc },
1023 { "SaveSettingsProc", SaveSettingsProc },
1024 { "SaveOnExitProc", SaveOnExitProc },
1025 { "InfoProc", InfoProc },
1026 { "ManProc", ManProc },
1027 { "HintProc", HintProc },
1028 { "BookProc", BookProc },
1029 { "AboutGameProc", AboutGameProc },
1030 { "AboutProc", AboutProc },
1031 { "DebugProc", DebugProc },
1032 { "NothingProc", NothingProc },
1033 { "CommentClick", (XtActionProc) CommentClick },
1034 { "CommentPopDown", (XtActionProc) CommentPopDown },
1035 { "TagsPopDown", (XtActionProc) TagsPopDown },
1036 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1037 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1038 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1039 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1040 { "GameListPopDown", (XtActionProc) GameListPopDown },
1041 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1042 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1043 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1044 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1045 { "GenericPopDown", (XtActionProc) GenericPopDown },
1046 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1047 { "SelectMove", (XtActionProc) SelectMove },
1050 char globalTranslations[] =
1051 ":<Key>F9: ResignProc() \n \
1052 :Ctrl<Key>n: ResetProc() \n \
1053 :Meta<Key>V: NewVariantProc() \n \
1054 :Ctrl<Key>o: LoadGameProc() \n \
1055 :Meta<Key>Next: LoadNextGameProc() \n \
1056 :Meta<Key>Prior: LoadPrevGameProc() \n \
1057 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1058 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1059 :Ctrl<Key>s: SaveGameProc() \n \
1060 :Ctrl<Key>c: CopyGameProc() \n \
1061 :Ctrl<Key>v: PasteGameProc() \n \
1062 :Ctrl<Key>O: LoadPositionProc() \n \
1063 :Shift<Key>Next: LoadNextPositionProc() \n \
1064 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1065 :Ctrl<Key>S: SavePositionProc() \n \
1066 :Ctrl<Key>C: CopyPositionProc() \n \
1067 :Ctrl<Key>V: PastePositionProc() \n \
1068 :Ctrl<Key>q: QuitProc() \n \
1069 :Ctrl<Key>w: MachineWhiteProc() \n \
1070 :Ctrl<Key>b: MachineBlackProc() \n \
1071 :Ctrl<Key>t: TwoMachinesProc() \n \
1072 :Ctrl<Key>a: AnalysisModeProc() \n \
1073 :Ctrl<Key>g: AnalyzeFileProc() \n \
1074 :Ctrl<Key>e: EditGameProc() \n \
1075 :Ctrl<Key>E: EditPositionProc() \n \
1076 :Meta<Key>O: EngineOutputProc() \n \
1077 :Meta<Key>E: EvalGraphProc() \n \
1078 :Meta<Key>G: ShowGameListProc() \n \
1079 :Meta<Key>H: ShowMoveListProc() \n \
1080 :<Key>Pause: PauseProc() \n \
1081 :<Key>F3: AcceptProc() \n \
1082 :<Key>F4: DeclineProc() \n \
1083 :<Key>F12: RematchProc() \n \
1084 :<Key>F5: CallFlagProc() \n \
1085 :<Key>F6: DrawProc() \n \
1086 :<Key>F7: AdjournProc() \n \
1087 :<Key>F8: AbortProc() \n \
1088 :<Key>F10: StopObservingProc() \n \
1089 :<Key>F11: StopExaminingProc() \n \
1090 :Meta Ctrl<Key>F12: DebugProc() \n \
1091 :Meta<Key>End: ToEndProc() \n \
1092 :Meta<Key>Right: ForwardProc() \n \
1093 :Meta<Key>Home: ToStartProc() \n \
1094 :Meta<Key>Left: BackwardProc() \n \
1095 :<Key>Left: BackwardProc() \n \
1096 :<Key>Right: ForwardProc() \n \
1097 :<Key>Home: RevertProc() \n \
1098 :<Key>End: TruncateGameProc() \n \
1099 :Ctrl<Key>m: MoveNowProc() \n \
1100 :Ctrl<Key>x: RetractMoveProc() \n \
1101 :Meta<Key>J: EngineMenuProc() \n \
1102 :Meta<Key>U: UciMenuProc() \n \
1103 :Meta<Key>T: TimeControlProc() \n \
1104 :Ctrl<Key>P: PonderNextMoveProc() \n "
1105 #ifndef OPTIONSDIALOG
1107 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1108 :Ctrl<Key>F: AutoflagProc() \n \
1109 :Ctrl<Key>A: AnimateMovingProc() \n \
1110 :Ctrl<Key>L: TestLegalityProc() \n \
1111 :Ctrl<Key>H: HideThinkingProc() \n "
1114 :<Key>F1: ManProc() \n \
1115 :<Key>F2: FlipViewProc() \n \
1116 :Ctrl<KeyDown>.: TempBackwardProc() \n \
1117 :Ctrl<KeyUp>.: TempForwardProc() \n \
1118 :Ctrl<Key>1: AskQuestionProc(\"Direct command\",\
1119 \"Send to chess program:\",,1) \n \
1120 :Ctrl<Key>2: AskQuestionProc(\"Direct command\",\
1121 \"Send to second chess program:\",,2) \n";
1123 char boardTranslations[] =
1124 "<Btn1Down>: HandleUserMove(0) \n \
1125 Shift<Btn1Up>: HandleUserMove(1) \n \
1126 <Btn1Up>: HandleUserMove(0) \n \
1127 <Btn1Motion>: AnimateUserMove() \n \
1128 <Btn3Motion>: HandlePV() \n \
1129 <Btn3Up>: PieceMenuPopup(menuB) \n \
1130 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1131 PieceMenuPopup(menuB) \n \
1132 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1133 PieceMenuPopup(menuW) \n \
1134 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1135 PieceMenuPopup(menuW) \n \
1136 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1137 PieceMenuPopup(menuB) \n";
1139 char whiteTranslations[] =
1140 "Shift<BtnDown>: WhiteClock(1)\n \
1141 <BtnDown>: WhiteClock(0)\n";
1142 char blackTranslations[] =
1143 "Shift<BtnDown>: BlackClock(1)\n \
1144 <BtnDown>: BlackClock(0)\n";
1146 char ICSInputTranslations[] =
1147 "<Key>Up: UpKeyProc() \n "
1148 "<Key>Down: DownKeyProc() \n "
1149 "<Key>Return: EnterKeyProc() \n";
1151 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1152 // as the widget is destroyed before the up-click can call extend-end
1153 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1155 String xboardResources[] = {
1156 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1157 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1158 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1163 /* Max possible square size */
1164 #define MAXSQSIZE 256
1166 static int xpm_avail[MAXSQSIZE];
1168 #ifdef HAVE_DIR_STRUCT
1170 /* Extract piece size from filename */
1172 xpm_getsize(name, len, ext)
1183 if ((p=strchr(name, '.')) == NULL ||
1184 StrCaseCmp(p+1, ext) != 0)
1190 while (*p && isdigit(*p))
1197 /* Setup xpm_avail */
1199 xpm_getavail(dirname, ext)
1207 for (i=0; i<MAXSQSIZE; ++i)
1210 if (appData.debugMode)
1211 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1213 dir = opendir(dirname);
1216 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1217 programName, dirname);
1221 while ((ent=readdir(dir)) != NULL) {
1222 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1223 if (i > 0 && i < MAXSQSIZE)
1233 xpm_print_avail(fp, ext)
1239 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1240 for (i=1; i<MAXSQSIZE; ++i) {
1246 /* Return XPM piecesize closest to size */
1248 xpm_closest_to(dirname, size, ext)
1254 int sm_diff = MAXSQSIZE;
1258 xpm_getavail(dirname, ext);
1260 if (appData.debugMode)
1261 xpm_print_avail(stderr, ext);
1263 for (i=1; i<MAXSQSIZE; ++i) {
1266 diff = (diff<0) ? -diff : diff;
1267 if (diff < sm_diff) {
1275 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1281 #else /* !HAVE_DIR_STRUCT */
1282 /* If we are on a system without a DIR struct, we can't
1283 read the directory, so we can't collect a list of
1284 filenames, etc., so we can't do any size-fitting. */
1286 xpm_closest_to(dirname, size, ext)
1291 fprintf(stderr, _("\
1292 Warning: No DIR structure found on this system --\n\
1293 Unable to autosize for XPM/XIM pieces.\n\
1294 Please report this error to %s.\n\
1295 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1298 #endif /* HAVE_DIR_STRUCT */
1300 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1301 "magenta", "cyan", "white" };
1305 TextColors textColors[(int)NColorClasses];
1307 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1309 parse_color(str, which)
1313 char *p, buf[100], *d;
1316 if (strlen(str) > 99) /* watch bounds on buf */
1321 for (i=0; i<which; ++i) {
1328 /* Could be looking at something like:
1330 .. in which case we want to stop on a comma also */
1331 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1335 return -1; /* Use default for empty field */
1338 if (which == 2 || isdigit(*p))
1341 while (*p && isalpha(*p))
1346 for (i=0; i<8; ++i) {
1347 if (!StrCaseCmp(buf, cnames[i]))
1348 return which? (i+40) : (i+30);
1350 if (!StrCaseCmp(buf, "default")) return -1;
1352 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1357 parse_cpair(cc, str)
1361 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1362 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1367 /* bg and attr are optional */
1368 textColors[(int)cc].bg = parse_color(str, 1);
1369 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1370 textColors[(int)cc].attr = 0;
1376 /* Arrange to catch delete-window events */
1377 Atom wm_delete_window;
1379 CatchDeleteWindow(Widget w, String procname)
1382 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1383 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1384 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1391 XtSetArg(args[0], XtNiconic, False);
1392 XtSetValues(shellWidget, args, 1);
1394 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1397 //---------------------------------------------------------------------------------------------------------
1398 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1401 #define CW_USEDEFAULT (1<<31)
1402 #define ICS_TEXT_MENU_SIZE 90
1403 #define DEBUG_FILE "xboard.debug"
1404 #define SetCurrentDirectory chdir
1405 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1409 // these two must some day move to frontend.h, when they are implemented
1410 Boolean GameListIsUp();
1412 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1415 // front-end part of option handling
1417 // [HGM] This platform-dependent table provides the location for storing the color info
1418 extern char *crWhite, * crBlack;
1422 &appData.whitePieceColor,
1423 &appData.blackPieceColor,
1424 &appData.lightSquareColor,
1425 &appData.darkSquareColor,
1426 &appData.highlightSquareColor,
1427 &appData.premoveHighlightColor,
1428 &appData.lowTimeWarningColor,
1439 // [HGM] font: keep a font for each square size, even non-stndard ones
1440 #define NUM_SIZES 18
1441 #define MAX_SIZE 130
1442 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1443 char *fontTable[NUM_FONTS][MAX_SIZE];
1446 ParseFont(char *name, int number)
1447 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1449 if(sscanf(name, "size%d:", &size)) {
1450 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1451 // defer processing it until we know if it matches our board size
1452 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1453 fontTable[number][size] = strdup(strchr(name, ':')+1);
1454 fontValid[number][size] = True;
1459 case 0: // CLOCK_FONT
1460 appData.clockFont = strdup(name);
1462 case 1: // MESSAGE_FONT
1463 appData.font = strdup(name);
1465 case 2: // COORD_FONT
1466 appData.coordFont = strdup(name);
1471 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1476 { // only 2 fonts currently
1477 appData.clockFont = CLOCK_FONT_NAME;
1478 appData.coordFont = COORD_FONT_NAME;
1479 appData.font = DEFAULT_FONT_NAME;
1484 { // no-op, until we identify the code for this already in XBoard and move it here
1488 ParseColor(int n, char *name)
1489 { // in XBoard, just copy the color-name string
1490 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1494 ParseTextAttribs(ColorClass cc, char *s)
1496 (&appData.colorShout)[cc] = strdup(s);
1500 ParseBoardSize(void *addr, char *name)
1502 appData.boardSize = strdup(name);
1507 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1511 SetCommPortDefaults()
1512 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1515 // [HGM] args: these three cases taken out to stay in front-end
1517 SaveFontArg(FILE *f, ArgDescriptor *ad)
1520 int i, n = (int)(intptr_t)ad->argLoc;
1522 case 0: // CLOCK_FONT
1523 name = appData.clockFont;
1525 case 1: // MESSAGE_FONT
1526 name = appData.font;
1528 case 2: // COORD_FONT
1529 name = appData.coordFont;
1534 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1535 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1536 fontTable[n][squareSize] = strdup(name);
1537 fontValid[n][squareSize] = True;
1540 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1541 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1546 { // nothing to do, as the sounds are at all times represented by their text-string names already
1550 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1551 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1552 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1556 SaveColor(FILE *f, ArgDescriptor *ad)
1557 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1558 if(colorVariable[(int)(intptr_t)ad->argLoc])
1559 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1563 SaveBoardSize(FILE *f, char *name, void *addr)
1564 { // wrapper to shield back-end from BoardSize & sizeInfo
1565 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1569 ParseCommPortSettings(char *s)
1570 { // no such option in XBoard (yet)
1573 extern Widget engineOutputShell;
1576 GetActualPlacement(Widget wg, WindowPlacement *wp)
1586 XtSetArg(args[i], XtNx, &x); i++;
1587 XtSetArg(args[i], XtNy, &y); i++;
1588 XtSetArg(args[i], XtNwidth, &w); i++;
1589 XtSetArg(args[i], XtNheight, &h); i++;
1590 XtGetValues(wg, args, i);
1599 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1600 // In XBoard this will have to wait until awareness of window parameters is implemented
1601 GetActualPlacement(shellWidget, &wpMain);
1602 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1603 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1604 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1605 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1606 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1607 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1611 PrintCommPortSettings(FILE *f, char *name)
1612 { // This option does not exist in XBoard
1616 MySearchPath(char *installDir, char *name, char *fullname)
1617 { // just append installDir and name. Perhaps ExpandPath should be used here?
1618 name = ExpandPathName(name);
1619 if(name && name[0] == '/')
1620 safeStrCpy(fullname, name, MSG_SIZ );
1622 sprintf(fullname, "%s%c%s", installDir, '/', name);
1628 MyGetFullPathName(char *name, char *fullname)
1629 { // should use ExpandPath?
1630 name = ExpandPathName(name);
1631 safeStrCpy(fullname, name, MSG_SIZ );
1636 EnsureOnScreen(int *x, int *y, int minX, int minY)
1643 { // [HGM] args: allows testing if main window is realized from back-end
1644 return xBoardWindow != 0;
1648 PopUpStartupDialog()
1649 { // start menu not implemented in XBoard
1653 ConvertToLine(int argc, char **argv)
1655 static char line[128*1024], buf[1024];
1659 for(i=1; i<argc; i++)
1661 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1662 && argv[i][0] != '{' )
1663 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1665 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1666 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1669 line[strlen(line)-1] = NULLCHAR;
1673 //--------------------------------------------------------------------------------------------
1675 extern Boolean twoBoards, partnerUp;
1678 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1680 #define BoardSize int
1681 void InitDrawingSizes(BoardSize boardSize, int flags)
1682 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1683 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1685 XtGeometryResult gres;
1687 static Dimension oldWidth, oldHeight;
1688 static VariantClass oldVariant;
1689 static int oldDual = -1;
1691 if(!formWidget) return;
1693 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1694 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1695 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1697 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1699 * Enable shell resizing.
1701 shellArgs[0].value = (XtArgVal) &w;
1702 shellArgs[1].value = (XtArgVal) &h;
1703 XtGetValues(shellWidget, shellArgs, 2);
1705 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1706 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1707 XtSetValues(shellWidget, &shellArgs[2], 4);
1709 XtSetArg(args[0], XtNdefaultDistance, &sep);
1710 XtGetValues(formWidget, args, 1);
1712 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1714 hOffset = boardWidth + 10;
1715 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1716 secondSegments[i] = gridSegments[i];
1717 secondSegments[i].x1 += hOffset;
1718 secondSegments[i].x2 += hOffset;
1721 XtSetArg(args[0], XtNwidth, boardWidth);
1722 XtSetArg(args[1], XtNheight, boardHeight);
1723 XtSetValues(boardWidget, args, 2);
1725 timerWidth = (boardWidth - sep) / 2;
1726 XtSetArg(args[0], XtNwidth, timerWidth);
1727 XtSetValues(whiteTimerWidget, args, 1);
1728 XtSetValues(blackTimerWidget, args, 1);
1730 XawFormDoLayout(formWidget, False);
1732 if (appData.titleInWindow) {
1734 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1735 XtSetArg(args[i], XtNheight, &h); i++;
1736 XtGetValues(titleWidget, args, i);
1738 w = boardWidth - 2*bor;
1740 XtSetArg(args[0], XtNwidth, &w);
1741 XtGetValues(menuBarWidget, args, 1);
1742 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1745 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1746 if (gres != XtGeometryYes && appData.debugMode) {
1748 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1749 programName, gres, w, h, wr, hr);
1753 XawFormDoLayout(formWidget, True);
1756 * Inhibit shell resizing.
1758 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1759 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1760 shellArgs[4].value = shellArgs[2].value = w;
1761 shellArgs[5].value = shellArgs[3].value = h;
1762 XtSetValues(shellWidget, &shellArgs[0], 6);
1765 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1768 if(gameInfo.variant == oldVariant) return; // and only if variant changed
1771 for(i=0; i<4; i++) {
1773 for(p=0; p<=(int)WhiteKing; p++)
1774 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1775 if(gameInfo.variant == VariantShogi) {
1776 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1777 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1778 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1779 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1780 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1783 if(gameInfo.variant == VariantGothic) {
1784 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1787 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1788 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1789 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1792 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1793 for(p=0; p<=(int)WhiteKing; p++)
1794 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1795 if(gameInfo.variant == VariantShogi) {
1796 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1797 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1798 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1799 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1800 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1803 if(gameInfo.variant == VariantGothic) {
1804 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1807 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1808 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1809 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1814 for(i=0; i<2; i++) {
1816 for(p=0; p<=(int)WhiteKing; p++)
1817 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1818 if(gameInfo.variant == VariantShogi) {
1819 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1820 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1821 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1822 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1823 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1826 if(gameInfo.variant == VariantGothic) {
1827 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1830 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1831 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1832 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1842 void ParseIcsTextColors()
1843 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1844 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1845 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1846 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1847 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1848 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1849 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1850 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1851 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1852 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1853 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1855 if (appData.colorize) {
1857 _("%s: can't parse color names; disabling colorization\n"),
1860 appData.colorize = FALSE;
1865 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1866 XrmValue vFrom, vTo;
1867 int forceMono = False;
1869 if (!appData.monoMode) {
1870 vFrom.addr = (caddr_t) appData.lightSquareColor;
1871 vFrom.size = strlen(appData.lightSquareColor);
1872 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1873 if (vTo.addr == NULL) {
1874 appData.monoMode = True;
1877 lightSquareColor = *(Pixel *) vTo.addr;
1880 if (!appData.monoMode) {
1881 vFrom.addr = (caddr_t) appData.darkSquareColor;
1882 vFrom.size = strlen(appData.darkSquareColor);
1883 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1884 if (vTo.addr == NULL) {
1885 appData.monoMode = True;
1888 darkSquareColor = *(Pixel *) vTo.addr;
1891 if (!appData.monoMode) {
1892 vFrom.addr = (caddr_t) appData.whitePieceColor;
1893 vFrom.size = strlen(appData.whitePieceColor);
1894 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1895 if (vTo.addr == NULL) {
1896 appData.monoMode = True;
1899 whitePieceColor = *(Pixel *) vTo.addr;
1902 if (!appData.monoMode) {
1903 vFrom.addr = (caddr_t) appData.blackPieceColor;
1904 vFrom.size = strlen(appData.blackPieceColor);
1905 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1906 if (vTo.addr == NULL) {
1907 appData.monoMode = True;
1910 blackPieceColor = *(Pixel *) vTo.addr;
1914 if (!appData.monoMode) {
1915 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1916 vFrom.size = strlen(appData.highlightSquareColor);
1917 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1918 if (vTo.addr == NULL) {
1919 appData.monoMode = True;
1922 highlightSquareColor = *(Pixel *) vTo.addr;
1926 if (!appData.monoMode) {
1927 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1928 vFrom.size = strlen(appData.premoveHighlightColor);
1929 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1930 if (vTo.addr == NULL) {
1931 appData.monoMode = True;
1934 premoveHighlightColor = *(Pixel *) vTo.addr;
1942 { // [HGM] taken out of main
1944 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1945 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1946 appData.bitmapDirectory = DEF_BITMAP_DIR;
1948 if (appData.bitmapDirectory[0] != NULLCHAR) {
1952 CreateXPMBoard(appData.liteBackTextureFile, 1);
1953 CreateXPMBoard(appData.darkBackTextureFile, 0);
1957 /* Create regular pieces */
1958 if (!useImages) CreatePieces();
1967 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1968 XSetWindowAttributes window_attributes;
1970 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1971 XrmValue vFrom, vTo;
1972 XtGeometryResult gres;
1975 int forceMono = False;
1977 srandom(time(0)); // [HGM] book: make random truly random
1979 setbuf(stdout, NULL);
1980 setbuf(stderr, NULL);
1983 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1984 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1988 programName = strrchr(argv[0], '/');
1989 if (programName == NULL)
1990 programName = argv[0];
1995 XtSetLanguageProc(NULL, NULL, NULL);
1996 bindtextdomain(PACKAGE, LOCALEDIR);
1997 textdomain(PACKAGE);
2001 XtAppInitialize(&appContext, "XBoard", shellOptions,
2002 XtNumber(shellOptions),
2003 &argc, argv, xboardResources, NULL, 0);
2004 appData.boardSize = "";
2005 InitAppData(ConvertToLine(argc, argv));
2007 if (p == NULL) p = "/tmp";
2008 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2009 gameCopyFilename = (char*) malloc(i);
2010 gamePasteFilename = (char*) malloc(i);
2011 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2012 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2014 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2015 clientResources, XtNumber(clientResources),
2018 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2019 static char buf[MSG_SIZ];
2020 EscapeExpand(buf, appData.firstInitString);
2021 appData.firstInitString = strdup(buf);
2022 EscapeExpand(buf, appData.secondInitString);
2023 appData.secondInitString = strdup(buf);
2024 EscapeExpand(buf, appData.firstComputerString);
2025 appData.firstComputerString = strdup(buf);
2026 EscapeExpand(buf, appData.secondComputerString);
2027 appData.secondComputerString = strdup(buf);
2030 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2033 if (chdir(chessDir) != 0) {
2034 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2040 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2041 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2042 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2043 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2046 setbuf(debugFP, NULL);
2050 if (appData.debugMode) {
2051 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2055 /* [HGM,HR] make sure board size is acceptable */
2056 if(appData.NrFiles > BOARD_FILES ||
2057 appData.NrRanks > BOARD_RANKS )
2058 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2061 /* This feature does not work; animation needs a rewrite */
2062 appData.highlightDragging = FALSE;
2066 xDisplay = XtDisplay(shellWidget);
2067 xScreen = DefaultScreen(xDisplay);
2068 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2070 gameInfo.variant = StringToVariant(appData.variant);
2071 InitPosition(FALSE);
2074 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2076 if (isdigit(appData.boardSize[0])) {
2077 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2078 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2079 &fontPxlSize, &smallLayout, &tinyLayout);
2081 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2082 programName, appData.boardSize);
2086 /* Find some defaults; use the nearest known size */
2087 SizeDefaults *szd, *nearest;
2088 int distance = 99999;
2089 nearest = szd = sizeDefaults;
2090 while (szd->name != NULL) {
2091 if (abs(szd->squareSize - squareSize) < distance) {
2093 distance = abs(szd->squareSize - squareSize);
2094 if (distance == 0) break;
2098 if (i < 2) lineGap = nearest->lineGap;
2099 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2100 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2101 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2102 if (i < 6) smallLayout = nearest->smallLayout;
2103 if (i < 7) tinyLayout = nearest->tinyLayout;
2106 SizeDefaults *szd = sizeDefaults;
2107 if (*appData.boardSize == NULLCHAR) {
2108 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2109 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2112 if (szd->name == NULL) szd--;
2113 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2115 while (szd->name != NULL &&
2116 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2117 if (szd->name == NULL) {
2118 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2119 programName, appData.boardSize);
2123 squareSize = szd->squareSize;
2124 lineGap = szd->lineGap;
2125 clockFontPxlSize = szd->clockFontPxlSize;
2126 coordFontPxlSize = szd->coordFontPxlSize;
2127 fontPxlSize = szd->fontPxlSize;
2128 smallLayout = szd->smallLayout;
2129 tinyLayout = szd->tinyLayout;
2130 // [HGM] font: use defaults from settings file if available and not overruled
2132 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2133 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2134 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2135 appData.font = fontTable[MESSAGE_FONT][squareSize];
2136 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2137 appData.coordFont = fontTable[COORD_FONT][squareSize];
2139 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2140 if (strlen(appData.pixmapDirectory) > 0) {
2141 p = ExpandPathName(appData.pixmapDirectory);
2143 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2144 appData.pixmapDirectory);
2147 if (appData.debugMode) {
2148 fprintf(stderr, _("\
2149 XBoard square size (hint): %d\n\
2150 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2152 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2153 if (appData.debugMode) {
2154 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2157 defaultLineGap = lineGap;
2158 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2160 /* [HR] height treated separately (hacked) */
2161 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2162 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2163 if (appData.showJail == 1) {
2164 /* Jail on top and bottom */
2165 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2166 XtSetArg(boardArgs[2], XtNheight,
2167 boardHeight + 2*(lineGap + squareSize));
2168 } else if (appData.showJail == 2) {
2170 XtSetArg(boardArgs[1], XtNwidth,
2171 boardWidth + 2*(lineGap + squareSize));
2172 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2175 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2176 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2180 * Determine what fonts to use.
2183 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2184 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2185 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2186 fontSet = CreateFontSet(appData.font);
2187 clockFontSet = CreateFontSet(appData.clockFont);
2189 /* For the coordFont, use the 0th font of the fontset. */
2190 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2191 XFontStruct **font_struct_list;
2192 char **font_name_list;
2193 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2194 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2195 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2198 appData.font = FindFont(appData.font, fontPxlSize);
2199 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2200 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2201 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2202 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2203 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2204 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2206 countFontID = coordFontID; // [HGM] holdings
2207 countFontStruct = coordFontStruct;
2209 xdb = XtDatabase(xDisplay);
2211 XrmPutLineResource(&xdb, "*international: True");
2212 vTo.size = sizeof(XFontSet);
2213 vTo.addr = (XtPointer) &fontSet;
2214 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2216 XrmPutStringResource(&xdb, "*font", appData.font);
2220 * Detect if there are not enough colors available and adapt.
2222 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2223 appData.monoMode = True;
2226 forceMono = MakeColors();
2229 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2231 appData.monoMode = True;
2234 if (appData.lowTimeWarning && !appData.monoMode) {
2235 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2236 vFrom.size = strlen(appData.lowTimeWarningColor);
2237 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2238 if (vTo.addr == NULL)
2239 appData.monoMode = True;
2241 lowTimeWarningColor = *(Pixel *) vTo.addr;
2244 if (appData.monoMode && appData.debugMode) {
2245 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2246 (unsigned long) XWhitePixel(xDisplay, xScreen),
2247 (unsigned long) XBlackPixel(xDisplay, xScreen));
2250 ParseIcsTextColors();
2251 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2252 textColors[ColorNone].attr = 0;
2254 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2260 layoutName = "tinyLayout";
2261 } else if (smallLayout) {
2262 layoutName = "smallLayout";
2264 layoutName = "normalLayout";
2266 /* Outer layoutWidget is there only to provide a name for use in
2267 resources that depend on the layout style */
2269 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2270 layoutArgs, XtNumber(layoutArgs));
2272 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2273 formArgs, XtNumber(formArgs));
2274 XtSetArg(args[0], XtNdefaultDistance, &sep);
2275 XtGetValues(formWidget, args, 1);
2278 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2279 XtSetArg(args[0], XtNtop, XtChainTop);
2280 XtSetArg(args[1], XtNbottom, XtChainTop);
2281 XtSetArg(args[2], XtNright, XtChainLeft);
2282 XtSetValues(menuBarWidget, args, 3);
2284 widgetList[j++] = whiteTimerWidget =
2285 XtCreateWidget("whiteTime", labelWidgetClass,
2286 formWidget, timerArgs, XtNumber(timerArgs));
2288 XtSetArg(args[0], XtNfontSet, clockFontSet);
2290 XtSetArg(args[0], XtNfont, clockFontStruct);
2292 XtSetArg(args[1], XtNtop, XtChainTop);
2293 XtSetArg(args[2], XtNbottom, XtChainTop);
2294 XtSetValues(whiteTimerWidget, args, 3);
2296 widgetList[j++] = blackTimerWidget =
2297 XtCreateWidget("blackTime", labelWidgetClass,
2298 formWidget, timerArgs, XtNumber(timerArgs));
2300 XtSetArg(args[0], XtNfontSet, clockFontSet);
2302 XtSetArg(args[0], XtNfont, clockFontStruct);
2304 XtSetArg(args[1], XtNtop, XtChainTop);
2305 XtSetArg(args[2], XtNbottom, XtChainTop);
2306 XtSetValues(blackTimerWidget, args, 3);
2308 if (appData.titleInWindow) {
2309 widgetList[j++] = titleWidget =
2310 XtCreateWidget("title", labelWidgetClass, formWidget,
2311 titleArgs, XtNumber(titleArgs));
2312 XtSetArg(args[0], XtNtop, XtChainTop);
2313 XtSetArg(args[1], XtNbottom, XtChainTop);
2314 XtSetValues(titleWidget, args, 2);
2317 if (appData.showButtonBar) {
2318 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2319 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2320 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2321 XtSetArg(args[2], XtNtop, XtChainTop);
2322 XtSetArg(args[3], XtNbottom, XtChainTop);
2323 XtSetValues(buttonBarWidget, args, 4);
2326 widgetList[j++] = messageWidget =
2327 XtCreateWidget("message", labelWidgetClass, formWidget,
2328 messageArgs, XtNumber(messageArgs));
2329 XtSetArg(args[0], XtNtop, XtChainTop);
2330 XtSetArg(args[1], XtNbottom, XtChainTop);
2331 XtSetValues(messageWidget, args, 2);
2333 widgetList[j++] = boardWidget =
2334 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2335 XtNumber(boardArgs));
2337 XtManageChildren(widgetList, j);
2339 timerWidth = (boardWidth - sep) / 2;
2340 XtSetArg(args[0], XtNwidth, timerWidth);
2341 XtSetValues(whiteTimerWidget, args, 1);
2342 XtSetValues(blackTimerWidget, args, 1);
2344 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2345 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2346 XtGetValues(whiteTimerWidget, args, 2);
2348 if (appData.showButtonBar) {
2349 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2350 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2351 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2355 * formWidget uses these constraints but they are stored
2359 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2360 XtSetValues(menuBarWidget, args, i);
2361 if (appData.titleInWindow) {
2364 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2365 XtSetValues(whiteTimerWidget, args, i);
2367 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2368 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2369 XtSetValues(blackTimerWidget, args, i);
2371 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2372 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2373 XtSetValues(titleWidget, args, i);
2375 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2376 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2377 XtSetValues(messageWidget, args, i);
2378 if (appData.showButtonBar) {
2380 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2381 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2382 XtSetValues(buttonBarWidget, args, i);
2386 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2387 XtSetValues(whiteTimerWidget, args, i);
2389 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2390 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2391 XtSetValues(blackTimerWidget, args, i);
2393 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2394 XtSetValues(titleWidget, args, i);
2396 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2397 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2398 XtSetValues(messageWidget, args, i);
2399 if (appData.showButtonBar) {
2401 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2402 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2403 XtSetValues(buttonBarWidget, args, i);
2408 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2409 XtSetValues(whiteTimerWidget, args, i);
2411 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2412 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2413 XtSetValues(blackTimerWidget, args, i);
2415 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2416 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2417 XtSetValues(messageWidget, args, i);
2418 if (appData.showButtonBar) {
2420 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2421 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2422 XtSetValues(buttonBarWidget, args, i);
2426 XtSetArg(args[0], XtNfromVert, messageWidget);
2427 XtSetArg(args[1], XtNtop, XtChainTop);
2428 XtSetArg(args[2], XtNbottom, XtChainBottom);
2429 XtSetArg(args[3], XtNleft, XtChainLeft);
2430 XtSetArg(args[4], XtNright, XtChainRight);
2431 XtSetValues(boardWidget, args, 5);
2433 XtRealizeWidget(shellWidget);
2436 XtSetArg(args[0], XtNx, wpMain.x);
2437 XtSetArg(args[1], XtNy, wpMain.y);
2438 XtSetValues(shellWidget, args, 2);
2442 * Correct the width of the message and title widgets.
2443 * It is not known why some systems need the extra fudge term.
2444 * The value "2" is probably larger than needed.
2446 XawFormDoLayout(formWidget, False);
2448 #define WIDTH_FUDGE 2
2450 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2451 XtSetArg(args[i], XtNheight, &h); i++;
2452 XtGetValues(messageWidget, args, i);
2453 if (appData.showButtonBar) {
2455 XtSetArg(args[i], XtNwidth, &w); i++;
2456 XtGetValues(buttonBarWidget, args, i);
2457 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2459 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2462 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2463 if (gres != XtGeometryYes && appData.debugMode) {
2464 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2465 programName, gres, w, h, wr, hr);
2468 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2469 /* The size used for the child widget in layout lags one resize behind
2470 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2472 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2473 if (gres != XtGeometryYes && appData.debugMode) {
2474 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2475 programName, gres, w, h, wr, hr);
2478 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2479 XtSetArg(args[1], XtNright, XtChainRight);
2480 XtSetValues(messageWidget, args, 2);
2482 if (appData.titleInWindow) {
2484 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2485 XtSetArg(args[i], XtNheight, &h); i++;
2486 XtGetValues(titleWidget, args, i);
2488 w = boardWidth - 2*bor;
2490 XtSetArg(args[0], XtNwidth, &w);
2491 XtGetValues(menuBarWidget, args, 1);
2492 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2495 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2496 if (gres != XtGeometryYes && appData.debugMode) {
2498 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2499 programName, gres, w, h, wr, hr);
2502 XawFormDoLayout(formWidget, True);
2504 xBoardWindow = XtWindow(boardWidget);
2506 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2507 // not need to go into InitDrawingSizes().
2511 * Create X checkmark bitmap and initialize option menu checks.
2513 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2514 checkmark_bits, checkmark_width, checkmark_height);
2515 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2516 #ifndef OPTIONSDIALOG
2517 if (appData.alwaysPromoteToQueen) {
2518 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2521 if (appData.animateDragging) {
2522 XtSetValues(XtNameToWidget(menuBarWidget,
2523 "menuOptions.Animate Dragging"),
2526 if (appData.animate) {
2527 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2530 if (appData.autoCallFlag) {
2531 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2534 if (appData.autoFlipView) {
2535 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2538 if (appData.blindfold) {
2539 XtSetValues(XtNameToWidget(menuBarWidget,
2540 "menuOptions.Blindfold"), args, 1);
2542 if (appData.flashCount > 0) {
2543 XtSetValues(XtNameToWidget(menuBarWidget,
2544 "menuOptions.Flash Moves"),
2548 if (appData.highlightDragging) {
2549 XtSetValues(XtNameToWidget(menuBarWidget,
2550 "menuOptions.Highlight Dragging"),
2554 if (appData.highlightLastMove) {
2555 XtSetValues(XtNameToWidget(menuBarWidget,
2556 "menuOptions.Highlight Last Move"),
2559 if (appData.highlightMoveWithArrow) {
2560 XtSetValues(XtNameToWidget(menuBarWidget,
2561 "menuOptions.Arrow"),
2564 // if (appData.icsAlarm) {
2565 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2568 if (appData.ringBellAfterMoves) {
2569 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2572 if (appData.oneClick) {
2573 XtSetValues(XtNameToWidget(menuBarWidget,
2574 "menuOptions.OneClick"), args, 1);
2576 if (appData.periodicUpdates) {
2577 XtSetValues(XtNameToWidget(menuBarWidget,
2578 "menuOptions.Periodic Updates"), args, 1);
2580 if (appData.ponderNextMove) {
2581 XtSetValues(XtNameToWidget(menuBarWidget,
2582 "menuOptions.Ponder Next Move"), args, 1);
2584 if (appData.popupExitMessage) {
2585 XtSetValues(XtNameToWidget(menuBarWidget,
2586 "menuOptions.Popup Exit Message"), args, 1);
2588 if (appData.popupMoveErrors) {
2589 XtSetValues(XtNameToWidget(menuBarWidget,
2590 "menuOptions.Popup Move Errors"), args, 1);
2592 // if (appData.premove) {
2593 // XtSetValues(XtNameToWidget(menuBarWidget,
2594 // "menuOptions.Premove"), args, 1);
2596 if (appData.showCoords) {
2597 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2600 if (appData.hideThinkingFromHuman) {
2601 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2604 if (appData.testLegality) {
2605 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2609 if (saveSettingsOnExit) {
2610 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2617 ReadBitmap(&wIconPixmap, "icon_white.bm",
2618 icon_white_bits, icon_white_width, icon_white_height);
2619 ReadBitmap(&bIconPixmap, "icon_black.bm",
2620 icon_black_bits, icon_black_width, icon_black_height);
2621 iconPixmap = wIconPixmap;
2623 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2624 XtSetValues(shellWidget, args, i);
2627 * Create a cursor for the board widget.
2629 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2630 XChangeWindowAttributes(xDisplay, xBoardWindow,
2631 CWCursor, &window_attributes);
2634 * Inhibit shell resizing.
2636 shellArgs[0].value = (XtArgVal) &w;
2637 shellArgs[1].value = (XtArgVal) &h;
2638 XtGetValues(shellWidget, shellArgs, 2);
2639 shellArgs[4].value = shellArgs[2].value = w;
2640 shellArgs[5].value = shellArgs[3].value = h;
2641 XtSetValues(shellWidget, &shellArgs[2], 4);
2642 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2643 marginH = h - boardHeight;
2645 CatchDeleteWindow(shellWidget, "QuitProc");
2653 if (appData.animate || appData.animateDragging)
2656 XtAugmentTranslations(formWidget,
2657 XtParseTranslationTable(globalTranslations));
2658 XtAugmentTranslations(boardWidget,
2659 XtParseTranslationTable(boardTranslations));
2660 XtAugmentTranslations(whiteTimerWidget,
2661 XtParseTranslationTable(whiteTranslations));
2662 XtAugmentTranslations(blackTimerWidget,
2663 XtParseTranslationTable(blackTranslations));
2665 /* Why is the following needed on some versions of X instead
2666 * of a translation? */
2667 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2668 (XtEventHandler) EventProc, NULL);
2670 XtAddEventHandler(formWidget, KeyPressMask, False,
2671 (XtEventHandler) MoveTypeInProc, NULL);
2673 /* [AS] Restore layout */
2674 if( wpMoveHistory.visible ) {
2678 if( wpEvalGraph.visible )
2683 if( wpEngineOutput.visible ) {
2684 EngineOutputPopUp();
2689 if (errorExitStatus == -1) {
2690 if (appData.icsActive) {
2691 /* We now wait until we see "login:" from the ICS before
2692 sending the logon script (problems with timestamp otherwise) */
2693 /*ICSInitScript();*/
2694 if (appData.icsInputBox) ICSInputBoxPopUp();
2698 signal(SIGWINCH, TermSizeSigHandler);
2700 signal(SIGINT, IntSigHandler);
2701 signal(SIGTERM, IntSigHandler);
2702 if (*appData.cmailGameName != NULLCHAR) {
2703 signal(SIGUSR1, CmailSigHandler);
2706 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2708 // XtSetKeyboardFocus(shellWidget, formWidget);
2709 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2711 XtAppMainLoop(appContext);
2712 if (appData.debugMode) fclose(debugFP); // [DM] debug
2716 static Boolean noEcho;
2721 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2722 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2724 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2725 unlink(gameCopyFilename);
2726 unlink(gamePasteFilename);
2727 if(noEcho) EchoOn();
2730 RETSIGTYPE TermSizeSigHandler(int sig)
2743 CmailSigHandler(sig)
2749 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2751 /* Activate call-back function CmailSigHandlerCallBack() */
2752 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2754 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2758 CmailSigHandlerCallBack(isr, closure, message, count, error)
2766 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2768 /**** end signal code ****/
2774 /* try to open the icsLogon script, either in the location given
2775 * or in the users HOME directory
2782 f = fopen(appData.icsLogon, "r");
2785 homedir = getenv("HOME");
2786 if (homedir != NULL)
2788 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2789 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2790 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2791 f = fopen(buf, "r");
2796 ProcessICSInitScript(f);
2798 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2821 if (!menuBarWidget) return;
2822 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2824 DisplayError("menuEdit.Revert", 0);
2826 XtSetSensitive(w, !grey);
2828 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2830 DisplayError("menuEdit.Annotate", 0);
2832 XtSetSensitive(w, !grey);
2837 SetMenuEnables(enab)
2841 if (!menuBarWidget) return;
2842 while (enab->name != NULL) {
2843 w = XtNameToWidget(menuBarWidget, enab->name);
2845 DisplayError(enab->name, 0);
2847 XtSetSensitive(w, enab->value);
2853 Enables icsEnables[] = {
2854 { "menuFile.Mail Move", False },
2855 { "menuFile.Reload CMail Message", False },
2856 { "menuMode.Machine Black", False },
2857 { "menuMode.Machine White", False },
2858 { "menuMode.Analysis Mode", False },
2859 { "menuMode.Analyze File", False },
2860 { "menuMode.Two Machines", False },
2861 { "menuMode.Machine Match", False },
2863 { "menuEngine.Hint", False },
2864 { "menuEngine.Book", False },
2865 { "menuEngine.Move Now", False },
2866 #ifndef OPTIONSDIALOG
2867 { "menuOptions.Periodic Updates", False },
2868 { "menuOptions.Hide Thinking", False },
2869 { "menuOptions.Ponder Next Move", False },
2872 { "menuEngine.Engine #1 Settings", False },
2873 { "menuEngine.Engine #2 Settings", False },
2874 { "menuEngine.Load Engine", False },
2875 { "menuEdit.Annotate", False },
2876 { "menuOptions.Match", False },
2880 Enables ncpEnables[] = {
2881 { "menuFile.Mail Move", False },
2882 { "menuFile.Reload CMail Message", False },
2883 { "menuMode.Machine White", False },
2884 { "menuMode.Machine Black", False },
2885 { "menuMode.Analysis Mode", False },
2886 { "menuMode.Analyze File", False },
2887 { "menuMode.Two Machines", False },
2888 { "menuMode.Machine Match", False },
2889 { "menuMode.ICS Client", False },
2890 { "menuView.ICStex", False },
2891 { "menuView.ICS Input Box", False },
2892 { "Action", False },
2893 { "menuEdit.Revert", False },
2894 { "menuEdit.Annotate", False },
2895 { "menuEngine.Engine #1 Settings", False },
2896 { "menuEngine.Engine #2 Settings", False },
2897 { "menuEngine.Move Now", False },
2898 { "menuEngine.Retract Move", False },
2899 { "menuOptions.ICS", False },
2900 #ifndef OPTIONSDIALOG
2901 { "menuOptions.Auto Flag", False },
2902 { "menuOptions.Auto Flip View", False },
2903 // { "menuOptions.ICS Alarm", False },
2904 { "menuOptions.Move Sound", False },
2905 { "menuOptions.Hide Thinking", False },
2906 { "menuOptions.Periodic Updates", False },
2907 { "menuOptions.Ponder Next Move", False },
2909 { "menuEngine.Hint", False },
2910 { "menuEngine.Book", False },
2914 Enables gnuEnables[] = {
2915 { "menuMode.ICS Client", False },
2916 { "menuView.ICStex", False },
2917 { "menuView.ICS Input Box", False },
2918 { "menuAction.Accept", False },
2919 { "menuAction.Decline", False },
2920 { "menuAction.Rematch", False },
2921 { "menuAction.Adjourn", False },
2922 { "menuAction.Stop Examining", False },
2923 { "menuAction.Stop Observing", False },
2924 { "menuAction.Upload to Examine", False },
2925 { "menuEdit.Revert", False },
2926 { "menuEdit.Annotate", False },
2927 { "menuOptions.ICS", False },
2929 /* The next two options rely on SetCmailMode being called *after* */
2930 /* SetGNUMode so that when GNU is being used to give hints these */
2931 /* menu options are still available */
2933 { "menuFile.Mail Move", False },
2934 { "menuFile.Reload CMail Message", False },
2935 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2936 { "menuMode.Machine White", True },
2937 { "menuMode.Machine Black", True },
2938 { "menuMode.Analysis Mode", True },
2939 { "menuMode.Analyze File", True },
2940 { "menuMode.Two Machines", True },
2941 { "menuMode.Machine Match", True },
2942 { "menuEngine.Engine #1 Settings", True },
2943 { "menuEngine.Engine #2 Settings", True },
2944 { "menuEngine.Hint", True },
2945 { "menuEngine.Book", True },
2946 { "menuEngine.Move Now", True },
2947 { "menuEngine.Retract Move", True },
2952 Enables cmailEnables[] = {
2954 { "menuAction.Call Flag", False },
2955 { "menuAction.Draw", True },
2956 { "menuAction.Adjourn", False },
2957 { "menuAction.Abort", False },
2958 { "menuAction.Stop Observing", False },
2959 { "menuAction.Stop Examining", False },
2960 { "menuFile.Mail Move", True },
2961 { "menuFile.Reload CMail Message", True },
2965 Enables trainingOnEnables[] = {
2966 { "menuMode.Edit Comment", False },
2967 { "menuMode.Pause", False },
2968 { "menuEdit.Forward", False },
2969 { "menuEdit.Backward", False },
2970 { "menuEdit.Forward to End", False },
2971 { "menuEdit.Back to Start", False },
2972 { "menuEngine.Move Now", False },
2973 { "menuEdit.Truncate Game", False },
2977 Enables trainingOffEnables[] = {
2978 { "menuMode.Edit Comment", True },
2979 { "menuMode.Pause", True },
2980 { "menuEdit.Forward", True },
2981 { "menuEdit.Backward", True },
2982 { "menuEdit.Forward to End", True },
2983 { "menuEdit.Back to Start", True },
2984 { "menuEngine.Move Now", True },
2985 { "menuEdit.Truncate Game", True },
2989 Enables machineThinkingEnables[] = {
2990 { "menuFile.Load Game", False },
2991 // { "menuFile.Load Next Game", False },
2992 // { "menuFile.Load Previous Game", False },
2993 // { "menuFile.Reload Same Game", False },
2994 { "menuEdit.Paste Game", False },
2995 { "menuFile.Load Position", False },
2996 // { "menuFile.Load Next Position", False },
2997 // { "menuFile.Load Previous Position", False },
2998 // { "menuFile.Reload Same Position", False },
2999 { "menuEdit.Paste Position", False },
3000 { "menuMode.Machine White", False },
3001 { "menuMode.Machine Black", False },
3002 { "menuMode.Two Machines", False },
3003 // { "menuMode.Machine Match", False },
3004 { "menuEngine.Retract Move", False },
3008 Enables userThinkingEnables[] = {
3009 { "menuFile.Load Game", True },
3010 // { "menuFile.Load Next Game", True },
3011 // { "menuFile.Load Previous Game", True },
3012 // { "menuFile.Reload Same Game", True },
3013 { "menuEdit.Paste Game", True },
3014 { "menuFile.Load Position", True },
3015 // { "menuFile.Load Next Position", True },
3016 // { "menuFile.Load Previous Position", True },
3017 // { "menuFile.Reload Same Position", True },
3018 { "menuEdit.Paste Position", True },
3019 { "menuMode.Machine White", True },
3020 { "menuMode.Machine Black", True },
3021 { "menuMode.Two Machines", True },
3022 // { "menuMode.Machine Match", True },
3023 { "menuEngine.Retract Move", True },
3029 SetMenuEnables(icsEnables);
3032 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3033 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3034 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3042 SetMenuEnables(ncpEnables);
3048 SetMenuEnables(gnuEnables);
3054 SetMenuEnables(cmailEnables);
3060 SetMenuEnables(trainingOnEnables);
3061 if (appData.showButtonBar) {
3062 XtSetSensitive(buttonBarWidget, False);
3068 SetTrainingModeOff()
3070 SetMenuEnables(trainingOffEnables);
3071 if (appData.showButtonBar) {
3072 XtSetSensitive(buttonBarWidget, True);
3077 SetUserThinkingEnables()
3079 if (appData.noChessProgram) return;
3080 SetMenuEnables(userThinkingEnables);
3084 SetMachineThinkingEnables()
3086 if (appData.noChessProgram) return;
3087 SetMenuEnables(machineThinkingEnables);
3089 case MachinePlaysBlack:
3090 case MachinePlaysWhite:
3091 case TwoMachinesPlay:
3092 XtSetSensitive(XtNameToWidget(menuBarWidget,
3093 ModeToWidgetName(gameMode)), True);
3100 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3101 #define HISTORY_SIZE 64
3102 static char *history[HISTORY_SIZE];
3103 int histIn = 0, histP = 0;
3106 SaveInHistory(char *cmd)
3108 if (history[histIn] != NULL) {
3109 free(history[histIn]);
3110 history[histIn] = NULL;
3112 if (*cmd == NULLCHAR) return;
3113 history[histIn] = StrSave(cmd);
3114 histIn = (histIn + 1) % HISTORY_SIZE;
3115 if (history[histIn] != NULL) {
3116 free(history[histIn]);
3117 history[histIn] = NULL;
3123 PrevInHistory(char *cmd)
3126 if (histP == histIn) {
3127 if (history[histIn] != NULL) free(history[histIn]);
3128 history[histIn] = StrSave(cmd);
3130 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3131 if (newhp == histIn || history[newhp] == NULL) return NULL;
3133 return history[histP];
3139 if (histP == histIn) return NULL;
3140 histP = (histP + 1) % HISTORY_SIZE;
3141 return history[histP];
3143 // end of borrowed code
3145 #define Abs(n) ((n)<0 ? -(n) : (n))
3149 InsertPxlSize(pattern, targetPxlSize)
3153 char *base_fnt_lst, strInt[12], *p, *q;
3154 int alternatives, i, len, strIntLen;
3157 * Replace the "*" (if present) in the pixel-size slot of each
3158 * alternative with the targetPxlSize.
3162 while ((p = strchr(p, ',')) != NULL) {
3166 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3167 strIntLen = strlen(strInt);
3168 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3172 while (alternatives--) {
3173 char *comma = strchr(p, ',');
3174 for (i=0; i<14; i++) {
3175 char *hyphen = strchr(p, '-');
3177 if (comma && hyphen > comma) break;
3178 len = hyphen + 1 - p;
3179 if (i == 7 && *p == '*' && len == 2) {
3181 memcpy(q, strInt, strIntLen);
3191 len = comma + 1 - p;
3198 return base_fnt_lst;
3202 CreateFontSet(base_fnt_lst)
3206 char **missing_list;
3210 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3211 &missing_list, &missing_count, &def_string);
3212 if (appData.debugMode) {
3214 XFontStruct **font_struct_list;
3215 char **font_name_list;
3216 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3218 fprintf(debugFP, " got list %s, locale %s\n",
3219 XBaseFontNameListOfFontSet(fntSet),
3220 XLocaleOfFontSet(fntSet));
3221 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3222 for (i = 0; i < count; i++) {
3223 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3226 for (i = 0; i < missing_count; i++) {
3227 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3230 if (fntSet == NULL) {
3231 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3236 #else // not ENABLE_NLS
3238 * Find a font that matches "pattern" that is as close as
3239 * possible to the targetPxlSize. Prefer fonts that are k
3240 * pixels smaller to fonts that are k pixels larger. The
3241 * pattern must be in the X Consortium standard format,
3242 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3243 * The return value should be freed with XtFree when no
3247 FindFont(pattern, targetPxlSize)
3251 char **fonts, *p, *best, *scalable, *scalableTail;
3252 int i, j, nfonts, minerr, err, pxlSize;
3254 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3256 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3257 programName, pattern);
3264 for (i=0; i<nfonts; i++) {
3267 if (*p != '-') continue;
3269 if (*p == NULLCHAR) break;
3270 if (*p++ == '-') j++;
3272 if (j < 7) continue;
3275 scalable = fonts[i];
3278 err = pxlSize - targetPxlSize;
3279 if (Abs(err) < Abs(minerr) ||
3280 (minerr > 0 && err < 0 && -err == minerr)) {
3286 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3287 /* If the error is too big and there is a scalable font,
3288 use the scalable font. */
3289 int headlen = scalableTail - scalable;
3290 p = (char *) XtMalloc(strlen(scalable) + 10);
3291 while (isdigit(*scalableTail)) scalableTail++;
3292 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3294 p = (char *) XtMalloc(strlen(best) + 2);
3295 safeStrCpy(p, best, strlen(best)+1 );
3297 if (appData.debugMode) {
3298 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3299 pattern, targetPxlSize, p);
3301 XFreeFontNames(fonts);
3307 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3308 // must be called before all non-first callse to CreateGCs()
3309 XtReleaseGC(shellWidget, highlineGC);
3310 XtReleaseGC(shellWidget, lightSquareGC);
3311 XtReleaseGC(shellWidget, darkSquareGC);
3312 XtReleaseGC(shellWidget, lineGC);
3313 if (appData.monoMode) {
3314 if (DefaultDepth(xDisplay, xScreen) == 1) {
3315 XtReleaseGC(shellWidget, wbPieceGC);
3317 XtReleaseGC(shellWidget, bwPieceGC);
3320 XtReleaseGC(shellWidget, prelineGC);
3321 XtReleaseGC(shellWidget, jailSquareGC);
3322 XtReleaseGC(shellWidget, wdPieceGC);
3323 XtReleaseGC(shellWidget, wlPieceGC);
3324 XtReleaseGC(shellWidget, wjPieceGC);
3325 XtReleaseGC(shellWidget, bdPieceGC);
3326 XtReleaseGC(shellWidget, blPieceGC);
3327 XtReleaseGC(shellWidget, bjPieceGC);
3331 void CreateGCs(int redo)
3333 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3334 | GCBackground | GCFunction | GCPlaneMask;
3335 XGCValues gc_values;
3338 gc_values.plane_mask = AllPlanes;
3339 gc_values.line_width = lineGap;
3340 gc_values.line_style = LineSolid;
3341 gc_values.function = GXcopy;
3344 DeleteGCs(); // called a second time; clean up old GCs first
3345 } else { // [HGM] grid and font GCs created on first call only
3346 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3347 gc_values.background = XWhitePixel(xDisplay, xScreen);
3348 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3349 XSetFont(xDisplay, coordGC, coordFontID);
3351 // [HGM] make font for holdings counts (white on black)
3352 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3353 gc_values.background = XBlackPixel(xDisplay, xScreen);
3354 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3355 XSetFont(xDisplay, countGC, countFontID);
3357 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3358 gc_values.background = XBlackPixel(xDisplay, xScreen);
3359 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3361 if (appData.monoMode) {
3362 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3363 gc_values.background = XWhitePixel(xDisplay, xScreen);
3364 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3366 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3367 gc_values.background = XBlackPixel(xDisplay, xScreen);
3368 lightSquareGC = wbPieceGC
3369 = XtGetGC(shellWidget, value_mask, &gc_values);
3371 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3372 gc_values.background = XWhitePixel(xDisplay, xScreen);
3373 darkSquareGC = bwPieceGC
3374 = XtGetGC(shellWidget, value_mask, &gc_values);
3376 if (DefaultDepth(xDisplay, xScreen) == 1) {
3377 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3378 gc_values.function = GXcopyInverted;
3379 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3380 gc_values.function = GXcopy;
3381 if (XBlackPixel(xDisplay, xScreen) == 1) {
3382 bwPieceGC = darkSquareGC;
3383 wbPieceGC = copyInvertedGC;
3385 bwPieceGC = copyInvertedGC;
3386 wbPieceGC = lightSquareGC;
3390 gc_values.foreground = highlightSquareColor;
3391 gc_values.background = highlightSquareColor;
3392 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3394 gc_values.foreground = premoveHighlightColor;
3395 gc_values.background = premoveHighlightColor;
3396 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3398 gc_values.foreground = lightSquareColor;
3399 gc_values.background = darkSquareColor;
3400 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3402 gc_values.foreground = darkSquareColor;
3403 gc_values.background = lightSquareColor;
3404 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3406 gc_values.foreground = jailSquareColor;
3407 gc_values.background = jailSquareColor;
3408 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3410 gc_values.foreground = whitePieceColor;
3411 gc_values.background = darkSquareColor;
3412 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3414 gc_values.foreground = whitePieceColor;
3415 gc_values.background = lightSquareColor;
3416 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3418 gc_values.foreground = whitePieceColor;
3419 gc_values.background = jailSquareColor;
3420 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3422 gc_values.foreground = blackPieceColor;
3423 gc_values.background = darkSquareColor;
3424 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3426 gc_values.foreground = blackPieceColor;
3427 gc_values.background = lightSquareColor;
3428 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3430 gc_values.foreground = blackPieceColor;
3431 gc_values.background = jailSquareColor;
3432 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3436 void loadXIM(xim, xmask, filename, dest, mask)
3449 fp = fopen(filename, "rb");
3451 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3458 for (y=0; y<h; ++y) {
3459 for (x=0; x<h; ++x) {
3464 XPutPixel(xim, x, y, blackPieceColor);
3466 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3469 XPutPixel(xim, x, y, darkSquareColor);
3471 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3474 XPutPixel(xim, x, y, whitePieceColor);
3476 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3479 XPutPixel(xim, x, y, lightSquareColor);
3481 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3489 /* create Pixmap of piece */
3490 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3492 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3495 /* create Pixmap of clipmask
3496 Note: We assume the white/black pieces have the same
3497 outline, so we make only 6 masks. This is okay
3498 since the XPM clipmask routines do the same. */
3500 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3502 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3505 /* now create the 1-bit version */
3506 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3509 values.foreground = 1;
3510 values.background = 0;
3512 /* Don't use XtGetGC, not read only */
3513 maskGC = XCreateGC(xDisplay, *mask,
3514 GCForeground | GCBackground, &values);
3515 XCopyPlane(xDisplay, temp, *mask, maskGC,
3516 0, 0, squareSize, squareSize, 0, 0, 1);
3517 XFreePixmap(xDisplay, temp);
3522 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3524 void CreateXIMPieces()
3529 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3534 /* The XSynchronize calls were copied from CreatePieces.
3535 Not sure if needed, but can't hurt */
3536 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3539 /* temp needed by loadXIM() */
3540 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3541 0, 0, ss, ss, AllPlanes, XYPixmap);
3543 if (strlen(appData.pixmapDirectory) == 0) {
3547 if (appData.monoMode) {
3548 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3552 fprintf(stderr, _("\nLoading XIMs...\n"));
3554 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3555 fprintf(stderr, "%d", piece+1);
3556 for (kind=0; kind<4; kind++) {
3557 fprintf(stderr, ".");
3558 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3559 ExpandPathName(appData.pixmapDirectory),
3560 piece <= (int) WhiteKing ? "" : "w",
3561 pieceBitmapNames[piece],
3563 ximPieceBitmap[kind][piece] =
3564 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3565 0, 0, ss, ss, AllPlanes, XYPixmap);
3566 if (appData.debugMode)
3567 fprintf(stderr, _("(File:%s:) "), buf);
3568 loadXIM(ximPieceBitmap[kind][piece],
3570 &(xpmPieceBitmap2[kind][piece]),
3571 &(ximMaskPm2[piece]));
3572 if(piece <= (int)WhiteKing)
3573 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3575 fprintf(stderr," ");
3577 /* Load light and dark squares */
3578 /* If the LSQ and DSQ pieces don't exist, we will
3579 draw them with solid squares. */
3580 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3581 if (access(buf, 0) != 0) {
3585 fprintf(stderr, _("light square "));
3587 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3588 0, 0, ss, ss, AllPlanes, XYPixmap);
3589 if (appData.debugMode)
3590 fprintf(stderr, _("(File:%s:) "), buf);
3592 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3593 fprintf(stderr, _("dark square "));
3594 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3595 ExpandPathName(appData.pixmapDirectory), ss);
3596 if (appData.debugMode)
3597 fprintf(stderr, _("(File:%s:) "), buf);
3599 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3600 0, 0, ss, ss, AllPlanes, XYPixmap);
3601 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3602 xpmJailSquare = xpmLightSquare;
3604 fprintf(stderr, _("Done.\n"));
3606 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3609 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3612 void CreateXPMBoard(char *s, int kind)
3616 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3617 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3618 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3622 void FreeXPMPieces()
3623 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3624 // thisroutine has to be called t free the old piece pixmaps
3626 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3627 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3629 XFreePixmap(xDisplay, xpmLightSquare);
3630 XFreePixmap(xDisplay, xpmDarkSquare);
3634 void CreateXPMPieces()
3638 u_int ss = squareSize;
3640 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3641 XpmColorSymbol symbols[4];
3642 static int redo = False;
3644 if(redo) FreeXPMPieces(); else redo = 1;
3646 /* The XSynchronize calls were copied from CreatePieces.
3647 Not sure if needed, but can't hurt */
3648 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3650 /* Setup translations so piece colors match square colors */
3651 symbols[0].name = "light_piece";
3652 symbols[0].value = appData.whitePieceColor;
3653 symbols[1].name = "dark_piece";
3654 symbols[1].value = appData.blackPieceColor;
3655 symbols[2].name = "light_square";
3656 symbols[2].value = appData.lightSquareColor;
3657 symbols[3].name = "dark_square";
3658 symbols[3].value = appData.darkSquareColor;
3660 attr.valuemask = XpmColorSymbols;
3661 attr.colorsymbols = symbols;
3662 attr.numsymbols = 4;
3664 if (appData.monoMode) {
3665 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3669 if (strlen(appData.pixmapDirectory) == 0) {
3670 XpmPieces* pieces = builtInXpms;
3673 while (pieces->size != squareSize && pieces->size) pieces++;
3674 if (!pieces->size) {
3675 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3678 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3679 for (kind=0; kind<4; kind++) {
3681 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3682 pieces->xpm[piece][kind],
3683 &(xpmPieceBitmap2[kind][piece]),
3684 NULL, &attr)) != 0) {
3685 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3689 if(piece <= (int) WhiteKing)
3690 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3694 xpmJailSquare = xpmLightSquare;
3698 fprintf(stderr, _("\nLoading XPMs...\n"));
3701 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3702 fprintf(stderr, "%d ", piece+1);
3703 for (kind=0; kind<4; kind++) {
3704 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3705 ExpandPathName(appData.pixmapDirectory),
3706 piece > (int) WhiteKing ? "w" : "",
3707 pieceBitmapNames[piece],
3709 if (appData.debugMode) {
3710 fprintf(stderr, _("(File:%s:) "), buf);
3712 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3713 &(xpmPieceBitmap2[kind][piece]),
3714 NULL, &attr)) != 0) {
3715 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3716 // [HGM] missing: read of unorthodox piece failed; substitute King.
3717 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3718 ExpandPathName(appData.pixmapDirectory),
3720 if (appData.debugMode) {
3721 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3723 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3724 &(xpmPieceBitmap2[kind][piece]),
3728 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3733 if(piece <= (int) WhiteKing)
3734 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3737 /* Load light and dark squares */
3738 /* If the LSQ and DSQ pieces don't exist, we will
3739 draw them with solid squares. */
3740 fprintf(stderr, _("light square "));
3741 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3742 if (access(buf, 0) != 0) {
3746 if (appData.debugMode)
3747 fprintf(stderr, _("(File:%s:) "), buf);
3749 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3750 &xpmLightSquare, NULL, &attr)) != 0) {
3751 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3754 fprintf(stderr, _("dark square "));
3755 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3756 ExpandPathName(appData.pixmapDirectory), ss);
3757 if (appData.debugMode) {
3758 fprintf(stderr, _("(File:%s:) "), buf);
3760 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3761 &xpmDarkSquare, NULL, &attr)) != 0) {
3762 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3766 xpmJailSquare = xpmLightSquare;
3767 fprintf(stderr, _("Done.\n"));
3769 oldVariant = -1; // kludge to force re-makig of animation masks
3770 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3773 #endif /* HAVE_LIBXPM */
3776 /* No built-in bitmaps */
3781 u_int ss = squareSize;
3783 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3786 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3787 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3788 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3789 pieceBitmapNames[piece],
3790 ss, kind == SOLID ? 's' : 'o');
3791 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3792 if(piece <= (int)WhiteKing)
3793 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3797 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3801 /* With built-in bitmaps */
3804 BuiltInBits* bib = builtInBits;
3807 u_int ss = squareSize;
3809 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3812 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3814 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3815 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3816 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3817 pieceBitmapNames[piece],
3818 ss, kind == SOLID ? 's' : 'o');
3819 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3820 bib->bits[kind][piece], ss, ss);
3821 if(piece <= (int)WhiteKing)
3822 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3826 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3831 void ReadBitmap(pm, name, bits, wreq, hreq)
3834 unsigned char bits[];
3840 char msg[MSG_SIZ], fullname[MSG_SIZ];
3842 if (*appData.bitmapDirectory != NULLCHAR) {
3843 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3844 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3845 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3846 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3847 &w, &h, pm, &x_hot, &y_hot);
3848 fprintf(stderr, "load %s\n", name);
3849 if (errcode != BitmapSuccess) {
3851 case BitmapOpenFailed:
3852 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3854 case BitmapFileInvalid:
3855 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3857 case BitmapNoMemory:
3858 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3862 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3866 fprintf(stderr, _("%s: %s...using built-in\n"),
3868 } else if (w != wreq || h != hreq) {
3870 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3871 programName, fullname, w, h, wreq, hreq);
3877 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3886 if (lineGap == 0) return;
3888 /* [HR] Split this into 2 loops for non-square boards. */
3890 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3891 gridSegments[i].x1 = 0;
3892 gridSegments[i].x2 =
3893 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3894 gridSegments[i].y1 = gridSegments[i].y2
3895 = lineGap / 2 + (i * (squareSize + lineGap));
3898 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3899 gridSegments[j + i].y1 = 0;
3900 gridSegments[j + i].y2 =
3901 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3902 gridSegments[j + i].x1 = gridSegments[j + i].x2
3903 = lineGap / 2 + (j * (squareSize + lineGap));
3907 static void MenuBarSelect(w, addr, index)
3912 XtActionProc proc = (XtActionProc) addr;
3914 (proc)(NULL, NULL, NULL, NULL);
3917 void CreateMenuBarPopup(parent, name, mb)
3927 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3930 XtSetArg(args[j], XtNleftMargin, 20); j++;
3931 XtSetArg(args[j], XtNrightMargin, 20); j++;
3933 while (mi->string != NULL) {
3934 if (strcmp(mi->string, "----") == 0) {
3935 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3938 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3939 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3941 XtAddCallback(entry, XtNcallback,
3942 (XtCallbackProc) MenuBarSelect,
3943 (caddr_t) mi->proc);
3949 Widget CreateMenuBar(mb)
3953 Widget anchor, menuBar;
3955 char menuName[MSG_SIZ];
3958 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3959 XtSetArg(args[j], XtNvSpace, 0); j++;
3960 XtSetArg(args[j], XtNborderWidth, 0); j++;
3961 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3962 formWidget, args, j);
3964 while (mb->name != NULL) {
3965 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3966 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3968 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3971 shortName[0] = mb->name[0];
3972 shortName[1] = NULLCHAR;
3973 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3976 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3979 XtSetArg(args[j], XtNborderWidth, 0); j++;
3980 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3982 CreateMenuBarPopup(menuBar, menuName, mb);
3988 Widget CreateButtonBar(mi)
3992 Widget button, buttonBar;
3996 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3998 XtSetArg(args[j], XtNhSpace, 0); j++;
4000 XtSetArg(args[j], XtNborderWidth, 0); j++;
4001 XtSetArg(args[j], XtNvSpace, 0); j++;
4002 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4003 formWidget, args, j);
4005 while (mi->string != NULL) {
4008 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4009 XtSetArg(args[j], XtNborderWidth, 0); j++;
4011 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4012 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4013 buttonBar, args, j);
4014 XtAddCallback(button, XtNcallback,
4015 (XtCallbackProc) MenuBarSelect,
4016 (caddr_t) mi->proc);
4023 CreatePieceMenu(name, color)
4030 ChessSquare selection;
4032 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4033 boardWidget, args, 0);
4035 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4036 String item = pieceMenuStrings[color][i];
4038 if (strcmp(item, "----") == 0) {
4039 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4042 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4043 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4045 selection = pieceMenuTranslation[color][i];
4046 XtAddCallback(entry, XtNcallback,
4047 (XtCallbackProc) PieceMenuSelect,
4048 (caddr_t) selection);
4049 if (selection == WhitePawn || selection == BlackPawn) {
4050 XtSetArg(args[0], XtNpopupOnEntry, entry);
4051 XtSetValues(menu, args, 1);
4064 ChessSquare selection;
4066 whitePieceMenu = CreatePieceMenu("menuW", 0);
4067 blackPieceMenu = CreatePieceMenu("menuB", 1);
4069 XtRegisterGrabAction(PieceMenuPopup, True,
4070 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4071 GrabModeAsync, GrabModeAsync);
4073 XtSetArg(args[0], XtNlabel, _("Drop"));
4074 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4075 boardWidget, args, 1);
4076 for (i = 0; i < DROP_MENU_SIZE; i++) {
4077 String item = dropMenuStrings[i];
4079 if (strcmp(item, "----") == 0) {
4080 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4083 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4084 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4086 selection = dropMenuTranslation[i];
4087 XtAddCallback(entry, XtNcallback,
4088 (XtCallbackProc) DropMenuSelect,
4089 (caddr_t) selection);
4094 void SetupDropMenu()
4102 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4103 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4104 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4105 dmEnables[i].piece);
4106 XtSetSensitive(entry, p != NULL || !appData.testLegality
4107 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4108 && !appData.icsActive));
4110 while (p && *p++ == dmEnables[i].piece) count++;
4111 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4113 XtSetArg(args[j], XtNlabel, label); j++;
4114 XtSetValues(entry, args, j);
4118 void PieceMenuPopup(w, event, params, num_params)
4122 Cardinal *num_params;
4124 String whichMenu; int menuNr = -2;
4125 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4126 if (event->type == ButtonRelease)
4127 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4128 else if (event->type == ButtonPress)
4129 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4131 case 0: whichMenu = params[0]; break;
4132 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4134 case -1: if (errorUp) ErrorPopDown();
4137 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4140 static void PieceMenuSelect(w, piece, junk)
4145 if (pmFromX < 0 || pmFromY < 0) return;
4146 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4149 static void DropMenuSelect(w, piece, junk)
4154 if (pmFromX < 0 || pmFromY < 0) return;
4155 DropMenuEvent(piece, pmFromX, pmFromY);
4158 void WhiteClock(w, event, prms, nprms)
4164 shiftKey = prms[0][0] & 1;
4168 void BlackClock(w, event, prms, nprms)
4174 shiftKey = prms[0][0] & 1;
4180 * If the user selects on a border boundary, return -1; if off the board,
4181 * return -2. Otherwise map the event coordinate to the square.
4183 int EventToSquare(x, limit)
4191 if ((x % (squareSize + lineGap)) >= squareSize)
4193 x /= (squareSize + lineGap);
4199 static void do_flash_delay(msec)
4205 static void drawHighlight(file, rank, gc)
4211 if (lineGap == 0) return;
4214 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4215 (squareSize + lineGap);
4216 y = lineGap/2 + rank * (squareSize + lineGap);
4218 x = lineGap/2 + file * (squareSize + lineGap);
4219 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4220 (squareSize + lineGap);
4223 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4224 squareSize+lineGap, squareSize+lineGap);
4227 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4228 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4231 SetHighlights(fromX, fromY, toX, toY)
4232 int fromX, fromY, toX, toY;
4234 if (hi1X != fromX || hi1Y != fromY) {
4235 if (hi1X >= 0 && hi1Y >= 0) {
4236 drawHighlight(hi1X, hi1Y, lineGC);
4238 } // [HGM] first erase both, then draw new!
4239 if (hi2X != toX || hi2Y != toY) {
4240 if (hi2X >= 0 && hi2Y >= 0) {
4241 drawHighlight(hi2X, hi2Y, lineGC);
4244 if (hi1X != fromX || hi1Y != fromY) {
4245 if (fromX >= 0 && fromY >= 0) {
4246 drawHighlight(fromX, fromY, highlineGC);
4249 if (hi2X != toX || hi2Y != toY) {
4250 if (toX >= 0 && toY >= 0) {
4251 drawHighlight(toX, toY, highlineGC);
4263 SetHighlights(-1, -1, -1, -1);
4268 SetPremoveHighlights(fromX, fromY, toX, toY)
4269 int fromX, fromY, toX, toY;
4271 if (pm1X != fromX || pm1Y != fromY) {
4272 if (pm1X >= 0 && pm1Y >= 0) {
4273 drawHighlight(pm1X, pm1Y, lineGC);
4275 if (fromX >= 0 && fromY >= 0) {
4276 drawHighlight(fromX, fromY, prelineGC);
4279 if (pm2X != toX || pm2Y != toY) {
4280 if (pm2X >= 0 && pm2Y >= 0) {
4281 drawHighlight(pm2X, pm2Y, lineGC);
4283 if (toX >= 0 && toY >= 0) {
4284 drawHighlight(toX, toY, prelineGC);
4294 ClearPremoveHighlights()
4296 SetPremoveHighlights(-1, -1, -1, -1);
4299 static int CutOutSquare(x, y, x0, y0, kind)
4300 int x, y, *x0, *y0, kind;
4302 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4303 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4305 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4306 if(textureW[kind] < W*squareSize)
4307 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4309 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4310 if(textureH[kind] < H*squareSize)
4311 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4313 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4317 static void BlankSquare(x, y, color, piece, dest, fac)
4318 int x, y, color, fac;
4321 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4323 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4324 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4325 squareSize, squareSize, x*fac, y*fac);
4327 if (useImages && useImageSqs) {
4331 pm = xpmLightSquare;
4336 case 2: /* neutral */
4341 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4342 squareSize, squareSize, x*fac, y*fac);
4352 case 2: /* neutral */
4357 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4362 I split out the routines to draw a piece so that I could
4363 make a generic flash routine.
4365 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4367 int square_color, x, y;
4370 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4371 switch (square_color) {
4373 case 2: /* neutral */
4375 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4376 ? *pieceToOutline(piece)
4377 : *pieceToSolid(piece),
4378 dest, bwPieceGC, 0, 0,
4379 squareSize, squareSize, x, y);
4382 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4383 ? *pieceToSolid(piece)
4384 : *pieceToOutline(piece),
4385 dest, wbPieceGC, 0, 0,
4386 squareSize, squareSize, x, y);
4391 static void monoDrawPiece(piece, square_color, x, y, dest)
4393 int square_color, x, y;
4396 switch (square_color) {
4398 case 2: /* neutral */
4400 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4401 ? *pieceToOutline(piece)
4402 : *pieceToSolid(piece),
4403 dest, bwPieceGC, 0, 0,
4404 squareSize, squareSize, x, y, 1);
4407 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4408 ? *pieceToSolid(piece)
4409 : *pieceToOutline(piece),
4410 dest, wbPieceGC, 0, 0,
4411 squareSize, squareSize, x, y, 1);
4416 static void colorDrawPiece(piece, square_color, x, y, dest)
4418 int square_color, x, y;
4421 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4422 switch (square_color) {
4424 XCopyPlane(xDisplay, *pieceToSolid(piece),
4425 dest, (int) piece < (int) BlackPawn
4426 ? wlPieceGC : blPieceGC, 0, 0,
4427 squareSize, squareSize, x, y, 1);
4430 XCopyPlane(xDisplay, *pieceToSolid(piece),
4431 dest, (int) piece < (int) BlackPawn
4432 ? wdPieceGC : bdPieceGC, 0, 0,
4433 squareSize, squareSize, x, y, 1);
4435 case 2: /* neutral */
4437 XCopyPlane(xDisplay, *pieceToSolid(piece),
4438 dest, (int) piece < (int) BlackPawn
4439 ? wjPieceGC : bjPieceGC, 0, 0,
4440 squareSize, squareSize, x, y, 1);
4445 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4447 int square_color, x, y;
4450 int kind, p = piece;
4452 switch (square_color) {
4454 case 2: /* neutral */
4456 if ((int)piece < (int) BlackPawn) {
4464 if ((int)piece < (int) BlackPawn) {
4472 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4473 if(useTexture & square_color+1) {
4474 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4475 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4476 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4477 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4478 XSetClipMask(xDisplay, wlPieceGC, None);
4479 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4481 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4482 dest, wlPieceGC, 0, 0,
4483 squareSize, squareSize, x, y);
4486 typedef void (*DrawFunc)();
4488 DrawFunc ChooseDrawFunc()
4490 if (appData.monoMode) {
4491 if (DefaultDepth(xDisplay, xScreen) == 1) {
4492 return monoDrawPiece_1bit;
4494 return monoDrawPiece;
4498 return colorDrawPieceImage;
4500 return colorDrawPiece;
4504 /* [HR] determine square color depending on chess variant. */
4505 static int SquareColor(row, column)
4510 if (gameInfo.variant == VariantXiangqi) {
4511 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4513 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4515 } else if (row <= 4) {
4521 square_color = ((column + row) % 2) == 1;
4524 /* [hgm] holdings: next line makes all holdings squares light */
4525 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4527 return square_color;
4530 void DrawSquare(row, column, piece, do_flash)
4531 int row, column, do_flash;
4534 int square_color, x, y, direction, font_ascent, font_descent;
4537 XCharStruct overall;
4541 /* Calculate delay in milliseconds (2-delays per complete flash) */
4542 flash_delay = 500 / appData.flashRate;
4545 x = lineGap + ((BOARD_WIDTH-1)-column) *
4546 (squareSize + lineGap);
4547 y = lineGap + row * (squareSize + lineGap);
4549 x = lineGap + column * (squareSize + lineGap);
4550 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4551 (squareSize + lineGap);
4554 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4556 square_color = SquareColor(row, column);
4558 if ( // [HGM] holdings: blank out area between board and holdings
4559 column == BOARD_LEFT-1 || column == BOARD_RGHT
4560 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4561 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4562 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4564 // [HGM] print piece counts next to holdings
4565 string[1] = NULLCHAR;
4566 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4567 string[0] = '0' + piece;
4568 XTextExtents(countFontStruct, string, 1, &direction,
4569 &font_ascent, &font_descent, &overall);
4570 if (appData.monoMode) {
4571 XDrawImageString(xDisplay, xBoardWindow, countGC,
4572 x + squareSize - overall.width - 2,
4573 y + font_ascent + 1, string, 1);
4575 XDrawString(xDisplay, xBoardWindow, countGC,
4576 x + squareSize - overall.width - 2,
4577 y + font_ascent + 1, string, 1);
4580 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4581 string[0] = '0' + piece;
4582 XTextExtents(countFontStruct, string, 1, &direction,
4583 &font_ascent, &font_descent, &overall);
4584 if (appData.monoMode) {
4585 XDrawImageString(xDisplay, xBoardWindow, countGC,
4586 x + 2, y + font_ascent + 1, string, 1);
4588 XDrawString(xDisplay, xBoardWindow, countGC,
4589 x + 2, y + font_ascent + 1, string, 1);
4593 if (piece == EmptySquare || appData.blindfold) {
4594 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4596 drawfunc = ChooseDrawFunc();
4598 if (do_flash && appData.flashCount > 0) {
4599 for (i=0; i<appData.flashCount; ++i) {
4600 drawfunc(piece, square_color, x, y, xBoardWindow);
4601 XSync(xDisplay, False);
4602 do_flash_delay(flash_delay);
4604 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4605 XSync(xDisplay, False);
4606 do_flash_delay(flash_delay);
4609 drawfunc(piece, square_color, x, y, xBoardWindow);
4613 string[1] = NULLCHAR;
4614 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4615 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4616 string[0] = 'a' + column - BOARD_LEFT;
4617 XTextExtents(coordFontStruct, string, 1, &direction,
4618 &font_ascent, &font_descent, &overall);
4619 if (appData.monoMode) {
4620 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4621 x + squareSize - overall.width - 2,
4622 y + squareSize - font_descent - 1, string, 1);
4624 XDrawString(xDisplay, xBoardWindow, coordGC,
4625 x + squareSize - overall.width - 2,
4626 y + squareSize - font_descent - 1, string, 1);
4629 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4630 string[0] = ONE + row;
4631 XTextExtents(coordFontStruct, string, 1, &direction,
4632 &font_ascent, &font_descent, &overall);
4633 if (appData.monoMode) {
4634 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4635 x + 2, y + font_ascent + 1, string, 1);
4637 XDrawString(xDisplay, xBoardWindow, coordGC,
4638 x + 2, y + font_ascent + 1, string, 1);
4641 if(!partnerUp && marker[row][column]) {
4642 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4643 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4648 /* Why is this needed on some versions of X? */
4649 void EventProc(widget, unused, event)
4654 if (!XtIsRealized(widget))
4657 switch (event->type) {
4659 if (event->xexpose.count > 0) return; /* no clipping is done */
4660 XDrawPosition(widget, True, NULL);
4661 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4662 flipView = !flipView; partnerUp = !partnerUp;
4663 XDrawPosition(widget, True, NULL);
4664 flipView = !flipView; partnerUp = !partnerUp;
4668 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4675 void DrawPosition(fullRedraw, board)
4676 /*Boolean*/int fullRedraw;
4679 XDrawPosition(boardWidget, fullRedraw, board);
4682 /* Returns 1 if there are "too many" differences between b1 and b2
4683 (i.e. more than 1 move was made) */
4684 static int too_many_diffs(b1, b2)
4690 for (i=0; i<BOARD_HEIGHT; ++i) {
4691 for (j=0; j<BOARD_WIDTH; ++j) {
4692 if (b1[i][j] != b2[i][j]) {
4693 if (++c > 4) /* Castling causes 4 diffs */
4701 /* Matrix describing castling maneuvers */
4702 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4703 static int castling_matrix[4][5] = {
4704 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4705 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4706 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4707 { 7, 7, 4, 5, 6 } /* 0-0, black */
4710 /* Checks whether castling occurred. If it did, *rrow and *rcol
4711 are set to the destination (row,col) of the rook that moved.
4713 Returns 1 if castling occurred, 0 if not.
4715 Note: Only handles a max of 1 castling move, so be sure
4716 to call too_many_diffs() first.
4718 static int check_castle_draw(newb, oldb, rrow, rcol)
4725 /* For each type of castling... */
4726 for (i=0; i<4; ++i) {
4727 r = castling_matrix[i];
4729 /* Check the 4 squares involved in the castling move */
4731 for (j=1; j<=4; ++j) {
4732 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4739 /* All 4 changed, so it must be a castling move */
4748 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4749 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4751 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4754 void DrawSeekBackground( int left, int top, int right, int bottom )
4756 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4759 void DrawSeekText(char *buf, int x, int y)
4761 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4764 void DrawSeekDot(int x, int y, int colorNr)
4766 int square = colorNr & 0x80;
4769 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4771 XFillRectangle(xDisplay, xBoardWindow, color,
4772 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4774 XFillArc(xDisplay, xBoardWindow, color,
4775 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4778 static int damage[2][BOARD_RANKS][BOARD_FILES];
4781 * event handler for redrawing the board
4783 void XDrawPosition(w, repaint, board)
4785 /*Boolean*/int repaint;
4789 static int lastFlipView = 0;
4790 static int lastBoardValid[2] = {0, 0};
4791 static Board lastBoard[2];
4794 int nr = twoBoards*partnerUp;
4796 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4798 if (board == NULL) {
4799 if (!lastBoardValid[nr]) return;
4800 board = lastBoard[nr];
4802 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4803 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4804 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4809 * It would be simpler to clear the window with XClearWindow()
4810 * but this causes a very distracting flicker.
4813 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4815 if ( lineGap && IsDrawArrowEnabled())
4816 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4817 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4819 /* If too much changes (begin observing new game, etc.), don't
4821 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4823 /* Special check for castling so we don't flash both the king
4824 and the rook (just flash the king). */
4826 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4827 /* Draw rook with NO flashing. King will be drawn flashing later */
4828 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4829 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4833 /* First pass -- Draw (newly) empty squares and repair damage.
4834 This prevents you from having a piece show up twice while it
4835 is flashing on its new square */
4836 for (i = 0; i < BOARD_HEIGHT; i++)
4837 for (j = 0; j < BOARD_WIDTH; j++)
4838 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4839 || damage[nr][i][j]) {
4840 DrawSquare(i, j, board[i][j], 0);
4841 damage[nr][i][j] = False;
4844 /* Second pass -- Draw piece(s) in new position and flash them */
4845 for (i = 0; i < BOARD_HEIGHT; i++)
4846 for (j = 0; j < BOARD_WIDTH; j++)
4847 if (board[i][j] != lastBoard[nr][i][j]) {
4848 DrawSquare(i, j, board[i][j], do_flash);
4852 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4853 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4854 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4856 for (i = 0; i < BOARD_HEIGHT; i++)
4857 for (j = 0; j < BOARD_WIDTH; j++) {
4858 DrawSquare(i, j, board[i][j], 0);
4859 damage[nr][i][j] = False;
4863 CopyBoard(lastBoard[nr], board);
4864 lastBoardValid[nr] = 1;
4865 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4866 lastFlipView = flipView;
4868 /* Draw highlights */
4869 if (pm1X >= 0 && pm1Y >= 0) {
4870 drawHighlight(pm1X, pm1Y, prelineGC);
4872 if (pm2X >= 0 && pm2Y >= 0) {
4873 drawHighlight(pm2X, pm2Y, prelineGC);
4875 if (hi1X >= 0 && hi1Y >= 0) {
4876 drawHighlight(hi1X, hi1Y, highlineGC);
4878 if (hi2X >= 0 && hi2Y >= 0) {
4879 drawHighlight(hi2X, hi2Y, highlineGC);
4881 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4883 /* If piece being dragged around board, must redraw that too */
4886 XSync(xDisplay, False);
4891 * event handler for redrawing the board
4893 void DrawPositionProc(w, event, prms, nprms)
4899 XDrawPosition(w, True, NULL);
4904 * event handler for parsing user moves
4906 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4907 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4908 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4909 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4910 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4911 // and at the end FinishMove() to perform the move after optional promotion popups.
4912 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4913 void HandleUserMove(w, event, prms, nprms)
4919 if (w != boardWidget || errorExitStatus != -1) return;
4920 if(nprms) shiftKey = !strcmp(prms[0], "1");
4923 if (event->type == ButtonPress) {
4924 XtPopdown(promotionShell);
4925 XtDestroyWidget(promotionShell);
4926 promotionUp = False;
4934 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4935 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4936 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4939 void AnimateUserMove (Widget w, XEvent * event,
4940 String * params, Cardinal * nParams)
4942 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4943 DragPieceMove(event->xmotion.x, event->xmotion.y);
4946 void HandlePV (Widget w, XEvent * event,
4947 String * params, Cardinal * nParams)
4948 { // [HGM] pv: walk PV
4949 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4952 static int savedIndex; /* gross that this is global */
4954 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4957 XawTextPosition index, dummy;
4960 XawTextGetSelectionPos(w, &index, &dummy);
4961 XtSetArg(arg, XtNstring, &val);
4962 XtGetValues(w, &arg, 1);
4963 ReplaceComment(savedIndex, val);
4964 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4965 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4968 void EditCommentPopUp(index, title, text)
4973 if (text == NULL) text = "";
4974 NewCommentPopup(title, text, index);
4977 void ICSInputBoxPopUp()
4982 extern Option boxOptions[];
4984 void ICSInputSendText()
4991 edit = boxOptions[0].handle;
4993 XtSetArg(args[j], XtNstring, &val); j++;
4994 XtGetValues(edit, args, j);
4996 SendMultiLineToICS(val);
4997 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4998 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5001 void ICSInputBoxPopDown()
5006 void CommentPopUp(title, text)
5009 savedIndex = currentMove; // [HGM] vari
5010 NewCommentPopup(title, text, currentMove);
5013 void CommentPopDown()
5018 static char *openName;
5023 (void) (*fileProc)(openFP, 0, openName);
5026 void FileNamePopUp(label, def, filter, proc, openMode)
5033 fileProc = proc; /* I can't see a way not */
5034 fileOpenMode = openMode; /* to use globals here */
5035 { // [HGM] use file-selector dialog stolen from Ghostview
5036 int index; // this is not supported yet
5037 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5038 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5039 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5040 ScheduleDelayedEvent(&DelayedLoad, 50);
5044 void FileNamePopDown()
5046 if (!filenameUp) return;
5047 XtPopdown(fileNameShell);
5048 XtDestroyWidget(fileNameShell);
5053 void FileNameCallback(w, client_data, call_data)
5055 XtPointer client_data, call_data;
5060 XtSetArg(args[0], XtNlabel, &name);
5061 XtGetValues(w, args, 1);
5063 if (strcmp(name, _("cancel")) == 0) {
5068 FileNameAction(w, NULL, NULL, NULL);
5071 void FileNameAction(w, event, prms, nprms)
5083 name = XawDialogGetValueString(w = XtParent(w));
5085 if ((name != NULL) && (*name != NULLCHAR)) {
5086 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5087 XtPopdown(w = XtParent(XtParent(w)));
5091 p = strrchr(buf, ' ');
5098 fullname = ExpandPathName(buf);
5100 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5103 f = fopen(fullname, fileOpenMode);
5105 DisplayError(_("Failed to open file"), errno);
5107 (void) (*fileProc)(f, index, buf);
5114 XtPopdown(w = XtParent(XtParent(w)));
5120 void PromotionPopUp()
5123 Widget dialog, layout;
5125 Dimension bw_width, pw_width;
5129 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5130 XtGetValues(boardWidget, args, j);
5133 XtSetArg(args[j], XtNresizable, True); j++;
5134 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5136 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5137 shellWidget, args, j);
5139 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5140 layoutArgs, XtNumber(layoutArgs));
5143 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5144 XtSetArg(args[j], XtNborderWidth, 0); j++;
5145 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5148 if(gameInfo.variant != VariantShogi) {
5149 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5150 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5151 (XtPointer) dialog);
5152 XawDialogAddButton(dialog, _("General"), PromotionCallback,
5153 (XtPointer) dialog);
5154 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5155 (XtPointer) dialog);
5156 XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5157 (XtPointer) dialog);
5159 XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5160 (XtPointer) dialog);
5161 XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5162 (XtPointer) dialog);
5163 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5164 (XtPointer) dialog);
5165 XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5166 (XtPointer) dialog);
5168 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5169 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5170 gameInfo.variant == VariantGiveaway) {
5171 XawDialogAddButton(dialog, _("King"), PromotionCallback,
5172 (XtPointer) dialog);
5174 if(gameInfo.variant == VariantCapablanca ||
5175 gameInfo.variant == VariantGothic ||
5176 gameInfo.variant == VariantCapaRandom) {
5177 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5178 (XtPointer) dialog);
5179 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5180 (XtPointer) dialog);
5182 } else // [HGM] shogi
5184 XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5185 (XtPointer) dialog);
5186 XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5187 (XtPointer) dialog);
5189 XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5190 (XtPointer) dialog);
5192 XtRealizeWidget(promotionShell);
5193 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5196 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5197 XtGetValues(promotionShell, args, j);
5199 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5200 lineGap + squareSize/3 +
5201 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5202 0 : 6*(squareSize + lineGap)), &x, &y);
5205 XtSetArg(args[j], XtNx, x); j++;
5206 XtSetArg(args[j], XtNy, y); j++;
5207 XtSetValues(promotionShell, args, j);
5209 XtPopup(promotionShell, XtGrabNone);
5214 void PromotionPopDown()
5216 if (!promotionUp) return;
5217 XtPopdown(promotionShell);
5218 XtDestroyWidget(promotionShell);
5219 promotionUp = False;
5222 void PromotionCallback(w, client_data, call_data)
5224 XtPointer client_data, call_data;
5230 XtSetArg(args[0], XtNlabel, &name);
5231 XtGetValues(w, args, 1);
5235 if (fromX == -1) return;
5237 if (strcmp(name, _("cancel")) == 0) {
5241 } else if (strcmp(name, _("Knight")) == 0) {
5243 } else if (strcmp(name, _("Promote")) == 0) {
5245 } else if (strcmp(name, _("Defer")) == 0) {
5248 promoChar = ToLower(name[0]);
5251 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5253 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5254 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5259 void ErrorCallback(w, client_data, call_data)
5261 XtPointer client_data, call_data;
5264 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5266 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5272 if (!errorUp) return;
5274 XtPopdown(errorShell);
5275 XtDestroyWidget(errorShell);
5276 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5279 void ErrorPopUp(title, label, modal)
5280 char *title, *label;
5284 Widget dialog, layout;
5288 Dimension bw_width, pw_width;
5289 Dimension pw_height;
5293 XtSetArg(args[i], XtNresizable, True); i++;
5294 XtSetArg(args[i], XtNtitle, title); i++;
5296 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5297 shellWidget, args, i);
5299 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5300 layoutArgs, XtNumber(layoutArgs));
5303 XtSetArg(args[i], XtNlabel, label); i++;
5304 XtSetArg(args[i], XtNborderWidth, 0); i++;
5305 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5308 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5310 XtRealizeWidget(errorShell);
5311 CatchDeleteWindow(errorShell, "ErrorPopDown");
5314 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5315 XtGetValues(boardWidget, args, i);
5317 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5318 XtSetArg(args[i], XtNheight, &pw_height); i++;
5319 XtGetValues(errorShell, args, i);
5322 /* This code seems to tickle an X bug if it is executed too soon
5323 after xboard starts up. The coordinates get transformed as if
5324 the main window was positioned at (0, 0).
5326 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5327 0 - pw_height + squareSize / 3, &x, &y);
5329 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5330 RootWindowOfScreen(XtScreen(boardWidget)),
5331 (bw_width - pw_width) / 2,
5332 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5336 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5339 XtSetArg(args[i], XtNx, x); i++;
5340 XtSetArg(args[i], XtNy, y); i++;
5341 XtSetValues(errorShell, args, i);
5344 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5347 /* Disable all user input other than deleting the window */
5348 static int frozen = 0;
5352 /* Grab by a widget that doesn't accept input */
5353 XtAddGrab(messageWidget, TRUE, FALSE);
5357 /* Undo a FreezeUI */
5360 if (!frozen) return;
5361 XtRemoveGrab(messageWidget);
5365 char *ModeToWidgetName(mode)
5369 case BeginningOfGame:
5370 if (appData.icsActive)
5371 return "menuMode.ICS Client";
5372 else if (appData.noChessProgram ||
5373 *appData.cmailGameName != NULLCHAR)
5374 return "menuMode.Edit Game";
5376 return "menuMode.Machine Black";
5377 case MachinePlaysBlack:
5378 return "menuMode.Machine Black";
5379 case MachinePlaysWhite:
5380 return "menuMode.Machine White";
5382 return "menuMode.Analysis Mode";
5384 return "menuMode.Analyze File";
5385 case TwoMachinesPlay:
5386 return "menuMode.Two Machines";
5388 return "menuMode.Edit Game";
5389 case PlayFromGameFile:
5390 return "menuFile.Load Game";
5392 return "menuMode.Edit Position";
5394 return "menuMode.Training";
5395 case IcsPlayingWhite:
5396 case IcsPlayingBlack:
5400 return "menuMode.ICS Client";
5407 void ModeHighlight()
5410 static int oldPausing = FALSE;
5411 static GameMode oldmode = (GameMode) -1;
5414 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5416 if (pausing != oldPausing) {
5417 oldPausing = pausing;
5419 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5421 XtSetArg(args[0], XtNleftBitmap, None);
5423 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5426 if (appData.showButtonBar) {
5427 /* Always toggle, don't set. Previous code messes up when
5428 invoked while the button is pressed, as releasing it
5429 toggles the state again. */
5432 XtSetArg(args[0], XtNbackground, &oldbg);
5433 XtSetArg(args[1], XtNforeground, &oldfg);
5434 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5436 XtSetArg(args[0], XtNbackground, oldfg);
5437 XtSetArg(args[1], XtNforeground, oldbg);
5439 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5443 wname = ModeToWidgetName(oldmode);
5444 if (wname != NULL) {
5445 XtSetArg(args[0], XtNleftBitmap, None);
5446 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5448 wname = ModeToWidgetName(gameMode);
5449 if (wname != NULL) {
5450 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5451 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5454 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5455 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5457 /* Maybe all the enables should be handled here, not just this one */
5458 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5459 gameMode == Training || gameMode == PlayFromGameFile);
5464 * Button/menu procedures
5466 void ResetProc(w, event, prms, nprms)
5475 int LoadGamePopUp(f, gameNumber, title)
5480 cmailMsgLoaded = FALSE;
5481 if (gameNumber == 0) {
5482 int error = GameListBuild(f);
5484 DisplayError(_("Cannot build game list"), error);
5485 } else if (!ListEmpty(&gameList) &&
5486 ((ListGame *) gameList.tailPred)->number > 1) {
5487 GameListPopUp(f, title);
5493 return LoadGame(f, gameNumber, title, FALSE);
5496 void LoadGameProc(w, event, prms, nprms)
5502 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5505 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5508 void LoadNextGameProc(w, event, prms, nprms)
5517 void LoadPrevGameProc(w, event, prms, nprms)
5526 void ReloadGameProc(w, event, prms, nprms)
5535 void LoadNextPositionProc(w, event, prms, nprms)
5544 void LoadPrevPositionProc(w, event, prms, nprms)
5553 void ReloadPositionProc(w, event, prms, nprms)
5562 void LoadPositionProc(w, event, prms, nprms)
5568 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5571 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5574 void SaveGameProc(w, event, prms, nprms)
5580 FileNamePopUp(_("Save game file name?"),
5581 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5582 appData.oldSaveStyle ? ".game" : ".pgn",
5586 void SavePositionProc(w, event, prms, nprms)
5592 FileNamePopUp(_("Save position file name?"),
5593 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5594 appData.oldSaveStyle ? ".pos" : ".fen",
5598 void ReloadCmailMsgProc(w, event, prms, nprms)
5604 ReloadCmailMsgEvent(FALSE);
5607 void MailMoveProc(w, event, prms, nprms)
5616 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5617 char *selected_fen_position=NULL;
5620 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5621 Atom *type_return, XtPointer *value_return,
5622 unsigned long *length_return, int *format_return)
5624 char *selection_tmp;
5626 if (!selected_fen_position) return False; /* should never happen */
5627 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5628 /* note: since no XtSelectionDoneProc was registered, Xt will
5629 * automatically call XtFree on the value returned. So have to
5630 * make a copy of it allocated with XtMalloc */
5631 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5632 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5634 *value_return=selection_tmp;
5635 *length_return=strlen(selection_tmp);
5636 *type_return=*target;
5637 *format_return = 8; /* bits per byte */
5639 } else if (*target == XA_TARGETS(xDisplay)) {
5640 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5641 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5642 targets_tmp[1] = XA_STRING;
5643 *value_return = targets_tmp;
5644 *type_return = XA_ATOM;
5646 *format_return = 8 * sizeof(Atom);
5647 if (*format_return > 32) {
5648 *length_return *= *format_return / 32;
5649 *format_return = 32;
5657 /* note: when called from menu all parameters are NULL, so no clue what the
5658 * Widget which was clicked on was, or what the click event was
5660 void CopyPositionProc(w, event, prms, nprms)
5667 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5668 * have a notion of a position that is selected but not copied.
5669 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5671 if(gameMode == EditPosition) EditPositionDone(TRUE);
5672 if (selected_fen_position) free(selected_fen_position);
5673 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5674 if (!selected_fen_position) return;
5675 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5677 SendPositionSelection,
5678 NULL/* lose_ownership_proc */ ,
5679 NULL/* transfer_done_proc */);
5680 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5682 SendPositionSelection,
5683 NULL/* lose_ownership_proc */ ,
5684 NULL/* transfer_done_proc */);
5687 /* function called when the data to Paste is ready */
5689 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5690 Atom *type, XtPointer value, unsigned long *len, int *format)
5693 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5694 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5695 EditPositionPasteFEN(fenstr);
5699 /* called when Paste Position button is pressed,
5700 * all parameters will be NULL */
5701 void PastePositionProc(w, event, prms, nprms)
5707 XtGetSelectionValue(menuBarWidget,
5708 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5709 /* (XtSelectionCallbackProc) */ PastePositionCB,
5710 NULL, /* client_data passed to PastePositionCB */
5712 /* better to use the time field from the event that triggered the
5713 * call to this function, but that isn't trivial to get
5721 SendGameSelection(Widget w, Atom *selection, Atom *target,
5722 Atom *type_return, XtPointer *value_return,
5723 unsigned long *length_return, int *format_return)
5725 char *selection_tmp;
5727 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5728 FILE* f = fopen(gameCopyFilename, "r");
5731 if (f == NULL) return False;
5735 selection_tmp = XtMalloc(len + 1);
5736 count = fread(selection_tmp, 1, len, f);
5739 XtFree(selection_tmp);
5742 selection_tmp[len] = NULLCHAR;
5743 *value_return = selection_tmp;
5744 *length_return = len;
5745 *type_return = *target;
5746 *format_return = 8; /* bits per byte */
5748 } else if (*target == XA_TARGETS(xDisplay)) {
5749 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5750 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5751 targets_tmp[1] = XA_STRING;
5752 *value_return = targets_tmp;
5753 *type_return = XA_ATOM;
5755 *format_return = 8 * sizeof(Atom);
5756 if (*format_return > 32) {
5757 *length_return *= *format_return / 32;
5758 *format_return = 32;
5766 void CopySomething()
5769 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5770 * have a notion of a game that is selected but not copied.
5771 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5773 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5776 NULL/* lose_ownership_proc */ ,
5777 NULL/* transfer_done_proc */);
5778 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5781 NULL/* lose_ownership_proc */ ,
5782 NULL/* transfer_done_proc */);
5785 /* note: when called from menu all parameters are NULL, so no clue what the
5786 * Widget which was clicked on was, or what the click event was
5788 void CopyGameProc(w, event, prms, nprms)
5796 ret = SaveGameToFile(gameCopyFilename, FALSE);
5802 void CopyGameListProc(w, event, prms, nprms)
5808 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5812 /* function called when the data to Paste is ready */
5814 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5815 Atom *type, XtPointer value, unsigned long *len, int *format)
5818 if (value == NULL || *len == 0) {
5819 return; /* nothing had been selected to copy */
5821 f = fopen(gamePasteFilename, "w");
5823 DisplayError(_("Can't open temp file"), errno);
5826 fwrite(value, 1, *len, f);
5829 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5832 /* called when Paste Game button is pressed,
5833 * all parameters will be NULL */
5834 void PasteGameProc(w, event, prms, nprms)
5840 XtGetSelectionValue(menuBarWidget,
5841 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5842 /* (XtSelectionCallbackProc) */ PasteGameCB,
5843 NULL, /* client_data passed to PasteGameCB */
5845 /* better to use the time field from the event that triggered the
5846 * call to this function, but that isn't trivial to get
5856 SaveGameProc(NULL, NULL, NULL, NULL);
5860 void QuitProc(w, event, prms, nprms)
5869 void PauseProc(w, event, prms, nprms)
5879 void MachineBlackProc(w, event, prms, nprms)
5885 MachineBlackEvent();
5888 void MachineWhiteProc(w, event, prms, nprms)
5894 MachineWhiteEvent();
5897 void AnalyzeModeProc(w, event, prms, nprms)
5905 if (!first.analysisSupport) {
5906 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5907 DisplayError(buf, 0);
5910 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5911 if (appData.icsActive) {
5912 if (gameMode != IcsObserving) {
5913 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5914 DisplayError(buf, 0);
5916 if (appData.icsEngineAnalyze) {
5917 if (appData.debugMode)
5918 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5924 /* if enable, use want disable icsEngineAnalyze */
5925 if (appData.icsEngineAnalyze) {
5930 appData.icsEngineAnalyze = TRUE;
5931 if (appData.debugMode)
5932 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5934 #ifndef OPTIONSDIALOG
5935 if (!appData.showThinking)
5936 ShowThinkingProc(w,event,prms,nprms);
5942 void AnalyzeFileProc(w, event, prms, nprms)
5948 if (!first.analysisSupport) {
5950 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5951 DisplayError(buf, 0);
5954 // Reset(FALSE, TRUE);
5955 #ifndef OPTIONSDIALOG
5956 if (!appData.showThinking)
5957 ShowThinkingProc(w,event,prms,nprms);
5960 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5961 AnalysisPeriodicEvent(1);
5964 void TwoMachinesProc(w, event, prms, nprms)
5973 void MatchProc(w, event, prms, nprms)
5982 void IcsClientProc(w, event, prms, nprms)
5991 void EditGameProc(w, event, prms, nprms)
6000 void EditPositionProc(w, event, prms, nprms)
6006 EditPositionEvent();
6009 void TrainingProc(w, event, prms, nprms)
6018 void EditCommentProc(w, event, prms, nprms)
6026 if (PopDown(1)) { // popdown succesful
6028 XtSetArg(args[j], XtNleftBitmap, None); j++;
6029 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6030 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6031 } else // was not up
6035 void IcsInputBoxProc(w, event, prms, nprms)
6041 if (!PopDown(4)) ICSInputBoxPopUp();
6044 void AcceptProc(w, event, prms, nprms)
6053 void DeclineProc(w, event, prms, nprms)
6062 void RematchProc(w, event, prms, nprms)
6071 void CallFlagProc(w, event, prms, nprms)
6080 void DrawProc(w, event, prms, nprms)
6089 void AbortProc(w, event, prms, nprms)
6098 void AdjournProc(w, event, prms, nprms)
6107 void ResignProc(w, event, prms, nprms)
6116 void AdjuWhiteProc(w, event, prms, nprms)
6122 UserAdjudicationEvent(+1);
6125 void AdjuBlackProc(w, event, prms, nprms)
6131 UserAdjudicationEvent(-1);
6134 void AdjuDrawProc(w, event, prms, nprms)
6140 UserAdjudicationEvent(0);
6143 void EnterKeyProc(w, event, prms, nprms)
6149 if (shellUp[4] == True)
6153 void UpKeyProc(w, event, prms, nprms)
6158 { // [HGM] input: let up-arrow recall previous line from history
6165 if (!shellUp[4]) return;
6166 edit = boxOptions[0].handle;
6168 XtSetArg(args[j], XtNstring, &val); j++;
6169 XtGetValues(edit, args, j);
6170 val = PrevInHistory(val);
6171 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6172 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6174 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6175 XawTextReplace(edit, 0, 0, &t);
6176 XawTextSetInsertionPoint(edit, 9999);
6180 void DownKeyProc(w, event, prms, nprms)
6185 { // [HGM] input: let down-arrow recall next line from history
6190 if (!shellUp[4]) return;
6191 edit = boxOptions[0].handle;
6192 val = NextInHistory();
6193 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6194 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6196 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6197 XawTextReplace(edit, 0, 0, &t);
6198 XawTextSetInsertionPoint(edit, 9999);
6202 void StopObservingProc(w, event, prms, nprms)
6208 StopObservingEvent();
6211 void StopExaminingProc(w, event, prms, nprms)
6217 StopExaminingEvent();
6220 void UploadProc(w, event, prms, nprms)
6230 void ForwardProc(w, event, prms, nprms)
6240 void BackwardProc(w, event, prms, nprms)
6249 void TempBackwardProc(w, event, prms, nprms)
6255 if (!TempBackwardActive) {
6256 TempBackwardActive = True;
6261 void TempForwardProc(w, event, prms, nprms)
6267 /* Check to see if triggered by a key release event for a repeating key.
6268 * If so the next queued event will be a key press of the same key at the same time */
6269 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6271 XPeekEvent(xDisplay, &next);
6272 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6273 next.xkey.keycode == event->xkey.keycode)
6277 TempBackwardActive = False;
6280 void ToStartProc(w, event, prms, nprms)
6289 void ToEndProc(w, event, prms, nprms)
6298 void RevertProc(w, event, prms, nprms)
6307 void AnnotateProc(w, event, prms, nprms)
6316 void TruncateGameProc(w, event, prms, nprms)
6322 TruncateGameEvent();
6324 void RetractMoveProc(w, event, prms, nprms)
6333 void MoveNowProc(w, event, prms, nprms)
6342 void FlipViewProc(w, event, prms, nprms)
6348 flipView = !flipView;
6349 DrawPosition(True, NULL);
6352 void PonderNextMoveProc(w, event, prms, nprms)
6360 PonderNextMoveEvent(!appData.ponderNextMove);
6361 #ifndef OPTIONSDIALOG
6362 if (appData.ponderNextMove) {
6363 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6365 XtSetArg(args[0], XtNleftBitmap, None);
6367 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6372 #ifndef OPTIONSDIALOG
6373 void AlwaysQueenProc(w, event, prms, nprms)
6381 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6383 if (appData.alwaysPromoteToQueen) {
6384 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6386 XtSetArg(args[0], XtNleftBitmap, None);
6388 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6392 void AnimateDraggingProc(w, event, prms, nprms)
6400 appData.animateDragging = !appData.animateDragging;
6402 if (appData.animateDragging) {
6403 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6406 XtSetArg(args[0], XtNleftBitmap, None);
6408 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6412 void AnimateMovingProc(w, event, prms, nprms)
6420 appData.animate = !appData.animate;
6422 if (appData.animate) {
6423 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6426 XtSetArg(args[0], XtNleftBitmap, None);
6428 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6432 void AutoflagProc(w, event, prms, nprms)
6440 appData.autoCallFlag = !appData.autoCallFlag;
6442 if (appData.autoCallFlag) {
6443 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6445 XtSetArg(args[0], XtNleftBitmap, None);
6447 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6451 void AutoflipProc(w, event, prms, nprms)
6459 appData.autoFlipView = !appData.autoFlipView;
6461 if (appData.autoFlipView) {
6462 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6464 XtSetArg(args[0], XtNleftBitmap, None);
6466 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6470 void BlindfoldProc(w, event, prms, nprms)
6478 appData.blindfold = !appData.blindfold;
6480 if (appData.blindfold) {
6481 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6483 XtSetArg(args[0], XtNleftBitmap, None);
6485 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6488 DrawPosition(True, NULL);
6491 void TestLegalityProc(w, event, prms, nprms)
6499 appData.testLegality = !appData.testLegality;
6501 if (appData.testLegality) {
6502 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6504 XtSetArg(args[0], XtNleftBitmap, None);
6506 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6511 void FlashMovesProc(w, event, prms, nprms)
6519 if (appData.flashCount == 0) {
6520 appData.flashCount = 3;
6522 appData.flashCount = -appData.flashCount;
6525 if (appData.flashCount > 0) {
6526 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6528 XtSetArg(args[0], XtNleftBitmap, None);
6530 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6535 void HighlightDraggingProc(w, event, prms, nprms)
6543 appData.highlightDragging = !appData.highlightDragging;
6545 if (appData.highlightDragging) {
6546 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6548 XtSetArg(args[0], XtNleftBitmap, None);
6550 XtSetValues(XtNameToWidget(menuBarWidget,
6551 "menuOptions.Highlight Dragging"), args, 1);
6555 void HighlightLastMoveProc(w, event, prms, nprms)
6563 appData.highlightLastMove = !appData.highlightLastMove;
6565 if (appData.highlightLastMove) {
6566 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6568 XtSetArg(args[0], XtNleftBitmap, None);
6570 XtSetValues(XtNameToWidget(menuBarWidget,
6571 "menuOptions.Highlight Last Move"), args, 1);
6574 void HighlightArrowProc(w, event, prms, nprms)
6582 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6584 if (appData.highlightMoveWithArrow) {
6585 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6587 XtSetArg(args[0], XtNleftBitmap, None);
6589 XtSetValues(XtNameToWidget(menuBarWidget,
6590 "menuOptions.Arrow"), args, 1);
6594 void IcsAlarmProc(w, event, prms, nprms)
6602 appData.icsAlarm = !appData.icsAlarm;
6604 if (appData.icsAlarm) {
6605 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6607 XtSetArg(args[0], XtNleftBitmap, None);
6609 XtSetValues(XtNameToWidget(menuBarWidget,
6610 "menuOptions.ICS Alarm"), args, 1);
6614 void MoveSoundProc(w, event, prms, nprms)
6622 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6624 if (appData.ringBellAfterMoves) {
6625 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6627 XtSetArg(args[0], XtNleftBitmap, None);
6629 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6633 void OneClickProc(w, event, prms, nprms)
6641 appData.oneClick = !appData.oneClick;
6643 if (appData.oneClick) {
6644 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6646 XtSetArg(args[0], XtNleftBitmap, None);
6648 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6652 void PeriodicUpdatesProc(w, event, prms, nprms)
6660 PeriodicUpdatesEvent(!appData.periodicUpdates);
6662 if (appData.periodicUpdates) {
6663 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6665 XtSetArg(args[0], XtNleftBitmap, None);
6667 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6671 void PopupExitMessageProc(w, event, prms, nprms)
6679 appData.popupExitMessage = !appData.popupExitMessage;
6681 if (appData.popupExitMessage) {
6682 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6684 XtSetArg(args[0], XtNleftBitmap, None);
6686 XtSetValues(XtNameToWidget(menuBarWidget,
6687 "menuOptions.Popup Exit Message"), args, 1);
6690 void PopupMoveErrorsProc(w, event, prms, nprms)
6698 appData.popupMoveErrors = !appData.popupMoveErrors;
6700 if (appData.popupMoveErrors) {
6701 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6703 XtSetArg(args[0], XtNleftBitmap, None);
6705 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6710 void PremoveProc(w, event, prms, nprms)
6718 appData.premove = !appData.premove;
6720 if (appData.premove) {
6721 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6723 XtSetArg(args[0], XtNleftBitmap, None);
6725 XtSetValues(XtNameToWidget(menuBarWidget,
6726 "menuOptions.Premove"), args, 1);
6730 void ShowCoordsProc(w, event, prms, nprms)
6738 appData.showCoords = !appData.showCoords;
6740 if (appData.showCoords) {
6741 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6743 XtSetArg(args[0], XtNleftBitmap, None);
6745 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6748 DrawPosition(True, NULL);
6751 void ShowThinkingProc(w, event, prms, nprms)
6757 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6758 ShowThinkingEvent();
6761 void HideThinkingProc(w, event, prms, nprms)
6769 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6770 ShowThinkingEvent();
6772 if (appData.hideThinkingFromHuman) {
6773 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6775 XtSetArg(args[0], XtNleftBitmap, None);
6777 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6782 void SaveOnExitProc(w, event, prms, nprms)
6790 saveSettingsOnExit = !saveSettingsOnExit;
6792 if (saveSettingsOnExit) {
6793 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6795 XtSetArg(args[0], XtNleftBitmap, None);
6797 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6801 void SaveSettingsProc(w, event, prms, nprms)
6807 SaveSettings(settingsFileName);
6810 void InfoProc(w, event, prms, nprms)
6817 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6822 void ManProc(w, event, prms, nprms)
6830 if (nprms && *nprms > 0)
6834 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6838 void HintProc(w, event, prms, nprms)
6847 void BookProc(w, event, prms, nprms)
6856 void AboutProc(w, event, prms, nprms)
6864 char *zippy = _(" (with Zippy code)");
6868 snprintf(buf, sizeof(buf),
6870 "Copyright 1991 Digital Equipment Corporation\n"
6871 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6872 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6873 "%s is free software and carries NO WARRANTY;"
6874 "see the file COPYING for more information."),
6875 programVersion, zippy, PACKAGE);
6876 ErrorPopUp(_("About XBoard"), buf, FALSE);
6879 void DebugProc(w, event, prms, nprms)
6885 appData.debugMode = !appData.debugMode;
6888 void AboutGameProc(w, event, prms, nprms)
6897 void NothingProc(w, event, prms, nprms)
6906 void DisplayMessage(message, extMessage)
6907 char *message, *extMessage;
6909 /* display a message in the message widget */
6918 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6923 message = extMessage;
6927 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6929 /* need to test if messageWidget already exists, since this function
6930 can also be called during the startup, if for example a Xresource
6931 is not set up correctly */
6934 XtSetArg(arg, XtNlabel, message);
6935 XtSetValues(messageWidget, &arg, 1);
6941 void DisplayTitle(text)
6946 char title[MSG_SIZ];
6949 if (text == NULL) text = "";
6951 if (appData.titleInWindow) {
6953 XtSetArg(args[i], XtNlabel, text); i++;
6954 XtSetValues(titleWidget, args, i);
6957 if (*text != NULLCHAR) {
6958 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6959 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6960 } else if (appData.icsActive) {
6961 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6962 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6963 } else if (appData.cmailGameName[0] != NULLCHAR) {
6964 snprintf(icon, sizeof(icon), "%s", "CMail");
6965 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6967 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6968 } else if (gameInfo.variant == VariantGothic) {
6969 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6970 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6973 } else if (gameInfo.variant == VariantFalcon) {
6974 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6975 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6977 } else if (appData.noChessProgram) {
6978 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6979 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6981 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6982 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6985 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6986 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6987 XtSetValues(shellWidget, args, i);
6988 XSync(xDisplay, False);
6993 DisplayError(message, error)
7000 if (appData.debugMode || appData.matchMode) {
7001 fprintf(stderr, "%s: %s\n", programName, message);
7004 if (appData.debugMode || appData.matchMode) {
7005 fprintf(stderr, "%s: %s: %s\n",
7006 programName, message, strerror(error));
7008 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7011 ErrorPopUp(_("Error"), message, FALSE);
7015 void DisplayMoveError(message)
7020 DrawPosition(FALSE, NULL);
7021 if (appData.debugMode || appData.matchMode) {
7022 fprintf(stderr, "%s: %s\n", programName, message);
7024 if (appData.popupMoveErrors) {
7025 ErrorPopUp(_("Error"), message, FALSE);
7027 DisplayMessage(message, "");
7032 void DisplayFatalError(message, error, status)
7038 errorExitStatus = status;
7040 fprintf(stderr, "%s: %s\n", programName, message);
7042 fprintf(stderr, "%s: %s: %s\n",
7043 programName, message, strerror(error));
7044 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7047 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7048 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7054 void DisplayInformation(message)
7058 ErrorPopUp(_("Information"), message, TRUE);
7061 void DisplayNote(message)
7065 ErrorPopUp(_("Note"), message, FALSE);
7069 NullXErrorCheck(dpy, error_event)
7071 XErrorEvent *error_event;
7076 void DisplayIcsInteractionTitle(message)
7079 if (oldICSInteractionTitle == NULL) {
7080 /* Magic to find the old window title, adapted from vim */
7081 char *wina = getenv("WINDOWID");
7083 Window win = (Window) atoi(wina);
7084 Window root, parent, *children;
7085 unsigned int nchildren;
7086 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7088 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7089 if (!XQueryTree(xDisplay, win, &root, &parent,
7090 &children, &nchildren)) break;
7091 if (children) XFree((void *)children);
7092 if (parent == root || parent == 0) break;
7095 XSetErrorHandler(oldHandler);
7097 if (oldICSInteractionTitle == NULL) {
7098 oldICSInteractionTitle = "xterm";
7101 printf("\033]0;%s\007", message);
7105 char pendingReplyPrefix[MSG_SIZ];
7106 ProcRef pendingReplyPR;
7108 void AskQuestionProc(w, event, prms, nprms)
7115 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7119 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7122 void AskQuestionPopDown()
7124 if (!askQuestionUp) return;
7125 XtPopdown(askQuestionShell);
7126 XtDestroyWidget(askQuestionShell);
7127 askQuestionUp = False;
7130 void AskQuestionReplyAction(w, event, prms, nprms)
7140 reply = XawDialogGetValueString(w = XtParent(w));
7141 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7142 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7143 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7144 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7145 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7146 AskQuestionPopDown();
7148 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7151 void AskQuestionCallback(w, client_data, call_data)
7153 XtPointer client_data, call_data;
7158 XtSetArg(args[0], XtNlabel, &name);
7159 XtGetValues(w, args, 1);
7161 if (strcmp(name, _("cancel")) == 0) {
7162 AskQuestionPopDown();
7164 AskQuestionReplyAction(w, NULL, NULL, NULL);
7168 void AskQuestion(title, question, replyPrefix, pr)
7169 char *title, *question, *replyPrefix;
7173 Widget popup, layout, dialog, edit;
7179 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7180 pendingReplyPR = pr;
7183 XtSetArg(args[i], XtNresizable, True); i++;
7184 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7185 askQuestionShell = popup =
7186 XtCreatePopupShell(title, transientShellWidgetClass,
7187 shellWidget, args, i);
7190 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7191 layoutArgs, XtNumber(layoutArgs));
7194 XtSetArg(args[i], XtNlabel, question); i++;
7195 XtSetArg(args[i], XtNvalue, ""); i++;
7196 XtSetArg(args[i], XtNborderWidth, 0); i++;
7197 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7200 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7201 (XtPointer) dialog);
7202 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7203 (XtPointer) dialog);
7205 XtRealizeWidget(popup);
7206 CatchDeleteWindow(popup, "AskQuestionPopDown");
7208 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7209 &x, &y, &win_x, &win_y, &mask);
7211 XtSetArg(args[0], XtNx, x - 10);
7212 XtSetArg(args[1], XtNy, y - 30);
7213 XtSetValues(popup, args, 2);
7215 XtPopup(popup, XtGrabExclusive);
7216 askQuestionUp = True;
7218 edit = XtNameToWidget(dialog, "*value");
7219 XtSetKeyboardFocus(popup, edit);
7227 if (*name == NULLCHAR) {
7229 } else if (strcmp(name, "$") == 0) {
7230 putc(BELLCHAR, stderr);
7233 char *prefix = "", *sep = "";
7234 if(appData.soundProgram[0] == NULLCHAR) return;
7235 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7236 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7244 PlaySound(appData.soundMove);
7250 PlaySound(appData.soundIcsWin);
7256 PlaySound(appData.soundIcsLoss);
7262 PlaySound(appData.soundIcsDraw);
7266 PlayIcsUnfinishedSound()
7268 PlaySound(appData.soundIcsUnfinished);
7274 PlaySound(appData.soundIcsAlarm);
7280 PlaySound(appData.soundTell);
7286 system("stty echo");
7293 system("stty -echo");
7298 RunCommand(char *buf)
7304 Colorize(cc, continuation)
7309 int count, outCount, error;
7311 if (textColors[(int)cc].bg > 0) {
7312 if (textColors[(int)cc].fg > 0) {
7313 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7314 textColors[(int)cc].fg, textColors[(int)cc].bg);
7316 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7317 textColors[(int)cc].bg);
7320 if (textColors[(int)cc].fg > 0) {
7321 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7322 textColors[(int)cc].fg);
7324 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7327 count = strlen(buf);
7328 outCount = OutputToProcess(NoProc, buf, count, &error);
7329 if (outCount < count) {
7330 DisplayFatalError(_("Error writing to display"), error, 1);
7333 if (continuation) return;
7336 PlaySound(appData.soundShout);
7339 PlaySound(appData.soundSShout);
7342 PlaySound(appData.soundChannel1);
7345 PlaySound(appData.soundChannel);
7348 PlaySound(appData.soundKibitz);
7351 PlaySound(appData.soundTell);
7353 case ColorChallenge:
7354 PlaySound(appData.soundChallenge);
7357 PlaySound(appData.soundRequest);
7360 PlaySound(appData.soundSeek);
7371 return getpwuid(getuid())->pw_name;
7375 ExpandPathName(path)
7378 static char static_buf[4*MSG_SIZ];
7379 char *d, *s, buf[4*MSG_SIZ];
7385 while (*s && isspace(*s))
7394 if (*(s+1) == '/') {
7395 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7399 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7400 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7401 pwd = getpwnam(buf);
7404 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7408 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7409 strcat(d, strchr(s+1, '/'));
7413 safeStrCpy(d, s, 4*MSG_SIZ );
7420 static char host_name[MSG_SIZ];
7422 #if HAVE_GETHOSTNAME
7423 gethostname(host_name, MSG_SIZ);
7425 #else /* not HAVE_GETHOSTNAME */
7426 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7427 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7429 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7431 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7432 #endif /* not HAVE_GETHOSTNAME */
7435 XtIntervalId delayedEventTimerXID = 0;
7436 DelayedEventCallback delayedEventCallback = 0;
7441 delayedEventTimerXID = 0;
7442 delayedEventCallback();
7446 ScheduleDelayedEvent(cb, millisec)
7447 DelayedEventCallback cb; long millisec;
7449 if(delayedEventTimerXID && delayedEventCallback == cb)
7450 // [HGM] alive: replace, rather than add or flush identical event
7451 XtRemoveTimeOut(delayedEventTimerXID);
7452 delayedEventCallback = cb;
7453 delayedEventTimerXID =
7454 XtAppAddTimeOut(appContext, millisec,
7455 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7458 DelayedEventCallback
7461 if (delayedEventTimerXID) {
7462 return delayedEventCallback;
7469 CancelDelayedEvent()
7471 if (delayedEventTimerXID) {
7472 XtRemoveTimeOut(delayedEventTimerXID);
7473 delayedEventTimerXID = 0;
7477 XtIntervalId loadGameTimerXID = 0;
7479 int LoadGameTimerRunning()
7481 return loadGameTimerXID != 0;
7484 int StopLoadGameTimer()
7486 if (loadGameTimerXID != 0) {
7487 XtRemoveTimeOut(loadGameTimerXID);
7488 loadGameTimerXID = 0;
7496 LoadGameTimerCallback(arg, id)
7500 loadGameTimerXID = 0;
7505 StartLoadGameTimer(millisec)
7509 XtAppAddTimeOut(appContext, millisec,
7510 (XtTimerCallbackProc) LoadGameTimerCallback,
7514 XtIntervalId analysisClockXID = 0;
7517 AnalysisClockCallback(arg, id)
7521 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7522 || appData.icsEngineAnalyze) { // [DM]
7523 AnalysisPeriodicEvent(0);
7524 StartAnalysisClock();
7529 StartAnalysisClock()
7532 XtAppAddTimeOut(appContext, 2000,
7533 (XtTimerCallbackProc) AnalysisClockCallback,
7537 XtIntervalId clockTimerXID = 0;
7539 int ClockTimerRunning()
7541 return clockTimerXID != 0;
7544 int StopClockTimer()
7546 if (clockTimerXID != 0) {
7547 XtRemoveTimeOut(clockTimerXID);
7556 ClockTimerCallback(arg, id)
7565 StartClockTimer(millisec)
7569 XtAppAddTimeOut(appContext, millisec,
7570 (XtTimerCallbackProc) ClockTimerCallback,
7575 DisplayTimerLabel(w, color, timer, highlight)
7584 /* check for low time warning */
7585 Pixel foregroundOrWarningColor = timerForegroundPixel;
7588 appData.lowTimeWarning &&
7589 (timer / 1000) < appData.icsAlarmTime)
7590 foregroundOrWarningColor = lowTimeWarningColor;
7592 if (appData.clockMode) {
7593 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7594 XtSetArg(args[0], XtNlabel, buf);
7596 snprintf(buf, MSG_SIZ, "%s ", color);
7597 XtSetArg(args[0], XtNlabel, buf);
7602 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7603 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7605 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7606 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7609 XtSetValues(w, args, 3);
7613 DisplayWhiteClock(timeRemaining, highlight)
7619 if(appData.noGUI) return;
7620 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7621 if (highlight && iconPixmap == bIconPixmap) {
7622 iconPixmap = wIconPixmap;
7623 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7624 XtSetValues(shellWidget, args, 1);
7629 DisplayBlackClock(timeRemaining, highlight)
7635 if(appData.noGUI) return;
7636 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7637 if (highlight && iconPixmap == wIconPixmap) {
7638 iconPixmap = bIconPixmap;
7639 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7640 XtSetValues(shellWidget, args, 1);
7658 int StartChildProcess(cmdLine, dir, pr)
7665 int to_prog[2], from_prog[2];
7669 if (appData.debugMode) {
7670 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7673 /* We do NOT feed the cmdLine to the shell; we just
7674 parse it into blank-separated arguments in the
7675 most simple-minded way possible.
7678 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7681 while(*p == ' ') p++;
7683 if(*p == '"' || *p == '\'')
7684 p = strchr(++argv[i-1], *p);
7685 else p = strchr(p, ' ');
7686 if (p == NULL) break;
7691 SetUpChildIO(to_prog, from_prog);
7693 if ((pid = fork()) == 0) {
7695 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7696 close(to_prog[1]); // first close the unused pipe ends
7697 close(from_prog[0]);
7698 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7699 dup2(from_prog[1], 1);
7700 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7701 close(from_prog[1]); // and closing again loses one of the pipes!
7702 if(fileno(stderr) >= 2) // better safe than sorry...
7703 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7705 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7710 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7712 execvp(argv[0], argv);
7714 /* If we get here, exec failed */
7719 /* Parent process */
7721 close(from_prog[1]);
7723 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7726 cp->fdFrom = from_prog[0];
7727 cp->fdTo = to_prog[1];
7732 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7733 static RETSIGTYPE AlarmCallBack(int n)
7739 DestroyChildProcess(pr, signalType)
7743 ChildProc *cp = (ChildProc *) pr;
7745 if (cp->kind != CPReal) return;
7747 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7748 signal(SIGALRM, AlarmCallBack);
7750 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7751 kill(cp->pid, SIGKILL); // kill it forcefully
7752 wait((int *) 0); // and wait again
7756 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7758 /* Process is exiting either because of the kill or because of
7759 a quit command sent by the backend; either way, wait for it to die.
7768 InterruptChildProcess(pr)
7771 ChildProc *cp = (ChildProc *) pr;
7773 if (cp->kind != CPReal) return;
7774 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7777 int OpenTelnet(host, port, pr)
7782 char cmdLine[MSG_SIZ];
7784 if (port[0] == NULLCHAR) {
7785 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7787 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7789 return StartChildProcess(cmdLine, "", pr);
7792 int OpenTCP(host, port, pr)
7798 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7799 #else /* !OMIT_SOCKETS */
7800 struct addrinfo hints;
7801 struct addrinfo *ais, *ai;
7806 memset(&hints, 0, sizeof(hints));
7807 hints.ai_family = AF_UNSPEC;
7808 hints.ai_socktype = SOCK_STREAM;
7810 error = getaddrinfo(host, port, &hints, &ais);
7812 /* a getaddrinfo error is not an errno, so can't return it */
7813 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7814 host, port, gai_strerror(error));
7818 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7819 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7823 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7836 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7842 #endif /* !OMIT_SOCKETS */
7847 int OpenCommPort(name, pr)
7854 fd = open(name, 2, 0);
7855 if (fd < 0) return errno;
7857 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7867 int OpenLoopback(pr)
7873 SetUpChildIO(to, from);
7875 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7878 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7885 int OpenRcmd(host, user, cmd, pr)
7886 char *host, *user, *cmd;
7889 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7893 #define INPUT_SOURCE_BUF_SIZE 8192
7902 char buf[INPUT_SOURCE_BUF_SIZE];
7907 DoInputCallback(closure, source, xid)
7912 InputSource *is = (InputSource *) closure;
7917 if (is->lineByLine) {
7918 count = read(is->fd, is->unused,
7919 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7921 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7924 is->unused += count;
7926 while (p < is->unused) {
7927 q = memchr(p, '\n', is->unused - p);
7928 if (q == NULL) break;
7930 (is->func)(is, is->closure, p, q - p, 0);
7934 while (p < is->unused) {
7939 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7944 (is->func)(is, is->closure, is->buf, count, error);
7948 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7955 ChildProc *cp = (ChildProc *) pr;
7957 is = (InputSource *) calloc(1, sizeof(InputSource));
7958 is->lineByLine = lineByLine;
7962 is->fd = fileno(stdin);
7964 is->kind = cp->kind;
7965 is->fd = cp->fdFrom;
7968 is->unused = is->buf;
7971 is->xid = XtAppAddInput(appContext, is->fd,
7972 (XtPointer) (XtInputReadMask),
7973 (XtInputCallbackProc) DoInputCallback,
7975 is->closure = closure;
7976 return (InputSourceRef) is;
7980 RemoveInputSource(isr)
7983 InputSource *is = (InputSource *) isr;
7985 if (is->xid == 0) return;
7986 XtRemoveInput(is->xid);
7990 int OutputToProcess(pr, message, count, outError)
7996 static int line = 0;
7997 ChildProc *cp = (ChildProc *) pr;
8002 if (appData.noJoin || !appData.useInternalWrap)
8003 outCount = fwrite(message, 1, count, stdout);
8006 int width = get_term_width();
8007 int len = wrap(NULL, message, count, width, &line);
8008 char *msg = malloc(len);
8012 outCount = fwrite(message, 1, count, stdout);
8015 dbgchk = wrap(msg, message, count, width, &line);
8016 if (dbgchk != len && appData.debugMode)
8017 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8018 outCount = fwrite(msg, 1, dbgchk, stdout);
8024 outCount = write(cp->fdTo, message, count);
8034 /* Output message to process, with "ms" milliseconds of delay
8035 between each character. This is needed when sending the logon
8036 script to ICC, which for some reason doesn't like the
8037 instantaneous send. */
8038 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8045 ChildProc *cp = (ChildProc *) pr;
8050 r = write(cp->fdTo, message++, 1);
8063 /**** Animation code by Hugh Fisher, DCS, ANU.
8065 Known problem: if a window overlapping the board is
8066 moved away while a piece is being animated underneath,
8067 the newly exposed area won't be updated properly.
8068 I can live with this.
8070 Known problem: if you look carefully at the animation
8071 of pieces in mono mode, they are being drawn as solid
8072 shapes without interior detail while moving. Fixing
8073 this would be a major complication for minimal return.
8076 /* Masks for XPM pieces. Black and white pieces can have
8077 different shapes, but in the interest of retaining my
8078 sanity pieces must have the same outline on both light
8079 and dark squares, and all pieces must use the same
8080 background square colors/images. */
8082 static int xpmDone = 0;
8085 CreateAnimMasks (pieceDepth)
8092 unsigned long plane;
8095 /* Need a bitmap just to get a GC with right depth */
8096 buf = XCreatePixmap(xDisplay, xBoardWindow,
8098 values.foreground = 1;
8099 values.background = 0;
8100 /* Don't use XtGetGC, not read only */
8101 maskGC = XCreateGC(xDisplay, buf,
8102 GCForeground | GCBackground, &values);
8103 XFreePixmap(xDisplay, buf);
8105 buf = XCreatePixmap(xDisplay, xBoardWindow,
8106 squareSize, squareSize, pieceDepth);
8107 values.foreground = XBlackPixel(xDisplay, xScreen);
8108 values.background = XWhitePixel(xDisplay, xScreen);
8109 bufGC = XCreateGC(xDisplay, buf,
8110 GCForeground | GCBackground, &values);
8112 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8113 /* Begin with empty mask */
8114 if(!xpmDone) // [HGM] pieces: keep using existing
8115 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8116 squareSize, squareSize, 1);
8117 XSetFunction(xDisplay, maskGC, GXclear);
8118 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8119 0, 0, squareSize, squareSize);
8121 /* Take a copy of the piece */
8126 XSetFunction(xDisplay, bufGC, GXcopy);
8127 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8129 0, 0, squareSize, squareSize, 0, 0);
8131 /* XOR the background (light) over the piece */
8132 XSetFunction(xDisplay, bufGC, GXxor);
8134 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8135 0, 0, squareSize, squareSize, 0, 0);
8137 XSetForeground(xDisplay, bufGC, lightSquareColor);
8138 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8141 /* We now have an inverted piece image with the background
8142 erased. Construct mask by just selecting all the non-zero
8143 pixels - no need to reconstruct the original image. */
8144 XSetFunction(xDisplay, maskGC, GXor);
8146 /* Might be quicker to download an XImage and create bitmap
8147 data from it rather than this N copies per piece, but it
8148 only takes a fraction of a second and there is a much
8149 longer delay for loading the pieces. */
8150 for (n = 0; n < pieceDepth; n ++) {
8151 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8152 0, 0, squareSize, squareSize,
8158 XFreePixmap(xDisplay, buf);
8159 XFreeGC(xDisplay, bufGC);
8160 XFreeGC(xDisplay, maskGC);
8164 InitAnimState (anim, info)
8166 XWindowAttributes * info;
8171 /* Each buffer is square size, same depth as window */
8172 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8173 squareSize, squareSize, info->depth);
8174 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8175 squareSize, squareSize, info->depth);
8177 /* Create a plain GC for blitting */
8178 mask = GCForeground | GCBackground | GCFunction |
8179 GCPlaneMask | GCGraphicsExposures;
8180 values.foreground = XBlackPixel(xDisplay, xScreen);
8181 values.background = XWhitePixel(xDisplay, xScreen);
8182 values.function = GXcopy;
8183 values.plane_mask = AllPlanes;
8184 values.graphics_exposures = False;
8185 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8187 /* Piece will be copied from an existing context at
8188 the start of each new animation/drag. */
8189 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8191 /* Outline will be a read-only copy of an existing */
8192 anim->outlineGC = None;
8198 XWindowAttributes info;
8200 if (xpmDone && gameInfo.variant == oldVariant) return;
8201 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8202 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8204 InitAnimState(&game, &info);
8205 InitAnimState(&player, &info);
8207 /* For XPM pieces, we need bitmaps to use as masks. */
8209 CreateAnimMasks(info.depth), xpmDone = 1;
8214 static Boolean frameWaiting;
8216 static RETSIGTYPE FrameAlarm (sig)
8219 frameWaiting = False;
8220 /* In case System-V style signals. Needed?? */
8221 signal(SIGALRM, FrameAlarm);
8228 struct itimerval delay;
8230 XSync(xDisplay, False);
8233 frameWaiting = True;
8234 signal(SIGALRM, FrameAlarm);
8235 delay.it_interval.tv_sec =
8236 delay.it_value.tv_sec = time / 1000;
8237 delay.it_interval.tv_usec =
8238 delay.it_value.tv_usec = (time % 1000) * 1000;
8239 setitimer(ITIMER_REAL, &delay, NULL);
8240 while (frameWaiting) pause();
8241 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8242 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8243 setitimer(ITIMER_REAL, &delay, NULL);
8253 XSync(xDisplay, False);
8255 usleep(time * 1000);
8266 /* Convert board position to corner of screen rect and color */
8269 ScreenSquare(column, row, pt, color)
8270 int column; int row; XPoint * pt; int * color;
8273 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8274 pt->y = lineGap + row * (squareSize + lineGap);
8276 pt->x = lineGap + column * (squareSize + lineGap);
8277 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8279 *color = SquareColor(row, column);
8282 /* Convert window coords to square */
8285 BoardSquare(x, y, column, row)
8286 int x; int y; int * column; int * row;
8288 *column = EventToSquare(x, BOARD_WIDTH);
8289 if (flipView && *column >= 0)
8290 *column = BOARD_WIDTH - 1 - *column;
8291 *row = EventToSquare(y, BOARD_HEIGHT);
8292 if (!flipView && *row >= 0)
8293 *row = BOARD_HEIGHT - 1 - *row;
8298 #undef Max /* just in case */
8300 #define Max(a, b) ((a) > (b) ? (a) : (b))
8301 #define Min(a, b) ((a) < (b) ? (a) : (b))
8304 SetRect(rect, x, y, width, height)
8305 XRectangle * rect; int x; int y; int width; int height;
8309 rect->width = width;
8310 rect->height = height;
8313 /* Test if two frames overlap. If they do, return
8314 intersection rect within old and location of
8315 that rect within new. */
8318 Intersect(old, new, size, area, pt)
8319 XPoint * old; XPoint * new;
8320 int size; XRectangle * area; XPoint * pt;
8322 if (old->x > new->x + size || new->x > old->x + size ||
8323 old->y > new->y + size || new->y > old->y + size) {
8326 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8327 size - abs(old->x - new->x), size - abs(old->y - new->y));
8328 pt->x = Max(old->x - new->x, 0);
8329 pt->y = Max(old->y - new->y, 0);
8334 /* For two overlapping frames, return the rect(s)
8335 in the old that do not intersect with the new. */
8338 CalcUpdateRects(old, new, size, update, nUpdates)
8339 XPoint * old; XPoint * new; int size;
8340 XRectangle update[]; int * nUpdates;
8344 /* If old = new (shouldn't happen) then nothing to draw */
8345 if (old->x == new->x && old->y == new->y) {
8349 /* Work out what bits overlap. Since we know the rects
8350 are the same size we don't need a full intersect calc. */
8352 /* Top or bottom edge? */
8353 if (new->y > old->y) {
8354 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8356 } else if (old->y > new->y) {
8357 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8358 size, old->y - new->y);
8361 /* Left or right edge - don't overlap any update calculated above. */
8362 if (new->x > old->x) {
8363 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8364 new->x - old->x, size - abs(new->y - old->y));
8366 } else if (old->x > new->x) {
8367 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8368 old->x - new->x, size - abs(new->y - old->y));
8375 /* Generate a series of frame coords from start->mid->finish.
8376 The movement rate doubles until the half way point is
8377 reached, then halves back down to the final destination,
8378 which gives a nice slow in/out effect. The algorithmn
8379 may seem to generate too many intermediates for short
8380 moves, but remember that the purpose is to attract the
8381 viewers attention to the piece about to be moved and
8382 then to where it ends up. Too few frames would be less
8386 Tween(start, mid, finish, factor, frames, nFrames)
8387 XPoint * start; XPoint * mid;
8388 XPoint * finish; int factor;
8389 XPoint frames[]; int * nFrames;
8391 int fraction, n, count;
8395 /* Slow in, stepping 1/16th, then 1/8th, ... */
8397 for (n = 0; n < factor; n++)
8399 for (n = 0; n < factor; n++) {
8400 frames[count].x = start->x + (mid->x - start->x) / fraction;
8401 frames[count].y = start->y + (mid->y - start->y) / fraction;
8403 fraction = fraction / 2;
8407 frames[count] = *mid;
8410 /* Slow out, stepping 1/2, then 1/4, ... */
8412 for (n = 0; n < factor; n++) {
8413 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8414 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8416 fraction = fraction * 2;
8421 /* Draw a piece on the screen without disturbing what's there */
8424 SelectGCMask(piece, clip, outline, mask)
8425 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8429 /* Bitmap for piece being moved. */
8430 if (appData.monoMode) {
8431 *mask = *pieceToSolid(piece);
8432 } else if (useImages) {
8434 *mask = xpmMask[piece];
8436 *mask = ximMaskPm[piece];
8439 *mask = *pieceToSolid(piece);
8442 /* GC for piece being moved. Square color doesn't matter, but
8443 since it gets modified we make a copy of the original. */
8445 if (appData.monoMode)
8450 if (appData.monoMode)
8455 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8457 /* Outline only used in mono mode and is not modified */
8459 *outline = bwPieceGC;
8461 *outline = wbPieceGC;
8465 OverlayPiece(piece, clip, outline, dest)
8466 ChessSquare piece; GC clip; GC outline; Drawable dest;
8471 /* Draw solid rectangle which will be clipped to shape of piece */
8472 XFillRectangle(xDisplay, dest, clip,
8473 0, 0, squareSize, squareSize);
8474 if (appData.monoMode)
8475 /* Also draw outline in contrasting color for black
8476 on black / white on white cases */
8477 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8478 0, 0, squareSize, squareSize, 0, 0, 1);
8480 /* Copy the piece */
8485 if(appData.upsideDown && flipView) kind ^= 2;
8486 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8488 0, 0, squareSize, squareSize,
8493 /* Animate the movement of a single piece */
8496 BeginAnimation(anim, piece, startColor, start)
8504 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8505 /* The old buffer is initialised with the start square (empty) */
8506 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8507 anim->prevFrame = *start;
8509 /* The piece will be drawn using its own bitmap as a matte */
8510 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8511 XSetClipMask(xDisplay, anim->pieceGC, mask);
8515 AnimationFrame(anim, frame, piece)
8520 XRectangle updates[4];
8525 /* Save what we are about to draw into the new buffer */
8526 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8527 frame->x, frame->y, squareSize, squareSize,
8530 /* Erase bits of the previous frame */
8531 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8532 /* Where the new frame overlapped the previous,
8533 the contents in newBuf are wrong. */
8534 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8535 overlap.x, overlap.y,
8536 overlap.width, overlap.height,
8538 /* Repaint the areas in the old that don't overlap new */
8539 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8540 for (i = 0; i < count; i++)
8541 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8542 updates[i].x - anim->prevFrame.x,
8543 updates[i].y - anim->prevFrame.y,
8544 updates[i].width, updates[i].height,
8545 updates[i].x, updates[i].y);
8547 /* Easy when no overlap */
8548 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8549 0, 0, squareSize, squareSize,
8550 anim->prevFrame.x, anim->prevFrame.y);
8553 /* Save this frame for next time round */
8554 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8555 0, 0, squareSize, squareSize,
8557 anim->prevFrame = *frame;
8559 /* Draw piece over original screen contents, not current,
8560 and copy entire rect. Wipes out overlapping piece images. */
8561 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8562 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8563 0, 0, squareSize, squareSize,
8564 frame->x, frame->y);
8568 EndAnimation (anim, finish)
8572 XRectangle updates[4];
8577 /* The main code will redraw the final square, so we
8578 only need to erase the bits that don't overlap. */
8579 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8580 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8581 for (i = 0; i < count; i++)
8582 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8583 updates[i].x - anim->prevFrame.x,
8584 updates[i].y - anim->prevFrame.y,
8585 updates[i].width, updates[i].height,
8586 updates[i].x, updates[i].y);
8588 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8589 0, 0, squareSize, squareSize,
8590 anim->prevFrame.x, anim->prevFrame.y);
8595 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8597 ChessSquare piece; int startColor;
8598 XPoint * start; XPoint * finish;
8599 XPoint frames[]; int nFrames;
8603 BeginAnimation(anim, piece, startColor, start);
8604 for (n = 0; n < nFrames; n++) {
8605 AnimationFrame(anim, &(frames[n]), piece);
8606 FrameDelay(appData.animSpeed);
8608 EndAnimation(anim, finish);
8612 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8615 ChessSquare piece = board[fromY][toY];
8616 board[fromY][toY] = EmptySquare;
8617 DrawPosition(FALSE, board);
8619 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8620 y = lineGap + toY * (squareSize + lineGap);
8622 x = lineGap + toX * (squareSize + lineGap);
8623 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8625 for(i=1; i<4*kFactor; i++) {
8626 int r = squareSize * 9 * i/(20*kFactor - 5);
8627 XFillArc(xDisplay, xBoardWindow, highlineGC,
8628 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8629 FrameDelay(appData.animSpeed);
8631 board[fromY][toY] = piece;
8634 /* Main control logic for deciding what to animate and how */
8637 AnimateMove(board, fromX, fromY, toX, toY)
8646 XPoint start, finish, mid;
8647 XPoint frames[kFactor * 2 + 1];
8648 int nFrames, startColor, endColor;
8650 /* Are we animating? */
8651 if (!appData.animate || appData.blindfold)
8654 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8655 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8656 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8658 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8659 piece = board[fromY][fromX];
8660 if (piece >= EmptySquare) return;
8665 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8668 if (appData.debugMode) {
8669 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8670 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8671 piece, fromX, fromY, toX, toY); }
8673 ScreenSquare(fromX, fromY, &start, &startColor);
8674 ScreenSquare(toX, toY, &finish, &endColor);
8677 /* Knight: make straight movement then diagonal */
8678 if (abs(toY - fromY) < abs(toX - fromX)) {
8679 mid.x = start.x + (finish.x - start.x) / 2;
8683 mid.y = start.y + (finish.y - start.y) / 2;
8686 mid.x = start.x + (finish.x - start.x) / 2;
8687 mid.y = start.y + (finish.y - start.y) / 2;
8690 /* Don't use as many frames for very short moves */
8691 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8692 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8694 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8695 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8696 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8698 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8699 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8702 /* Be sure end square is redrawn */
8703 damage[0][toY][toX] = True;
8707 DragPieceBegin(x, y, instantly)
8708 int x; int y; Boolean instantly;
8710 int boardX, boardY, color;
8713 /* Are we animating? */
8714 if (!appData.animateDragging || appData.blindfold)
8717 /* Figure out which square we start in and the
8718 mouse position relative to top left corner. */
8719 BoardSquare(x, y, &boardX, &boardY);
8720 player.startBoardX = boardX;
8721 player.startBoardY = boardY;
8722 ScreenSquare(boardX, boardY, &corner, &color);
8723 player.startSquare = corner;
8724 player.startColor = color;
8725 /* As soon as we start dragging, the piece will jump slightly to
8726 be centered over the mouse pointer. */
8727 player.mouseDelta.x = squareSize/2;
8728 player.mouseDelta.y = squareSize/2;
8729 /* Initialise animation */
8730 player.dragPiece = PieceForSquare(boardX, boardY);
8732 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8733 player.dragActive = True;
8734 BeginAnimation(&player, player.dragPiece, color, &corner);
8735 /* Mark this square as needing to be redrawn. Note that
8736 we don't remove the piece though, since logically (ie
8737 as seen by opponent) the move hasn't been made yet. */
8738 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8739 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8740 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8741 corner.x, corner.y, squareSize, squareSize,
8742 0, 0); // [HGM] zh: unstack in stead of grab
8743 if(gatingPiece != EmptySquare) {
8744 /* Kludge alert: When gating we want the introduced
8745 piece to appear on the from square. To generate an
8746 image of it, we draw it on the board, copy the image,
8747 and draw the original piece again. */
8748 ChessSquare piece = boards[currentMove][boardY][boardX];
8749 DrawSquare(boardY, boardX, gatingPiece, 0);
8750 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8751 corner.x, corner.y, squareSize, squareSize, 0, 0);
8752 DrawSquare(boardY, boardX, piece, 0);
8754 damage[0][boardY][boardX] = True;
8756 player.dragActive = False;
8761 ChangeDragPiece(ChessSquare piece)
8764 player.dragPiece = piece;
8765 /* The piece will be drawn using its own bitmap as a matte */
8766 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8767 XSetClipMask(xDisplay, player.pieceGC, mask);
8776 /* Are we animating? */
8777 if (!appData.animateDragging || appData.blindfold)
8781 if (! player.dragActive)
8783 /* Move piece, maintaining same relative position
8784 of mouse within square */
8785 corner.x = x - player.mouseDelta.x;
8786 corner.y = y - player.mouseDelta.y;
8787 AnimationFrame(&player, &corner, player.dragPiece);
8789 if (appData.highlightDragging) {
8791 BoardSquare(x, y, &boardX, &boardY);
8792 SetHighlights(fromX, fromY, boardX, boardY);
8801 int boardX, boardY, color;
8804 /* Are we animating? */
8805 if (!appData.animateDragging || appData.blindfold)
8809 if (! player.dragActive)
8811 /* Last frame in sequence is square piece is
8812 placed on, which may not match mouse exactly. */
8813 BoardSquare(x, y, &boardX, &boardY);
8814 ScreenSquare(boardX, boardY, &corner, &color);
8815 EndAnimation(&player, &corner);
8817 /* Be sure end square is redrawn */
8818 damage[0][boardY][boardX] = True;
8820 /* This prevents weird things happening with fast successive
8821 clicks which on my Sun at least can cause motion events
8822 without corresponding press/release. */
8823 player.dragActive = False;
8826 /* Handle expose event while piece being dragged */
8831 if (!player.dragActive || appData.blindfold)
8834 /* What we're doing: logically, the move hasn't been made yet,
8835 so the piece is still in it's original square. But visually
8836 it's being dragged around the board. So we erase the square
8837 that the piece is on and draw it at the last known drag point. */
8838 BlankSquare(player.startSquare.x, player.startSquare.y,
8839 player.startColor, EmptySquare, xBoardWindow, 1);
8840 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8841 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8844 #include <sys/ioctl.h>
8845 int get_term_width()
8847 int fd, default_width;
8850 default_width = 79; // this is FICS default anyway...
8852 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8854 if (!ioctl(fd, TIOCGSIZE, &win))
8855 default_width = win.ts_cols;
8856 #elif defined(TIOCGWINSZ)
8858 if (!ioctl(fd, TIOCGWINSZ, &win))
8859 default_width = win.ws_col;
8861 return default_width;
8867 static int old_width = 0;
8868 int new_width = get_term_width();
8870 if (old_width != new_width)
8871 ics_printf("set width %d\n", new_width);
8872 old_width = new_width;
8875 void NotifyFrontendLogin()
8880 /* [AS] Arrow highlighting support */
8882 static double A_WIDTH = 5; /* Width of arrow body */
8884 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8885 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8887 static double Sqr( double x )
8892 static int Round( double x )
8894 return (int) (x + 0.5);
8897 void SquareToPos(int rank, int file, int *x, int *y)
8900 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8901 *y = lineGap + rank * (squareSize + lineGap);
8903 *x = lineGap + file * (squareSize + lineGap);
8904 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8908 /* Draw an arrow between two points using current settings */
8909 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8912 double dx, dy, j, k, x, y;
8915 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8917 arrow[0].x = s_x + A_WIDTH + 0.5;
8920 arrow[1].x = s_x + A_WIDTH + 0.5;
8921 arrow[1].y = d_y - h;
8923 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8924 arrow[2].y = d_y - h;
8929 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8930 arrow[5].y = d_y - h;
8932 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8933 arrow[4].y = d_y - h;
8935 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8938 else if( d_y == s_y ) {
8939 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8942 arrow[0].y = s_y + A_WIDTH + 0.5;
8944 arrow[1].x = d_x - w;
8945 arrow[1].y = s_y + A_WIDTH + 0.5;
8947 arrow[2].x = d_x - w;
8948 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8953 arrow[5].x = d_x - w;
8954 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8956 arrow[4].x = d_x - w;
8957 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8960 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8963 /* [AS] Needed a lot of paper for this! :-) */
8964 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8965 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8967 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8969 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8974 arrow[0].x = Round(x - j);
8975 arrow[0].y = Round(y + j*dx);
8977 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8978 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8981 x = (double) d_x - k;
8982 y = (double) d_y - k*dy;
8985 x = (double) d_x + k;
8986 y = (double) d_y + k*dy;
8989 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8991 arrow[6].x = Round(x - j);
8992 arrow[6].y = Round(y + j*dx);
8994 arrow[2].x = Round(arrow[6].x + 2*j);
8995 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8997 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8998 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
9003 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9004 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9007 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9008 // Polygon( hdc, arrow, 7 );
9011 /* [AS] Draw an arrow between two squares */
9012 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9014 int s_x, s_y, d_x, d_y, hor, vert, i;
9016 if( s_col == d_col && s_row == d_row ) {
9020 /* Get source and destination points */
9021 SquareToPos( s_row, s_col, &s_x, &s_y);
9022 SquareToPos( d_row, d_col, &d_x, &d_y);
9025 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9027 else if( d_y < s_y ) {
9028 d_y += squareSize / 2 + squareSize / 4;
9031 d_y += squareSize / 2;
9035 d_x += squareSize / 2 - squareSize / 4;
9037 else if( d_x < s_x ) {
9038 d_x += squareSize / 2 + squareSize / 4;
9041 d_x += squareSize / 2;
9044 s_x += squareSize / 2;
9045 s_y += squareSize / 2;
9048 A_WIDTH = squareSize / 14.; //[HGM] make float
9050 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9052 hor = 64*s_col + 32; vert = 64*s_row + 32;
9053 for(i=0; i<= 64; i++) {
9054 damage[0][vert+6>>6][hor+6>>6] = True;
9055 damage[0][vert-6>>6][hor+6>>6] = True;
9056 damage[0][vert+6>>6][hor-6>>6] = True;
9057 damage[0][vert-6>>6][hor-6>>6] = True;
9058 hor += d_col - s_col; vert += d_row - s_row;
9062 Boolean IsDrawArrowEnabled()
9064 return appData.highlightMoveWithArrow && squareSize >= 32;
9067 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9069 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9070 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9073 void UpdateLogos(int displ)
9075 return; // no logos in XBoard yet