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;
5127 char *PromoChars = "wglcqrbnkac+=\0";
5130 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5131 XtGetValues(boardWidget, args, j);
5134 XtSetArg(args[j], XtNresizable, True); j++;
5135 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5137 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5138 shellWidget, args, j);
5140 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5141 layoutArgs, XtNumber(layoutArgs));
5144 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5145 XtSetArg(args[j], XtNborderWidth, 0); j++;
5146 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5149 if(gameInfo.variant != VariantShogi) {
5150 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5151 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5152 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5153 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5154 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5156 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5157 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5158 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5159 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5161 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5162 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5163 gameInfo.variant == VariantGiveaway) {
5164 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5166 if(gameInfo.variant == VariantCapablanca ||
5167 gameInfo.variant == VariantGothic ||
5168 gameInfo.variant == VariantCapaRandom) {
5169 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5170 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5172 } else // [HGM] shogi
5174 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5175 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5177 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5179 XtRealizeWidget(promotionShell);
5180 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5183 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5184 XtGetValues(promotionShell, args, j);
5186 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5187 lineGap + squareSize/3 +
5188 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5189 0 : 6*(squareSize + lineGap)), &x, &y);
5192 XtSetArg(args[j], XtNx, x); j++;
5193 XtSetArg(args[j], XtNy, y); j++;
5194 XtSetValues(promotionShell, args, j);
5196 XtPopup(promotionShell, XtGrabNone);
5201 void PromotionPopDown()
5203 if (!promotionUp) return;
5204 XtPopdown(promotionShell);
5205 XtDestroyWidget(promotionShell);
5206 promotionUp = False;
5209 void PromotionCallback(w, client_data, call_data)
5211 XtPointer client_data, call_data;
5213 int promoChar = * (const char *) client_data;
5217 if (fromX == -1) return;
5224 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5226 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5227 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5232 void ErrorCallback(w, client_data, call_data)
5234 XtPointer client_data, call_data;
5237 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5239 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5245 if (!errorUp) return;
5247 XtPopdown(errorShell);
5248 XtDestroyWidget(errorShell);
5249 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5252 void ErrorPopUp(title, label, modal)
5253 char *title, *label;
5257 Widget dialog, layout;
5261 Dimension bw_width, pw_width;
5262 Dimension pw_height;
5266 XtSetArg(args[i], XtNresizable, True); i++;
5267 XtSetArg(args[i], XtNtitle, title); i++;
5269 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5270 shellWidget, args, i);
5272 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5273 layoutArgs, XtNumber(layoutArgs));
5276 XtSetArg(args[i], XtNlabel, label); i++;
5277 XtSetArg(args[i], XtNborderWidth, 0); i++;
5278 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5281 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5283 XtRealizeWidget(errorShell);
5284 CatchDeleteWindow(errorShell, "ErrorPopDown");
5287 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5288 XtGetValues(boardWidget, args, i);
5290 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5291 XtSetArg(args[i], XtNheight, &pw_height); i++;
5292 XtGetValues(errorShell, args, i);
5295 /* This code seems to tickle an X bug if it is executed too soon
5296 after xboard starts up. The coordinates get transformed as if
5297 the main window was positioned at (0, 0).
5299 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5300 0 - pw_height + squareSize / 3, &x, &y);
5302 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5303 RootWindowOfScreen(XtScreen(boardWidget)),
5304 (bw_width - pw_width) / 2,
5305 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5309 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5312 XtSetArg(args[i], XtNx, x); i++;
5313 XtSetArg(args[i], XtNy, y); i++;
5314 XtSetValues(errorShell, args, i);
5317 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5320 /* Disable all user input other than deleting the window */
5321 static int frozen = 0;
5325 /* Grab by a widget that doesn't accept input */
5326 XtAddGrab(messageWidget, TRUE, FALSE);
5330 /* Undo a FreezeUI */
5333 if (!frozen) return;
5334 XtRemoveGrab(messageWidget);
5338 char *ModeToWidgetName(mode)
5342 case BeginningOfGame:
5343 if (appData.icsActive)
5344 return "menuMode.ICS Client";
5345 else if (appData.noChessProgram ||
5346 *appData.cmailGameName != NULLCHAR)
5347 return "menuMode.Edit Game";
5349 return "menuMode.Machine Black";
5350 case MachinePlaysBlack:
5351 return "menuMode.Machine Black";
5352 case MachinePlaysWhite:
5353 return "menuMode.Machine White";
5355 return "menuMode.Analysis Mode";
5357 return "menuMode.Analyze File";
5358 case TwoMachinesPlay:
5359 return "menuMode.Two Machines";
5361 return "menuMode.Edit Game";
5362 case PlayFromGameFile:
5363 return "menuFile.Load Game";
5365 return "menuMode.Edit Position";
5367 return "menuMode.Training";
5368 case IcsPlayingWhite:
5369 case IcsPlayingBlack:
5373 return "menuMode.ICS Client";
5380 void ModeHighlight()
5383 static int oldPausing = FALSE;
5384 static GameMode oldmode = (GameMode) -1;
5387 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5389 if (pausing != oldPausing) {
5390 oldPausing = pausing;
5392 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5394 XtSetArg(args[0], XtNleftBitmap, None);
5396 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5399 if (appData.showButtonBar) {
5400 /* Always toggle, don't set. Previous code messes up when
5401 invoked while the button is pressed, as releasing it
5402 toggles the state again. */
5405 XtSetArg(args[0], XtNbackground, &oldbg);
5406 XtSetArg(args[1], XtNforeground, &oldfg);
5407 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5409 XtSetArg(args[0], XtNbackground, oldfg);
5410 XtSetArg(args[1], XtNforeground, oldbg);
5412 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5416 wname = ModeToWidgetName(oldmode);
5417 if (wname != NULL) {
5418 XtSetArg(args[0], XtNleftBitmap, None);
5419 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5421 wname = ModeToWidgetName(gameMode);
5422 if (wname != NULL) {
5423 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5424 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5427 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5428 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5430 /* Maybe all the enables should be handled here, not just this one */
5431 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5432 gameMode == Training || gameMode == PlayFromGameFile);
5437 * Button/menu procedures
5439 void ResetProc(w, event, prms, nprms)
5448 int LoadGamePopUp(f, gameNumber, title)
5453 cmailMsgLoaded = FALSE;
5454 if (gameNumber == 0) {
5455 int error = GameListBuild(f);
5457 DisplayError(_("Cannot build game list"), error);
5458 } else if (!ListEmpty(&gameList) &&
5459 ((ListGame *) gameList.tailPred)->number > 1) {
5460 GameListPopUp(f, title);
5466 return LoadGame(f, gameNumber, title, FALSE);
5469 void LoadGameProc(w, event, prms, nprms)
5475 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5478 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5481 void LoadNextGameProc(w, event, prms, nprms)
5490 void LoadPrevGameProc(w, event, prms, nprms)
5499 void ReloadGameProc(w, event, prms, nprms)
5508 void LoadNextPositionProc(w, event, prms, nprms)
5517 void LoadPrevPositionProc(w, event, prms, nprms)
5526 void ReloadPositionProc(w, event, prms, nprms)
5535 void LoadPositionProc(w, event, prms, nprms)
5541 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5544 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5547 void SaveGameProc(w, event, prms, nprms)
5553 FileNamePopUp(_("Save game file name?"),
5554 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5555 appData.oldSaveStyle ? ".game" : ".pgn",
5559 void SavePositionProc(w, event, prms, nprms)
5565 FileNamePopUp(_("Save position file name?"),
5566 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5567 appData.oldSaveStyle ? ".pos" : ".fen",
5571 void ReloadCmailMsgProc(w, event, prms, nprms)
5577 ReloadCmailMsgEvent(FALSE);
5580 void MailMoveProc(w, event, prms, nprms)
5589 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5590 char *selected_fen_position=NULL;
5593 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5594 Atom *type_return, XtPointer *value_return,
5595 unsigned long *length_return, int *format_return)
5597 char *selection_tmp;
5599 if (!selected_fen_position) return False; /* should never happen */
5600 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5601 /* note: since no XtSelectionDoneProc was registered, Xt will
5602 * automatically call XtFree on the value returned. So have to
5603 * make a copy of it allocated with XtMalloc */
5604 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5605 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5607 *value_return=selection_tmp;
5608 *length_return=strlen(selection_tmp);
5609 *type_return=*target;
5610 *format_return = 8; /* bits per byte */
5612 } else if (*target == XA_TARGETS(xDisplay)) {
5613 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5614 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5615 targets_tmp[1] = XA_STRING;
5616 *value_return = targets_tmp;
5617 *type_return = XA_ATOM;
5620 // This code leads to a read of value_return out of bounds on 64-bit systems.
5621 // Other code which I have seen always sets *format_return to 32 independent of
5622 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5623 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5624 *format_return = 8 * sizeof(Atom);
5625 if (*format_return > 32) {
5626 *length_return *= *format_return / 32;
5627 *format_return = 32;
5630 *format_return = 32;
5638 /* note: when called from menu all parameters are NULL, so no clue what the
5639 * Widget which was clicked on was, or what the click event was
5641 void CopyPositionProc(w, event, prms, nprms)
5648 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5649 * have a notion of a position that is selected but not copied.
5650 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5652 if(gameMode == EditPosition) EditPositionDone(TRUE);
5653 if (selected_fen_position) free(selected_fen_position);
5654 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5655 if (!selected_fen_position) return;
5656 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5658 SendPositionSelection,
5659 NULL/* lose_ownership_proc */ ,
5660 NULL/* transfer_done_proc */);
5661 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5663 SendPositionSelection,
5664 NULL/* lose_ownership_proc */ ,
5665 NULL/* transfer_done_proc */);
5668 /* function called when the data to Paste is ready */
5670 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5671 Atom *type, XtPointer value, unsigned long *len, int *format)
5674 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5675 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5676 EditPositionPasteFEN(fenstr);
5680 /* called when Paste Position button is pressed,
5681 * all parameters will be NULL */
5682 void PastePositionProc(w, event, prms, nprms)
5688 XtGetSelectionValue(menuBarWidget,
5689 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5690 /* (XtSelectionCallbackProc) */ PastePositionCB,
5691 NULL, /* client_data passed to PastePositionCB */
5693 /* better to use the time field from the event that triggered the
5694 * call to this function, but that isn't trivial to get
5702 SendGameSelection(Widget w, Atom *selection, Atom *target,
5703 Atom *type_return, XtPointer *value_return,
5704 unsigned long *length_return, int *format_return)
5706 char *selection_tmp;
5708 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5709 FILE* f = fopen(gameCopyFilename, "r");
5712 if (f == NULL) return False;
5716 selection_tmp = XtMalloc(len + 1);
5717 count = fread(selection_tmp, 1, len, f);
5720 XtFree(selection_tmp);
5723 selection_tmp[len] = NULLCHAR;
5724 *value_return = selection_tmp;
5725 *length_return = len;
5726 *type_return = *target;
5727 *format_return = 8; /* bits per byte */
5729 } else if (*target == XA_TARGETS(xDisplay)) {
5730 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5731 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5732 targets_tmp[1] = XA_STRING;
5733 *value_return = targets_tmp;
5734 *type_return = XA_ATOM;
5737 // This code leads to a read of value_return out of bounds on 64-bit systems.
5738 // Other code which I have seen always sets *format_return to 32 independent of
5739 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5740 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5741 *format_return = 8 * sizeof(Atom);
5742 if (*format_return > 32) {
5743 *length_return *= *format_return / 32;
5744 *format_return = 32;
5747 *format_return = 32;
5755 void CopySomething()
5758 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5759 * have a notion of a game that is selected but not copied.
5760 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5762 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5765 NULL/* lose_ownership_proc */ ,
5766 NULL/* transfer_done_proc */);
5767 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5770 NULL/* lose_ownership_proc */ ,
5771 NULL/* transfer_done_proc */);
5774 /* note: when called from menu all parameters are NULL, so no clue what the
5775 * Widget which was clicked on was, or what the click event was
5777 void CopyGameProc(w, event, prms, nprms)
5785 ret = SaveGameToFile(gameCopyFilename, FALSE);
5791 void CopyGameListProc(w, event, prms, nprms)
5797 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5801 /* function called when the data to Paste is ready */
5803 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5804 Atom *type, XtPointer value, unsigned long *len, int *format)
5807 if (value == NULL || *len == 0) {
5808 return; /* nothing had been selected to copy */
5810 f = fopen(gamePasteFilename, "w");
5812 DisplayError(_("Can't open temp file"), errno);
5815 fwrite(value, 1, *len, f);
5818 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5821 /* called when Paste Game button is pressed,
5822 * all parameters will be NULL */
5823 void PasteGameProc(w, event, prms, nprms)
5829 XtGetSelectionValue(menuBarWidget,
5830 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5831 /* (XtSelectionCallbackProc) */ PasteGameCB,
5832 NULL, /* client_data passed to PasteGameCB */
5834 /* better to use the time field from the event that triggered the
5835 * call to this function, but that isn't trivial to get
5845 SaveGameProc(NULL, NULL, NULL, NULL);
5849 void QuitProc(w, event, prms, nprms)
5858 void PauseProc(w, event, prms, nprms)
5868 void MachineBlackProc(w, event, prms, nprms)
5874 MachineBlackEvent();
5877 void MachineWhiteProc(w, event, prms, nprms)
5883 MachineWhiteEvent();
5886 void AnalyzeModeProc(w, event, prms, nprms)
5894 if (!first.analysisSupport) {
5895 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5896 DisplayError(buf, 0);
5899 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5900 if (appData.icsActive) {
5901 if (gameMode != IcsObserving) {
5902 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5903 DisplayError(buf, 0);
5905 if (appData.icsEngineAnalyze) {
5906 if (appData.debugMode)
5907 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5913 /* if enable, use want disable icsEngineAnalyze */
5914 if (appData.icsEngineAnalyze) {
5919 appData.icsEngineAnalyze = TRUE;
5920 if (appData.debugMode)
5921 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5923 #ifndef OPTIONSDIALOG
5924 if (!appData.showThinking)
5925 ShowThinkingProc(w,event,prms,nprms);
5931 void AnalyzeFileProc(w, event, prms, nprms)
5937 if (!first.analysisSupport) {
5939 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5940 DisplayError(buf, 0);
5943 // Reset(FALSE, TRUE);
5944 #ifndef OPTIONSDIALOG
5945 if (!appData.showThinking)
5946 ShowThinkingProc(w,event,prms,nprms);
5949 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5950 AnalysisPeriodicEvent(1);
5953 void TwoMachinesProc(w, event, prms, nprms)
5962 void MatchProc(w, event, prms, nprms)
5971 void IcsClientProc(w, event, prms, nprms)
5980 void EditGameProc(w, event, prms, nprms)
5989 void EditPositionProc(w, event, prms, nprms)
5995 EditPositionEvent();
5998 void TrainingProc(w, event, prms, nprms)
6007 void EditCommentProc(w, event, prms, nprms)
6015 if (PopDown(1)) { // popdown succesful
6017 XtSetArg(args[j], XtNleftBitmap, None); j++;
6018 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6019 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6020 } else // was not up
6024 void IcsInputBoxProc(w, event, prms, nprms)
6030 if (!PopDown(4)) ICSInputBoxPopUp();
6033 void AcceptProc(w, event, prms, nprms)
6042 void DeclineProc(w, event, prms, nprms)
6051 void RematchProc(w, event, prms, nprms)
6060 void CallFlagProc(w, event, prms, nprms)
6069 void DrawProc(w, event, prms, nprms)
6078 void AbortProc(w, event, prms, nprms)
6087 void AdjournProc(w, event, prms, nprms)
6096 void ResignProc(w, event, prms, nprms)
6105 void AdjuWhiteProc(w, event, prms, nprms)
6111 UserAdjudicationEvent(+1);
6114 void AdjuBlackProc(w, event, prms, nprms)
6120 UserAdjudicationEvent(-1);
6123 void AdjuDrawProc(w, event, prms, nprms)
6129 UserAdjudicationEvent(0);
6132 void EnterKeyProc(w, event, prms, nprms)
6138 if (shellUp[4] == True)
6142 void UpKeyProc(w, event, prms, nprms)
6147 { // [HGM] input: let up-arrow recall previous line from history
6154 if (!shellUp[4]) return;
6155 edit = boxOptions[0].handle;
6157 XtSetArg(args[j], XtNstring, &val); j++;
6158 XtGetValues(edit, args, j);
6159 val = PrevInHistory(val);
6160 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6161 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6163 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6164 XawTextReplace(edit, 0, 0, &t);
6165 XawTextSetInsertionPoint(edit, 9999);
6169 void DownKeyProc(w, event, prms, nprms)
6174 { // [HGM] input: let down-arrow recall next line from history
6179 if (!shellUp[4]) return;
6180 edit = boxOptions[0].handle;
6181 val = NextInHistory();
6182 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6183 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6185 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6186 XawTextReplace(edit, 0, 0, &t);
6187 XawTextSetInsertionPoint(edit, 9999);
6191 void StopObservingProc(w, event, prms, nprms)
6197 StopObservingEvent();
6200 void StopExaminingProc(w, event, prms, nprms)
6206 StopExaminingEvent();
6209 void UploadProc(w, event, prms, nprms)
6219 void ForwardProc(w, event, prms, nprms)
6229 void BackwardProc(w, event, prms, nprms)
6238 void TempBackwardProc(w, event, prms, nprms)
6244 if (!TempBackwardActive) {
6245 TempBackwardActive = True;
6250 void TempForwardProc(w, event, prms, nprms)
6256 /* Check to see if triggered by a key release event for a repeating key.
6257 * If so the next queued event will be a key press of the same key at the same time */
6258 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6260 XPeekEvent(xDisplay, &next);
6261 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6262 next.xkey.keycode == event->xkey.keycode)
6266 TempBackwardActive = False;
6269 void ToStartProc(w, event, prms, nprms)
6278 void ToEndProc(w, event, prms, nprms)
6287 void RevertProc(w, event, prms, nprms)
6296 void AnnotateProc(w, event, prms, nprms)
6305 void TruncateGameProc(w, event, prms, nprms)
6311 TruncateGameEvent();
6313 void RetractMoveProc(w, event, prms, nprms)
6322 void MoveNowProc(w, event, prms, nprms)
6331 void FlipViewProc(w, event, prms, nprms)
6337 flipView = !flipView;
6338 DrawPosition(True, NULL);
6341 void PonderNextMoveProc(w, event, prms, nprms)
6349 PonderNextMoveEvent(!appData.ponderNextMove);
6350 #ifndef OPTIONSDIALOG
6351 if (appData.ponderNextMove) {
6352 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6354 XtSetArg(args[0], XtNleftBitmap, None);
6356 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6361 #ifndef OPTIONSDIALOG
6362 void AlwaysQueenProc(w, event, prms, nprms)
6370 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6372 if (appData.alwaysPromoteToQueen) {
6373 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6375 XtSetArg(args[0], XtNleftBitmap, None);
6377 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6381 void AnimateDraggingProc(w, event, prms, nprms)
6389 appData.animateDragging = !appData.animateDragging;
6391 if (appData.animateDragging) {
6392 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6395 XtSetArg(args[0], XtNleftBitmap, None);
6397 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6401 void AnimateMovingProc(w, event, prms, nprms)
6409 appData.animate = !appData.animate;
6411 if (appData.animate) {
6412 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6415 XtSetArg(args[0], XtNleftBitmap, None);
6417 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6421 void AutoflagProc(w, event, prms, nprms)
6429 appData.autoCallFlag = !appData.autoCallFlag;
6431 if (appData.autoCallFlag) {
6432 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6434 XtSetArg(args[0], XtNleftBitmap, None);
6436 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6440 void AutoflipProc(w, event, prms, nprms)
6448 appData.autoFlipView = !appData.autoFlipView;
6450 if (appData.autoFlipView) {
6451 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6453 XtSetArg(args[0], XtNleftBitmap, None);
6455 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6459 void BlindfoldProc(w, event, prms, nprms)
6467 appData.blindfold = !appData.blindfold;
6469 if (appData.blindfold) {
6470 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6472 XtSetArg(args[0], XtNleftBitmap, None);
6474 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6477 DrawPosition(True, NULL);
6480 void TestLegalityProc(w, event, prms, nprms)
6488 appData.testLegality = !appData.testLegality;
6490 if (appData.testLegality) {
6491 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6493 XtSetArg(args[0], XtNleftBitmap, None);
6495 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6500 void FlashMovesProc(w, event, prms, nprms)
6508 if (appData.flashCount == 0) {
6509 appData.flashCount = 3;
6511 appData.flashCount = -appData.flashCount;
6514 if (appData.flashCount > 0) {
6515 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6517 XtSetArg(args[0], XtNleftBitmap, None);
6519 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6524 void HighlightDraggingProc(w, event, prms, nprms)
6532 appData.highlightDragging = !appData.highlightDragging;
6534 if (appData.highlightDragging) {
6535 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6537 XtSetArg(args[0], XtNleftBitmap, None);
6539 XtSetValues(XtNameToWidget(menuBarWidget,
6540 "menuOptions.Highlight Dragging"), args, 1);
6544 void HighlightLastMoveProc(w, event, prms, nprms)
6552 appData.highlightLastMove = !appData.highlightLastMove;
6554 if (appData.highlightLastMove) {
6555 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6557 XtSetArg(args[0], XtNleftBitmap, None);
6559 XtSetValues(XtNameToWidget(menuBarWidget,
6560 "menuOptions.Highlight Last Move"), args, 1);
6563 void HighlightArrowProc(w, event, prms, nprms)
6571 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6573 if (appData.highlightMoveWithArrow) {
6574 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6576 XtSetArg(args[0], XtNleftBitmap, None);
6578 XtSetValues(XtNameToWidget(menuBarWidget,
6579 "menuOptions.Arrow"), args, 1);
6583 void IcsAlarmProc(w, event, prms, nprms)
6591 appData.icsAlarm = !appData.icsAlarm;
6593 if (appData.icsAlarm) {
6594 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6596 XtSetArg(args[0], XtNleftBitmap, None);
6598 XtSetValues(XtNameToWidget(menuBarWidget,
6599 "menuOptions.ICS Alarm"), args, 1);
6603 void MoveSoundProc(w, event, prms, nprms)
6611 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6613 if (appData.ringBellAfterMoves) {
6614 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6616 XtSetArg(args[0], XtNleftBitmap, None);
6618 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6622 void OneClickProc(w, event, prms, nprms)
6630 appData.oneClick = !appData.oneClick;
6632 if (appData.oneClick) {
6633 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6635 XtSetArg(args[0], XtNleftBitmap, None);
6637 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6641 void PeriodicUpdatesProc(w, event, prms, nprms)
6649 PeriodicUpdatesEvent(!appData.periodicUpdates);
6651 if (appData.periodicUpdates) {
6652 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6654 XtSetArg(args[0], XtNleftBitmap, None);
6656 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6660 void PopupExitMessageProc(w, event, prms, nprms)
6668 appData.popupExitMessage = !appData.popupExitMessage;
6670 if (appData.popupExitMessage) {
6671 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6673 XtSetArg(args[0], XtNleftBitmap, None);
6675 XtSetValues(XtNameToWidget(menuBarWidget,
6676 "menuOptions.Popup Exit Message"), args, 1);
6679 void PopupMoveErrorsProc(w, event, prms, nprms)
6687 appData.popupMoveErrors = !appData.popupMoveErrors;
6689 if (appData.popupMoveErrors) {
6690 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6692 XtSetArg(args[0], XtNleftBitmap, None);
6694 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6699 void PremoveProc(w, event, prms, nprms)
6707 appData.premove = !appData.premove;
6709 if (appData.premove) {
6710 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6712 XtSetArg(args[0], XtNleftBitmap, None);
6714 XtSetValues(XtNameToWidget(menuBarWidget,
6715 "menuOptions.Premove"), args, 1);
6719 void ShowCoordsProc(w, event, prms, nprms)
6727 appData.showCoords = !appData.showCoords;
6729 if (appData.showCoords) {
6730 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6732 XtSetArg(args[0], XtNleftBitmap, None);
6734 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6737 DrawPosition(True, NULL);
6740 void ShowThinkingProc(w, event, prms, nprms)
6746 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6747 ShowThinkingEvent();
6750 void HideThinkingProc(w, event, prms, nprms)
6758 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6759 ShowThinkingEvent();
6761 if (appData.hideThinkingFromHuman) {
6762 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6764 XtSetArg(args[0], XtNleftBitmap, None);
6766 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6771 void SaveOnExitProc(w, event, prms, nprms)
6779 saveSettingsOnExit = !saveSettingsOnExit;
6781 if (saveSettingsOnExit) {
6782 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6784 XtSetArg(args[0], XtNleftBitmap, None);
6786 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6790 void SaveSettingsProc(w, event, prms, nprms)
6796 SaveSettings(settingsFileName);
6799 void InfoProc(w, event, prms, nprms)
6806 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6811 void ManProc(w, event, prms, nprms)
6819 if (nprms && *nprms > 0)
6823 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6827 void HintProc(w, event, prms, nprms)
6836 void BookProc(w, event, prms, nprms)
6845 void AboutProc(w, event, prms, nprms)
6853 char *zippy = _(" (with Zippy code)");
6857 snprintf(buf, sizeof(buf),
6859 "Copyright 1991 Digital Equipment Corporation\n"
6860 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6861 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6862 "%s is free software and carries NO WARRANTY;"
6863 "see the file COPYING for more information."),
6864 programVersion, zippy, PACKAGE);
6865 ErrorPopUp(_("About XBoard"), buf, FALSE);
6868 void DebugProc(w, event, prms, nprms)
6874 appData.debugMode = !appData.debugMode;
6877 void AboutGameProc(w, event, prms, nprms)
6886 void NothingProc(w, event, prms, nprms)
6895 void DisplayMessage(message, extMessage)
6896 char *message, *extMessage;
6898 /* display a message in the message widget */
6907 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6912 message = extMessage;
6916 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6918 /* need to test if messageWidget already exists, since this function
6919 can also be called during the startup, if for example a Xresource
6920 is not set up correctly */
6923 XtSetArg(arg, XtNlabel, message);
6924 XtSetValues(messageWidget, &arg, 1);
6930 void DisplayTitle(text)
6935 char title[MSG_SIZ];
6938 if (text == NULL) text = "";
6940 if (appData.titleInWindow) {
6942 XtSetArg(args[i], XtNlabel, text); i++;
6943 XtSetValues(titleWidget, args, i);
6946 if (*text != NULLCHAR) {
6947 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6948 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6949 } else if (appData.icsActive) {
6950 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6951 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6952 } else if (appData.cmailGameName[0] != NULLCHAR) {
6953 snprintf(icon, sizeof(icon), "%s", "CMail");
6954 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6956 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6957 } else if (gameInfo.variant == VariantGothic) {
6958 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6959 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6962 } else if (gameInfo.variant == VariantFalcon) {
6963 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6964 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6966 } else if (appData.noChessProgram) {
6967 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6968 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6970 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6971 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6974 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6975 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6976 XtSetValues(shellWidget, args, i);
6977 XSync(xDisplay, False);
6982 DisplayError(message, error)
6989 if (appData.debugMode || appData.matchMode) {
6990 fprintf(stderr, "%s: %s\n", programName, message);
6993 if (appData.debugMode || appData.matchMode) {
6994 fprintf(stderr, "%s: %s: %s\n",
6995 programName, message, strerror(error));
6997 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7000 ErrorPopUp(_("Error"), message, FALSE);
7004 void DisplayMoveError(message)
7009 DrawPosition(FALSE, NULL);
7010 if (appData.debugMode || appData.matchMode) {
7011 fprintf(stderr, "%s: %s\n", programName, message);
7013 if (appData.popupMoveErrors) {
7014 ErrorPopUp(_("Error"), message, FALSE);
7016 DisplayMessage(message, "");
7021 void DisplayFatalError(message, error, status)
7027 errorExitStatus = status;
7029 fprintf(stderr, "%s: %s\n", programName, message);
7031 fprintf(stderr, "%s: %s: %s\n",
7032 programName, message, strerror(error));
7033 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7036 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7037 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7043 void DisplayInformation(message)
7047 ErrorPopUp(_("Information"), message, TRUE);
7050 void DisplayNote(message)
7054 ErrorPopUp(_("Note"), message, FALSE);
7058 NullXErrorCheck(dpy, error_event)
7060 XErrorEvent *error_event;
7065 void DisplayIcsInteractionTitle(message)
7068 if (oldICSInteractionTitle == NULL) {
7069 /* Magic to find the old window title, adapted from vim */
7070 char *wina = getenv("WINDOWID");
7072 Window win = (Window) atoi(wina);
7073 Window root, parent, *children;
7074 unsigned int nchildren;
7075 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7077 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7078 if (!XQueryTree(xDisplay, win, &root, &parent,
7079 &children, &nchildren)) break;
7080 if (children) XFree((void *)children);
7081 if (parent == root || parent == 0) break;
7084 XSetErrorHandler(oldHandler);
7086 if (oldICSInteractionTitle == NULL) {
7087 oldICSInteractionTitle = "xterm";
7090 printf("\033]0;%s\007", message);
7094 char pendingReplyPrefix[MSG_SIZ];
7095 ProcRef pendingReplyPR;
7097 void AskQuestionProc(w, event, prms, nprms)
7104 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7108 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7111 void AskQuestionPopDown()
7113 if (!askQuestionUp) return;
7114 XtPopdown(askQuestionShell);
7115 XtDestroyWidget(askQuestionShell);
7116 askQuestionUp = False;
7119 void AskQuestionReplyAction(w, event, prms, nprms)
7129 reply = XawDialogGetValueString(w = XtParent(w));
7130 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7131 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7132 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7133 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7134 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7135 AskQuestionPopDown();
7137 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7140 void AskQuestionCallback(w, client_data, call_data)
7142 XtPointer client_data, call_data;
7147 XtSetArg(args[0], XtNlabel, &name);
7148 XtGetValues(w, args, 1);
7150 if (strcmp(name, _("cancel")) == 0) {
7151 AskQuestionPopDown();
7153 AskQuestionReplyAction(w, NULL, NULL, NULL);
7157 void AskQuestion(title, question, replyPrefix, pr)
7158 char *title, *question, *replyPrefix;
7162 Widget popup, layout, dialog, edit;
7168 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7169 pendingReplyPR = pr;
7172 XtSetArg(args[i], XtNresizable, True); i++;
7173 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7174 askQuestionShell = popup =
7175 XtCreatePopupShell(title, transientShellWidgetClass,
7176 shellWidget, args, i);
7179 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7180 layoutArgs, XtNumber(layoutArgs));
7183 XtSetArg(args[i], XtNlabel, question); i++;
7184 XtSetArg(args[i], XtNvalue, ""); i++;
7185 XtSetArg(args[i], XtNborderWidth, 0); i++;
7186 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7189 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7190 (XtPointer) dialog);
7191 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7192 (XtPointer) dialog);
7194 XtRealizeWidget(popup);
7195 CatchDeleteWindow(popup, "AskQuestionPopDown");
7197 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7198 &x, &y, &win_x, &win_y, &mask);
7200 XtSetArg(args[0], XtNx, x - 10);
7201 XtSetArg(args[1], XtNy, y - 30);
7202 XtSetValues(popup, args, 2);
7204 XtPopup(popup, XtGrabExclusive);
7205 askQuestionUp = True;
7207 edit = XtNameToWidget(dialog, "*value");
7208 XtSetKeyboardFocus(popup, edit);
7216 if (*name == NULLCHAR) {
7218 } else if (strcmp(name, "$") == 0) {
7219 putc(BELLCHAR, stderr);
7222 char *prefix = "", *sep = "";
7223 if(appData.soundProgram[0] == NULLCHAR) return;
7224 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7225 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7233 PlaySound(appData.soundMove);
7239 PlaySound(appData.soundIcsWin);
7245 PlaySound(appData.soundIcsLoss);
7251 PlaySound(appData.soundIcsDraw);
7255 PlayIcsUnfinishedSound()
7257 PlaySound(appData.soundIcsUnfinished);
7263 PlaySound(appData.soundIcsAlarm);
7269 PlaySound(appData.soundTell);
7275 system("stty echo");
7282 system("stty -echo");
7287 RunCommand(char *buf)
7293 Colorize(cc, continuation)
7298 int count, outCount, error;
7300 if (textColors[(int)cc].bg > 0) {
7301 if (textColors[(int)cc].fg > 0) {
7302 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7303 textColors[(int)cc].fg, textColors[(int)cc].bg);
7305 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7306 textColors[(int)cc].bg);
7309 if (textColors[(int)cc].fg > 0) {
7310 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7311 textColors[(int)cc].fg);
7313 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7316 count = strlen(buf);
7317 outCount = OutputToProcess(NoProc, buf, count, &error);
7318 if (outCount < count) {
7319 DisplayFatalError(_("Error writing to display"), error, 1);
7322 if (continuation) return;
7325 PlaySound(appData.soundShout);
7328 PlaySound(appData.soundSShout);
7331 PlaySound(appData.soundChannel1);
7334 PlaySound(appData.soundChannel);
7337 PlaySound(appData.soundKibitz);
7340 PlaySound(appData.soundTell);
7342 case ColorChallenge:
7343 PlaySound(appData.soundChallenge);
7346 PlaySound(appData.soundRequest);
7349 PlaySound(appData.soundSeek);
7360 return getpwuid(getuid())->pw_name;
7364 ExpandPathName(path)
7367 static char static_buf[4*MSG_SIZ];
7368 char *d, *s, buf[4*MSG_SIZ];
7374 while (*s && isspace(*s))
7383 if (*(s+1) == '/') {
7384 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7388 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7389 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7390 pwd = getpwnam(buf);
7393 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7397 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7398 strcat(d, strchr(s+1, '/'));
7402 safeStrCpy(d, s, 4*MSG_SIZ );
7409 static char host_name[MSG_SIZ];
7411 #if HAVE_GETHOSTNAME
7412 gethostname(host_name, MSG_SIZ);
7414 #else /* not HAVE_GETHOSTNAME */
7415 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7416 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7418 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7420 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7421 #endif /* not HAVE_GETHOSTNAME */
7424 XtIntervalId delayedEventTimerXID = 0;
7425 DelayedEventCallback delayedEventCallback = 0;
7430 delayedEventTimerXID = 0;
7431 delayedEventCallback();
7435 ScheduleDelayedEvent(cb, millisec)
7436 DelayedEventCallback cb; long millisec;
7438 if(delayedEventTimerXID && delayedEventCallback == cb)
7439 // [HGM] alive: replace, rather than add or flush identical event
7440 XtRemoveTimeOut(delayedEventTimerXID);
7441 delayedEventCallback = cb;
7442 delayedEventTimerXID =
7443 XtAppAddTimeOut(appContext, millisec,
7444 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7447 DelayedEventCallback
7450 if (delayedEventTimerXID) {
7451 return delayedEventCallback;
7458 CancelDelayedEvent()
7460 if (delayedEventTimerXID) {
7461 XtRemoveTimeOut(delayedEventTimerXID);
7462 delayedEventTimerXID = 0;
7466 XtIntervalId loadGameTimerXID = 0;
7468 int LoadGameTimerRunning()
7470 return loadGameTimerXID != 0;
7473 int StopLoadGameTimer()
7475 if (loadGameTimerXID != 0) {
7476 XtRemoveTimeOut(loadGameTimerXID);
7477 loadGameTimerXID = 0;
7485 LoadGameTimerCallback(arg, id)
7489 loadGameTimerXID = 0;
7494 StartLoadGameTimer(millisec)
7498 XtAppAddTimeOut(appContext, millisec,
7499 (XtTimerCallbackProc) LoadGameTimerCallback,
7503 XtIntervalId analysisClockXID = 0;
7506 AnalysisClockCallback(arg, id)
7510 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7511 || appData.icsEngineAnalyze) { // [DM]
7512 AnalysisPeriodicEvent(0);
7513 StartAnalysisClock();
7518 StartAnalysisClock()
7521 XtAppAddTimeOut(appContext, 2000,
7522 (XtTimerCallbackProc) AnalysisClockCallback,
7526 XtIntervalId clockTimerXID = 0;
7528 int ClockTimerRunning()
7530 return clockTimerXID != 0;
7533 int StopClockTimer()
7535 if (clockTimerXID != 0) {
7536 XtRemoveTimeOut(clockTimerXID);
7545 ClockTimerCallback(arg, id)
7554 StartClockTimer(millisec)
7558 XtAppAddTimeOut(appContext, millisec,
7559 (XtTimerCallbackProc) ClockTimerCallback,
7564 DisplayTimerLabel(w, color, timer, highlight)
7573 /* check for low time warning */
7574 Pixel foregroundOrWarningColor = timerForegroundPixel;
7577 appData.lowTimeWarning &&
7578 (timer / 1000) < appData.icsAlarmTime)
7579 foregroundOrWarningColor = lowTimeWarningColor;
7581 if (appData.clockMode) {
7582 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7583 XtSetArg(args[0], XtNlabel, buf);
7585 snprintf(buf, MSG_SIZ, "%s ", color);
7586 XtSetArg(args[0], XtNlabel, buf);
7591 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7592 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7594 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7595 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7598 XtSetValues(w, args, 3);
7602 DisplayWhiteClock(timeRemaining, highlight)
7608 if(appData.noGUI) return;
7609 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7610 if (highlight && iconPixmap == bIconPixmap) {
7611 iconPixmap = wIconPixmap;
7612 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7613 XtSetValues(shellWidget, args, 1);
7618 DisplayBlackClock(timeRemaining, highlight)
7624 if(appData.noGUI) return;
7625 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7626 if (highlight && iconPixmap == wIconPixmap) {
7627 iconPixmap = bIconPixmap;
7628 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7629 XtSetValues(shellWidget, args, 1);
7647 int StartChildProcess(cmdLine, dir, pr)
7654 int to_prog[2], from_prog[2];
7658 if (appData.debugMode) {
7659 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7662 /* We do NOT feed the cmdLine to the shell; we just
7663 parse it into blank-separated arguments in the
7664 most simple-minded way possible.
7667 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7670 while(*p == ' ') p++;
7672 if(*p == '"' || *p == '\'')
7673 p = strchr(++argv[i-1], *p);
7674 else p = strchr(p, ' ');
7675 if (p == NULL) break;
7680 SetUpChildIO(to_prog, from_prog);
7682 if ((pid = fork()) == 0) {
7684 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7685 close(to_prog[1]); // first close the unused pipe ends
7686 close(from_prog[0]);
7687 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7688 dup2(from_prog[1], 1);
7689 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7690 close(from_prog[1]); // and closing again loses one of the pipes!
7691 if(fileno(stderr) >= 2) // better safe than sorry...
7692 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7694 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7699 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7701 execvp(argv[0], argv);
7703 /* If we get here, exec failed */
7708 /* Parent process */
7710 close(from_prog[1]);
7712 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7715 cp->fdFrom = from_prog[0];
7716 cp->fdTo = to_prog[1];
7721 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7722 static RETSIGTYPE AlarmCallBack(int n)
7728 DestroyChildProcess(pr, signalType)
7732 ChildProc *cp = (ChildProc *) pr;
7734 if (cp->kind != CPReal) return;
7736 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7737 signal(SIGALRM, AlarmCallBack);
7739 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7740 kill(cp->pid, SIGKILL); // kill it forcefully
7741 wait((int *) 0); // and wait again
7745 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7747 /* Process is exiting either because of the kill or because of
7748 a quit command sent by the backend; either way, wait for it to die.
7757 InterruptChildProcess(pr)
7760 ChildProc *cp = (ChildProc *) pr;
7762 if (cp->kind != CPReal) return;
7763 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7766 int OpenTelnet(host, port, pr)
7771 char cmdLine[MSG_SIZ];
7773 if (port[0] == NULLCHAR) {
7774 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7776 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7778 return StartChildProcess(cmdLine, "", pr);
7781 int OpenTCP(host, port, pr)
7787 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7788 #else /* !OMIT_SOCKETS */
7789 struct addrinfo hints;
7790 struct addrinfo *ais, *ai;
7795 memset(&hints, 0, sizeof(hints));
7796 hints.ai_family = AF_UNSPEC;
7797 hints.ai_socktype = SOCK_STREAM;
7799 error = getaddrinfo(host, port, &hints, &ais);
7801 /* a getaddrinfo error is not an errno, so can't return it */
7802 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7803 host, port, gai_strerror(error));
7807 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7808 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7812 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7825 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7831 #endif /* !OMIT_SOCKETS */
7836 int OpenCommPort(name, pr)
7843 fd = open(name, 2, 0);
7844 if (fd < 0) return errno;
7846 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7856 int OpenLoopback(pr)
7862 SetUpChildIO(to, from);
7864 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7867 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7874 int OpenRcmd(host, user, cmd, pr)
7875 char *host, *user, *cmd;
7878 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7882 #define INPUT_SOURCE_BUF_SIZE 8192
7891 char buf[INPUT_SOURCE_BUF_SIZE];
7896 DoInputCallback(closure, source, xid)
7901 InputSource *is = (InputSource *) closure;
7906 if (is->lineByLine) {
7907 count = read(is->fd, is->unused,
7908 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7910 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7913 is->unused += count;
7915 while (p < is->unused) {
7916 q = memchr(p, '\n', is->unused - p);
7917 if (q == NULL) break;
7919 (is->func)(is, is->closure, p, q - p, 0);
7923 while (p < is->unused) {
7928 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7933 (is->func)(is, is->closure, is->buf, count, error);
7937 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7944 ChildProc *cp = (ChildProc *) pr;
7946 is = (InputSource *) calloc(1, sizeof(InputSource));
7947 is->lineByLine = lineByLine;
7951 is->fd = fileno(stdin);
7953 is->kind = cp->kind;
7954 is->fd = cp->fdFrom;
7957 is->unused = is->buf;
7960 is->xid = XtAppAddInput(appContext, is->fd,
7961 (XtPointer) (XtInputReadMask),
7962 (XtInputCallbackProc) DoInputCallback,
7964 is->closure = closure;
7965 return (InputSourceRef) is;
7969 RemoveInputSource(isr)
7972 InputSource *is = (InputSource *) isr;
7974 if (is->xid == 0) return;
7975 XtRemoveInput(is->xid);
7979 int OutputToProcess(pr, message, count, outError)
7985 static int line = 0;
7986 ChildProc *cp = (ChildProc *) pr;
7991 if (appData.noJoin || !appData.useInternalWrap)
7992 outCount = fwrite(message, 1, count, stdout);
7995 int width = get_term_width();
7996 int len = wrap(NULL, message, count, width, &line);
7997 char *msg = malloc(len);
8001 outCount = fwrite(message, 1, count, stdout);
8004 dbgchk = wrap(msg, message, count, width, &line);
8005 if (dbgchk != len && appData.debugMode)
8006 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8007 outCount = fwrite(msg, 1, dbgchk, stdout);
8013 outCount = write(cp->fdTo, message, count);
8023 /* Output message to process, with "ms" milliseconds of delay
8024 between each character. This is needed when sending the logon
8025 script to ICC, which for some reason doesn't like the
8026 instantaneous send. */
8027 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8034 ChildProc *cp = (ChildProc *) pr;
8039 r = write(cp->fdTo, message++, 1);
8052 /**** Animation code by Hugh Fisher, DCS, ANU.
8054 Known problem: if a window overlapping the board is
8055 moved away while a piece is being animated underneath,
8056 the newly exposed area won't be updated properly.
8057 I can live with this.
8059 Known problem: if you look carefully at the animation
8060 of pieces in mono mode, they are being drawn as solid
8061 shapes without interior detail while moving. Fixing
8062 this would be a major complication for minimal return.
8065 /* Masks for XPM pieces. Black and white pieces can have
8066 different shapes, but in the interest of retaining my
8067 sanity pieces must have the same outline on both light
8068 and dark squares, and all pieces must use the same
8069 background square colors/images. */
8071 static int xpmDone = 0;
8074 CreateAnimMasks (pieceDepth)
8081 unsigned long plane;
8084 /* Need a bitmap just to get a GC with right depth */
8085 buf = XCreatePixmap(xDisplay, xBoardWindow,
8087 values.foreground = 1;
8088 values.background = 0;
8089 /* Don't use XtGetGC, not read only */
8090 maskGC = XCreateGC(xDisplay, buf,
8091 GCForeground | GCBackground, &values);
8092 XFreePixmap(xDisplay, buf);
8094 buf = XCreatePixmap(xDisplay, xBoardWindow,
8095 squareSize, squareSize, pieceDepth);
8096 values.foreground = XBlackPixel(xDisplay, xScreen);
8097 values.background = XWhitePixel(xDisplay, xScreen);
8098 bufGC = XCreateGC(xDisplay, buf,
8099 GCForeground | GCBackground, &values);
8101 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8102 /* Begin with empty mask */
8103 if(!xpmDone) // [HGM] pieces: keep using existing
8104 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8105 squareSize, squareSize, 1);
8106 XSetFunction(xDisplay, maskGC, GXclear);
8107 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8108 0, 0, squareSize, squareSize);
8110 /* Take a copy of the piece */
8115 XSetFunction(xDisplay, bufGC, GXcopy);
8116 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8118 0, 0, squareSize, squareSize, 0, 0);
8120 /* XOR the background (light) over the piece */
8121 XSetFunction(xDisplay, bufGC, GXxor);
8123 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8124 0, 0, squareSize, squareSize, 0, 0);
8126 XSetForeground(xDisplay, bufGC, lightSquareColor);
8127 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8130 /* We now have an inverted piece image with the background
8131 erased. Construct mask by just selecting all the non-zero
8132 pixels - no need to reconstruct the original image. */
8133 XSetFunction(xDisplay, maskGC, GXor);
8135 /* Might be quicker to download an XImage and create bitmap
8136 data from it rather than this N copies per piece, but it
8137 only takes a fraction of a second and there is a much
8138 longer delay for loading the pieces. */
8139 for (n = 0; n < pieceDepth; n ++) {
8140 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8141 0, 0, squareSize, squareSize,
8147 XFreePixmap(xDisplay, buf);
8148 XFreeGC(xDisplay, bufGC);
8149 XFreeGC(xDisplay, maskGC);
8153 InitAnimState (anim, info)
8155 XWindowAttributes * info;
8160 /* Each buffer is square size, same depth as window */
8161 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8162 squareSize, squareSize, info->depth);
8163 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8164 squareSize, squareSize, info->depth);
8166 /* Create a plain GC for blitting */
8167 mask = GCForeground | GCBackground | GCFunction |
8168 GCPlaneMask | GCGraphicsExposures;
8169 values.foreground = XBlackPixel(xDisplay, xScreen);
8170 values.background = XWhitePixel(xDisplay, xScreen);
8171 values.function = GXcopy;
8172 values.plane_mask = AllPlanes;
8173 values.graphics_exposures = False;
8174 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8176 /* Piece will be copied from an existing context at
8177 the start of each new animation/drag. */
8178 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8180 /* Outline will be a read-only copy of an existing */
8181 anim->outlineGC = None;
8187 XWindowAttributes info;
8189 if (xpmDone && gameInfo.variant == oldVariant) return;
8190 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8191 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8193 InitAnimState(&game, &info);
8194 InitAnimState(&player, &info);
8196 /* For XPM pieces, we need bitmaps to use as masks. */
8198 CreateAnimMasks(info.depth), xpmDone = 1;
8203 static Boolean frameWaiting;
8205 static RETSIGTYPE FrameAlarm (sig)
8208 frameWaiting = False;
8209 /* In case System-V style signals. Needed?? */
8210 signal(SIGALRM, FrameAlarm);
8217 struct itimerval delay;
8219 XSync(xDisplay, False);
8222 frameWaiting = True;
8223 signal(SIGALRM, FrameAlarm);
8224 delay.it_interval.tv_sec =
8225 delay.it_value.tv_sec = time / 1000;
8226 delay.it_interval.tv_usec =
8227 delay.it_value.tv_usec = (time % 1000) * 1000;
8228 setitimer(ITIMER_REAL, &delay, NULL);
8229 while (frameWaiting) pause();
8230 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8231 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8232 setitimer(ITIMER_REAL, &delay, NULL);
8242 XSync(xDisplay, False);
8244 usleep(time * 1000);
8255 /* Convert board position to corner of screen rect and color */
8258 ScreenSquare(column, row, pt, color)
8259 int column; int row; XPoint * pt; int * color;
8262 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8263 pt->y = lineGap + row * (squareSize + lineGap);
8265 pt->x = lineGap + column * (squareSize + lineGap);
8266 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8268 *color = SquareColor(row, column);
8271 /* Convert window coords to square */
8274 BoardSquare(x, y, column, row)
8275 int x; int y; int * column; int * row;
8277 *column = EventToSquare(x, BOARD_WIDTH);
8278 if (flipView && *column >= 0)
8279 *column = BOARD_WIDTH - 1 - *column;
8280 *row = EventToSquare(y, BOARD_HEIGHT);
8281 if (!flipView && *row >= 0)
8282 *row = BOARD_HEIGHT - 1 - *row;
8287 #undef Max /* just in case */
8289 #define Max(a, b) ((a) > (b) ? (a) : (b))
8290 #define Min(a, b) ((a) < (b) ? (a) : (b))
8293 SetRect(rect, x, y, width, height)
8294 XRectangle * rect; int x; int y; int width; int height;
8298 rect->width = width;
8299 rect->height = height;
8302 /* Test if two frames overlap. If they do, return
8303 intersection rect within old and location of
8304 that rect within new. */
8307 Intersect(old, new, size, area, pt)
8308 XPoint * old; XPoint * new;
8309 int size; XRectangle * area; XPoint * pt;
8311 if (old->x > new->x + size || new->x > old->x + size ||
8312 old->y > new->y + size || new->y > old->y + size) {
8315 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8316 size - abs(old->x - new->x), size - abs(old->y - new->y));
8317 pt->x = Max(old->x - new->x, 0);
8318 pt->y = Max(old->y - new->y, 0);
8323 /* For two overlapping frames, return the rect(s)
8324 in the old that do not intersect with the new. */
8327 CalcUpdateRects(old, new, size, update, nUpdates)
8328 XPoint * old; XPoint * new; int size;
8329 XRectangle update[]; int * nUpdates;
8333 /* If old = new (shouldn't happen) then nothing to draw */
8334 if (old->x == new->x && old->y == new->y) {
8338 /* Work out what bits overlap. Since we know the rects
8339 are the same size we don't need a full intersect calc. */
8341 /* Top or bottom edge? */
8342 if (new->y > old->y) {
8343 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8345 } else if (old->y > new->y) {
8346 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8347 size, old->y - new->y);
8350 /* Left or right edge - don't overlap any update calculated above. */
8351 if (new->x > old->x) {
8352 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8353 new->x - old->x, size - abs(new->y - old->y));
8355 } else if (old->x > new->x) {
8356 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8357 old->x - new->x, size - abs(new->y - old->y));
8364 /* Generate a series of frame coords from start->mid->finish.
8365 The movement rate doubles until the half way point is
8366 reached, then halves back down to the final destination,
8367 which gives a nice slow in/out effect. The algorithmn
8368 may seem to generate too many intermediates for short
8369 moves, but remember that the purpose is to attract the
8370 viewers attention to the piece about to be moved and
8371 then to where it ends up. Too few frames would be less
8375 Tween(start, mid, finish, factor, frames, nFrames)
8376 XPoint * start; XPoint * mid;
8377 XPoint * finish; int factor;
8378 XPoint frames[]; int * nFrames;
8380 int fraction, n, count;
8384 /* Slow in, stepping 1/16th, then 1/8th, ... */
8386 for (n = 0; n < factor; n++)
8388 for (n = 0; n < factor; n++) {
8389 frames[count].x = start->x + (mid->x - start->x) / fraction;
8390 frames[count].y = start->y + (mid->y - start->y) / fraction;
8392 fraction = fraction / 2;
8396 frames[count] = *mid;
8399 /* Slow out, stepping 1/2, then 1/4, ... */
8401 for (n = 0; n < factor; n++) {
8402 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8403 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8405 fraction = fraction * 2;
8410 /* Draw a piece on the screen without disturbing what's there */
8413 SelectGCMask(piece, clip, outline, mask)
8414 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8418 /* Bitmap for piece being moved. */
8419 if (appData.monoMode) {
8420 *mask = *pieceToSolid(piece);
8421 } else if (useImages) {
8423 *mask = xpmMask[piece];
8425 *mask = ximMaskPm[piece];
8428 *mask = *pieceToSolid(piece);
8431 /* GC for piece being moved. Square color doesn't matter, but
8432 since it gets modified we make a copy of the original. */
8434 if (appData.monoMode)
8439 if (appData.monoMode)
8444 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8446 /* Outline only used in mono mode and is not modified */
8448 *outline = bwPieceGC;
8450 *outline = wbPieceGC;
8454 OverlayPiece(piece, clip, outline, dest)
8455 ChessSquare piece; GC clip; GC outline; Drawable dest;
8460 /* Draw solid rectangle which will be clipped to shape of piece */
8461 XFillRectangle(xDisplay, dest, clip,
8462 0, 0, squareSize, squareSize);
8463 if (appData.monoMode)
8464 /* Also draw outline in contrasting color for black
8465 on black / white on white cases */
8466 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8467 0, 0, squareSize, squareSize, 0, 0, 1);
8469 /* Copy the piece */
8474 if(appData.upsideDown && flipView) kind ^= 2;
8475 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8477 0, 0, squareSize, squareSize,
8482 /* Animate the movement of a single piece */
8485 BeginAnimation(anim, piece, startColor, start)
8493 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8494 /* The old buffer is initialised with the start square (empty) */
8495 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8496 anim->prevFrame = *start;
8498 /* The piece will be drawn using its own bitmap as a matte */
8499 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8500 XSetClipMask(xDisplay, anim->pieceGC, mask);
8504 AnimationFrame(anim, frame, piece)
8509 XRectangle updates[4];
8514 /* Save what we are about to draw into the new buffer */
8515 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8516 frame->x, frame->y, squareSize, squareSize,
8519 /* Erase bits of the previous frame */
8520 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8521 /* Where the new frame overlapped the previous,
8522 the contents in newBuf are wrong. */
8523 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8524 overlap.x, overlap.y,
8525 overlap.width, overlap.height,
8527 /* Repaint the areas in the old that don't overlap new */
8528 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8529 for (i = 0; i < count; i++)
8530 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8531 updates[i].x - anim->prevFrame.x,
8532 updates[i].y - anim->prevFrame.y,
8533 updates[i].width, updates[i].height,
8534 updates[i].x, updates[i].y);
8536 /* Easy when no overlap */
8537 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8538 0, 0, squareSize, squareSize,
8539 anim->prevFrame.x, anim->prevFrame.y);
8542 /* Save this frame for next time round */
8543 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8544 0, 0, squareSize, squareSize,
8546 anim->prevFrame = *frame;
8548 /* Draw piece over original screen contents, not current,
8549 and copy entire rect. Wipes out overlapping piece images. */
8550 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8551 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8552 0, 0, squareSize, squareSize,
8553 frame->x, frame->y);
8557 EndAnimation (anim, finish)
8561 XRectangle updates[4];
8566 /* The main code will redraw the final square, so we
8567 only need to erase the bits that don't overlap. */
8568 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8569 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8570 for (i = 0; i < count; i++)
8571 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8572 updates[i].x - anim->prevFrame.x,
8573 updates[i].y - anim->prevFrame.y,
8574 updates[i].width, updates[i].height,
8575 updates[i].x, updates[i].y);
8577 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8578 0, 0, squareSize, squareSize,
8579 anim->prevFrame.x, anim->prevFrame.y);
8584 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8586 ChessSquare piece; int startColor;
8587 XPoint * start; XPoint * finish;
8588 XPoint frames[]; int nFrames;
8592 BeginAnimation(anim, piece, startColor, start);
8593 for (n = 0; n < nFrames; n++) {
8594 AnimationFrame(anim, &(frames[n]), piece);
8595 FrameDelay(appData.animSpeed);
8597 EndAnimation(anim, finish);
8601 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8604 ChessSquare piece = board[fromY][toY];
8605 board[fromY][toY] = EmptySquare;
8606 DrawPosition(FALSE, board);
8608 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8609 y = lineGap + toY * (squareSize + lineGap);
8611 x = lineGap + toX * (squareSize + lineGap);
8612 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8614 for(i=1; i<4*kFactor; i++) {
8615 int r = squareSize * 9 * i/(20*kFactor - 5);
8616 XFillArc(xDisplay, xBoardWindow, highlineGC,
8617 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8618 FrameDelay(appData.animSpeed);
8620 board[fromY][toY] = piece;
8623 /* Main control logic for deciding what to animate and how */
8626 AnimateMove(board, fromX, fromY, toX, toY)
8635 XPoint start, finish, mid;
8636 XPoint frames[kFactor * 2 + 1];
8637 int nFrames, startColor, endColor;
8639 /* Are we animating? */
8640 if (!appData.animate || appData.blindfold)
8643 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8644 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8645 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8647 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8648 piece = board[fromY][fromX];
8649 if (piece >= EmptySquare) return;
8654 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8657 if (appData.debugMode) {
8658 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8659 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8660 piece, fromX, fromY, toX, toY); }
8662 ScreenSquare(fromX, fromY, &start, &startColor);
8663 ScreenSquare(toX, toY, &finish, &endColor);
8666 /* Knight: make straight movement then diagonal */
8667 if (abs(toY - fromY) < abs(toX - fromX)) {
8668 mid.x = start.x + (finish.x - start.x) / 2;
8672 mid.y = start.y + (finish.y - start.y) / 2;
8675 mid.x = start.x + (finish.x - start.x) / 2;
8676 mid.y = start.y + (finish.y - start.y) / 2;
8679 /* Don't use as many frames for very short moves */
8680 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8681 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8683 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8684 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8685 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8687 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8688 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8691 /* Be sure end square is redrawn */
8692 damage[0][toY][toX] = True;
8696 DragPieceBegin(x, y, instantly)
8697 int x; int y; Boolean instantly;
8699 int boardX, boardY, color;
8702 /* Are we animating? */
8703 if (!appData.animateDragging || appData.blindfold)
8706 /* Figure out which square we start in and the
8707 mouse position relative to top left corner. */
8708 BoardSquare(x, y, &boardX, &boardY);
8709 player.startBoardX = boardX;
8710 player.startBoardY = boardY;
8711 ScreenSquare(boardX, boardY, &corner, &color);
8712 player.startSquare = corner;
8713 player.startColor = color;
8714 /* As soon as we start dragging, the piece will jump slightly to
8715 be centered over the mouse pointer. */
8716 player.mouseDelta.x = squareSize/2;
8717 player.mouseDelta.y = squareSize/2;
8718 /* Initialise animation */
8719 player.dragPiece = PieceForSquare(boardX, boardY);
8721 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8722 player.dragActive = True;
8723 BeginAnimation(&player, player.dragPiece, color, &corner);
8724 /* Mark this square as needing to be redrawn. Note that
8725 we don't remove the piece though, since logically (ie
8726 as seen by opponent) the move hasn't been made yet. */
8727 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8728 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8729 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8730 corner.x, corner.y, squareSize, squareSize,
8731 0, 0); // [HGM] zh: unstack in stead of grab
8732 if(gatingPiece != EmptySquare) {
8733 /* Kludge alert: When gating we want the introduced
8734 piece to appear on the from square. To generate an
8735 image of it, we draw it on the board, copy the image,
8736 and draw the original piece again. */
8737 ChessSquare piece = boards[currentMove][boardY][boardX];
8738 DrawSquare(boardY, boardX, gatingPiece, 0);
8739 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8740 corner.x, corner.y, squareSize, squareSize, 0, 0);
8741 DrawSquare(boardY, boardX, piece, 0);
8743 damage[0][boardY][boardX] = True;
8745 player.dragActive = False;
8750 ChangeDragPiece(ChessSquare piece)
8753 player.dragPiece = piece;
8754 /* The piece will be drawn using its own bitmap as a matte */
8755 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8756 XSetClipMask(xDisplay, player.pieceGC, mask);
8765 /* Are we animating? */
8766 if (!appData.animateDragging || appData.blindfold)
8770 if (! player.dragActive)
8772 /* Move piece, maintaining same relative position
8773 of mouse within square */
8774 corner.x = x - player.mouseDelta.x;
8775 corner.y = y - player.mouseDelta.y;
8776 AnimationFrame(&player, &corner, player.dragPiece);
8778 if (appData.highlightDragging) {
8780 BoardSquare(x, y, &boardX, &boardY);
8781 SetHighlights(fromX, fromY, boardX, boardY);
8790 int boardX, boardY, color;
8793 /* Are we animating? */
8794 if (!appData.animateDragging || appData.blindfold)
8798 if (! player.dragActive)
8800 /* Last frame in sequence is square piece is
8801 placed on, which may not match mouse exactly. */
8802 BoardSquare(x, y, &boardX, &boardY);
8803 ScreenSquare(boardX, boardY, &corner, &color);
8804 EndAnimation(&player, &corner);
8806 /* Be sure end square is redrawn */
8807 damage[0][boardY][boardX] = True;
8809 /* This prevents weird things happening with fast successive
8810 clicks which on my Sun at least can cause motion events
8811 without corresponding press/release. */
8812 player.dragActive = False;
8815 /* Handle expose event while piece being dragged */
8820 if (!player.dragActive || appData.blindfold)
8823 /* What we're doing: logically, the move hasn't been made yet,
8824 so the piece is still in it's original square. But visually
8825 it's being dragged around the board. So we erase the square
8826 that the piece is on and draw it at the last known drag point. */
8827 BlankSquare(player.startSquare.x, player.startSquare.y,
8828 player.startColor, EmptySquare, xBoardWindow, 1);
8829 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8830 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8833 #include <sys/ioctl.h>
8834 int get_term_width()
8836 int fd, default_width;
8839 default_width = 79; // this is FICS default anyway...
8841 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8843 if (!ioctl(fd, TIOCGSIZE, &win))
8844 default_width = win.ts_cols;
8845 #elif defined(TIOCGWINSZ)
8847 if (!ioctl(fd, TIOCGWINSZ, &win))
8848 default_width = win.ws_col;
8850 return default_width;
8856 static int old_width = 0;
8857 int new_width = get_term_width();
8859 if (old_width != new_width)
8860 ics_printf("set width %d\n", new_width);
8861 old_width = new_width;
8864 void NotifyFrontendLogin()
8869 /* [AS] Arrow highlighting support */
8871 static double A_WIDTH = 5; /* Width of arrow body */
8873 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8874 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8876 static double Sqr( double x )
8881 static int Round( double x )
8883 return (int) (x + 0.5);
8886 void SquareToPos(int rank, int file, int *x, int *y)
8889 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8890 *y = lineGap + rank * (squareSize + lineGap);
8892 *x = lineGap + file * (squareSize + lineGap);
8893 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8897 /* Draw an arrow between two points using current settings */
8898 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8901 double dx, dy, j, k, x, y;
8904 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8906 arrow[0].x = s_x + A_WIDTH + 0.5;
8909 arrow[1].x = s_x + A_WIDTH + 0.5;
8910 arrow[1].y = d_y - h;
8912 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8913 arrow[2].y = d_y - h;
8918 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8919 arrow[5].y = d_y - h;
8921 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8922 arrow[4].y = d_y - h;
8924 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8927 else if( d_y == s_y ) {
8928 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8931 arrow[0].y = s_y + A_WIDTH + 0.5;
8933 arrow[1].x = d_x - w;
8934 arrow[1].y = s_y + A_WIDTH + 0.5;
8936 arrow[2].x = d_x - w;
8937 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8942 arrow[5].x = d_x - w;
8943 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8945 arrow[4].x = d_x - w;
8946 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8949 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8952 /* [AS] Needed a lot of paper for this! :-) */
8953 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8954 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8956 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8958 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8963 arrow[0].x = Round(x - j);
8964 arrow[0].y = Round(y + j*dx);
8966 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8967 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8970 x = (double) d_x - k;
8971 y = (double) d_y - k*dy;
8974 x = (double) d_x + k;
8975 y = (double) d_y + k*dy;
8978 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8980 arrow[6].x = Round(x - j);
8981 arrow[6].y = Round(y + j*dx);
8983 arrow[2].x = Round(arrow[6].x + 2*j);
8984 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8986 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8987 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8992 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8993 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8996 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8997 // Polygon( hdc, arrow, 7 );
9000 /* [AS] Draw an arrow between two squares */
9001 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9003 int s_x, s_y, d_x, d_y, hor, vert, i;
9005 if( s_col == d_col && s_row == d_row ) {
9009 /* Get source and destination points */
9010 SquareToPos( s_row, s_col, &s_x, &s_y);
9011 SquareToPos( d_row, d_col, &d_x, &d_y);
9014 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9016 else if( d_y < s_y ) {
9017 d_y += squareSize / 2 + squareSize / 4;
9020 d_y += squareSize / 2;
9024 d_x += squareSize / 2 - squareSize / 4;
9026 else if( d_x < s_x ) {
9027 d_x += squareSize / 2 + squareSize / 4;
9030 d_x += squareSize / 2;
9033 s_x += squareSize / 2;
9034 s_y += squareSize / 2;
9037 A_WIDTH = squareSize / 14.; //[HGM] make float
9039 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9041 hor = 64*s_col + 32; vert = 64*s_row + 32;
9042 for(i=0; i<= 64; i++) {
9043 damage[0][vert+6>>6][hor+6>>6] = True;
9044 damage[0][vert-6>>6][hor+6>>6] = True;
9045 damage[0][vert+6>>6][hor-6>>6] = True;
9046 damage[0][vert-6>>6][hor-6>>6] = True;
9047 hor += d_col - s_col; vert += d_row - s_row;
9051 Boolean IsDrawArrowEnabled()
9053 return appData.highlightMoveWithArrow && squareSize >= 32;
9056 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9058 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9059 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9062 void UpdateLogos(int displ)
9064 return; // no logos in XBoard yet