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)
245 int main P((int argc, char **argv));
246 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
247 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
248 RETSIGTYPE CmailSigHandler P((int sig));
249 RETSIGTYPE IntSigHandler P((int sig));
250 RETSIGTYPE TermSizeSigHandler P((int sig));
251 void CreateGCs P((int redo));
252 void CreateAnyPieces P((void));
253 void CreateXIMPieces P((void));
254 void CreateXPMPieces P((void));
255 void CreateXPMBoard P((char *s, int n));
256 void CreatePieces P((void));
257 void CreatePieceMenus P((void));
258 Widget CreateMenuBar P((Menu *mb, int boardWidth));
259 Widget CreateButtonBar P ((MenuItem *mi));
261 char *InsertPxlSize P((char *pattern, int targetPxlSize));
262 XFontSet CreateFontSet P((char *base_fnt_lst));
264 char *FindFont P((char *pattern, int targetPxlSize));
266 void PieceMenuPopup P((Widget w, XEvent *event,
267 String *params, Cardinal *num_params));
268 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
269 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
270 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
271 u_int wreq, u_int hreq));
272 void CreateGrid P((void));
273 int EventToSquare P((int x, int limit));
274 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
275 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
276 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
277 void HandleUserMove P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AnimateUserMove P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void HandlePV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void SelectPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void StopPV P((Widget w, XEvent * event,
286 String * params, Cardinal * nParams));
287 void WhiteClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void BlackClock P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void DrawPositionProc P((Widget w, XEvent *event,
292 String *prms, Cardinal *nprms));
293 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
295 void CommentClick P((Widget w, XEvent * event,
296 String * params, Cardinal * nParams));
297 void CommentPopUp P((char *title, char *label));
298 void CommentPopDown P((void));
299 void ICSInputBoxPopUp P((void));
300 void ICSInputBoxPopDown P((void));
301 void FileNamePopUp P((char *label, char *def, char *filter,
302 FileProc proc, char *openMode));
303 void FileNamePopDown P((void));
304 void FileNameCallback P((Widget w, XtPointer client_data,
305 XtPointer call_data));
306 void FileNameAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionReplyAction P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionProc P((Widget w, XEvent *event,
311 String *prms, Cardinal *nprms));
312 void AskQuestionPopDown P((void));
313 void PromotionPopDown P((void));
314 void PromotionCallback P((Widget w, XtPointer client_data,
315 XtPointer call_data));
316 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
317 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
318 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
321 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
323 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
325 void LoadPositionProc P((Widget w, XEvent *event,
326 String *prms, Cardinal *nprms));
327 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
329 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
331 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
333 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
335 void PastePositionProc P((Widget w, XEvent *event, String *prms,
337 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
340 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
341 void SavePositionProc P((Widget w, XEvent *event,
342 String *prms, Cardinal *nprms));
343 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
344 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
346 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
347 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
350 void MachineWhiteProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeModeProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void AnalyzeFileProc P((Widget w, XEvent *event,
355 String *prms, Cardinal *nprms));
356 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
358 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void IcsClientProc P((Widget w, XEvent *event, String *prms,
362 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void EditPositionProc P((Widget w, XEvent *event,
364 String *prms, Cardinal *nprms));
365 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void EditCommentProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void IcsInputBoxProc P((Widget w, XEvent *event,
369 String *prms, Cardinal *nprms));
370 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void StopObservingProc P((Widget w, XEvent *event, String *prms,
387 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
389 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 Boolean TempBackwardActive = False;
395 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
396 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
401 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
403 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
404 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
406 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
408 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
410 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
412 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
413 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
415 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
416 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
418 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
420 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
422 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
424 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
425 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
427 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
429 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
431 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
433 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
436 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
438 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
440 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
442 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void DisplayMove P((int moveNumber));
453 void DisplayTitle P((char *title));
454 void ICSInitScript P((void));
455 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
456 void ErrorPopUp P((char *title, char *text, int modal));
457 void ErrorPopDown P((void));
458 static char *ExpandPathName P((char *path));
459 static void CreateAnimVars P((void));
460 static void DragPieceMove P((int x, int y));
461 static void DrawDragPiece P((void));
462 char *ModeToWidgetName P((GameMode mode));
463 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
477 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
478 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
479 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
480 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
481 void GameListOptionsPopDown P(());
482 void GenericPopDown P(());
483 void update_ics_width P(());
484 int get_term_width P(());
485 int CopyMemoProc P(());
486 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
487 Boolean IsDrawArrowEnabled P(());
490 * XBoard depends on Xt R4 or higher
492 int xtVersion = XtSpecificationRelease;
497 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
498 jailSquareColor, highlightSquareColor, premoveHighlightColor;
499 Pixel lowTimeWarningColor;
500 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
501 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
502 wjPieceGC, bjPieceGC, prelineGC, countGC;
503 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
504 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
505 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
506 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
507 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
508 ICSInputShell, fileNameShell, askQuestionShell;
509 Widget historyShell, evalGraphShell, gameListShell;
510 int hOffset; // [HGM] dual
511 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
512 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
513 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
515 XFontSet fontSet, clockFontSet;
518 XFontStruct *clockFontStruct;
520 Font coordFontID, countFontID;
521 XFontStruct *coordFontStruct, *countFontStruct;
522 XtAppContext appContext;
524 char *oldICSInteractionTitle;
528 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
530 Position commentX = -1, commentY = -1;
531 Dimension commentW, commentH;
532 typedef unsigned int BoardSize;
534 Boolean chessProgram;
536 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
537 int squareSize, smallLayout = 0, tinyLayout = 0,
538 marginW, marginH, // [HGM] for run-time resizing
539 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
540 ICSInputBoxUp = False, askQuestionUp = False,
541 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
542 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
543 Pixel timerForegroundPixel, timerBackgroundPixel;
544 Pixel buttonForegroundPixel, buttonBackgroundPixel;
545 char *chessDir, *programName, *programVersion,
546 *gameCopyFilename, *gamePasteFilename;
547 Boolean alwaysOnTop = False;
548 Boolean saveSettingsOnExit;
549 char *settingsFileName;
550 char *icsTextMenuString;
552 char *firstChessProgramNames;
553 char *secondChessProgramNames;
555 WindowPlacement wpMain;
556 WindowPlacement wpConsole;
557 WindowPlacement wpComment;
558 WindowPlacement wpMoveHistory;
559 WindowPlacement wpEvalGraph;
560 WindowPlacement wpEngineOutput;
561 WindowPlacement wpGameList;
562 WindowPlacement wpTags;
564 extern Widget shells[];
565 extern Boolean shellUp[];
569 Pixmap pieceBitmap[2][(int)BlackPawn];
570 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
571 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
572 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
573 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
574 Pixmap xpmBoardBitmap[2];
575 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
576 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
577 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
578 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
579 XImage *ximLightSquare, *ximDarkSquare;
582 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
583 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
585 #define White(piece) ((int)(piece) < (int)BlackPawn)
587 /* Variables for doing smooth animation. This whole thing
588 would be much easier if the board was double-buffered,
589 but that would require a fairly major rewrite. */
594 GC blitGC, pieceGC, outlineGC;
595 XPoint startSquare, prevFrame, mouseDelta;
599 int startBoardX, startBoardY;
602 /* There can be two pieces being animated at once: a player
603 can begin dragging a piece before the remote opponent has moved. */
605 static AnimState game, player;
607 /* Bitmaps for use as masks when drawing XPM pieces.
608 Need one for each black and white piece. */
609 static Pixmap xpmMask[BlackKing + 1];
611 /* This magic number is the number of intermediate frames used
612 in each half of the animation. For short moves it's reduced
613 by 1. The total number of frames will be factor * 2 + 1. */
616 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
618 MenuItem fileMenu[] = {
619 {N_("New Game Ctrl+N"), "New Game", ResetProc},
620 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
621 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
622 {"----", NULL, NothingProc},
623 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
624 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
625 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
626 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
627 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
628 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
629 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
630 {"----", NULL, NothingProc},
631 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
632 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
633 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
634 {"----", NULL, NothingProc},
635 {N_("Mail Move"), "Mail Move", MailMoveProc},
636 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
637 {"----", NULL, NothingProc},
638 {N_("Quit Ctr+Q"), "Exit", QuitProc},
642 MenuItem editMenu[] = {
643 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
644 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
645 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
646 {"----", NULL, NothingProc},
647 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
648 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
649 {"----", NULL, NothingProc},
650 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
651 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
652 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
653 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
654 {N_("Edit Book"), "Edit Book", EditBookProc},
655 {"----", NULL, NothingProc},
656 {N_("Revert Home"), "Revert", RevertProc},
657 {N_("Annotate"), "Annotate", AnnotateProc},
658 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
659 {"----", NULL, NothingProc},
660 {N_("Backward Alt+Left"), "Backward", BackwardProc},
661 {N_("Forward Alt+Right"), "Forward", ForwardProc},
662 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
663 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
667 MenuItem viewMenu[] = {
668 {N_("Flip View F2"), "Flip View", FlipViewProc},
669 {"----", NULL, NothingProc},
670 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
671 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
672 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
673 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
674 {N_("ICS text menu"), "ICStex", IcsTextProc},
675 {"----", NULL, NothingProc},
676 {N_("Tags"), "Show Tags", EditTagsProc},
677 {N_("Comments"), "Show Comments", EditCommentProc},
678 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
679 {"----", NULL, NothingProc},
680 {N_("Board..."), "Board Options", BoardOptionsProc},
681 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
685 MenuItem modeMenu[] = {
686 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
687 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
688 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
689 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
690 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
691 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
692 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
693 {N_("Training"), "Training", TrainingProc},
694 {N_("ICS Client"), "ICS Client", IcsClientProc},
695 {"----", NULL, NothingProc},
696 {N_("Machine Match"), "Machine Match", MatchProc},
697 {N_("Pause Pause"), "Pause", PauseProc},
701 MenuItem actionMenu[] = {
702 {N_("Accept F3"), "Accept", AcceptProc},
703 {N_("Decline F4"), "Decline", DeclineProc},
704 {N_("Rematch F12"), "Rematch", RematchProc},
705 {"----", NULL, NothingProc},
706 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
707 {N_("Draw F6"), "Draw", DrawProc},
708 {N_("Adjourn F7"), "Adjourn", AdjournProc},
709 {N_("Abort F8"),"Abort", AbortProc},
710 {N_("Resign F9"), "Resign", ResignProc},
711 {"----", NULL, NothingProc},
712 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
713 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
714 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
715 {"----", NULL, NothingProc},
716 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
717 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
718 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
722 MenuItem engineMenu[] = {
723 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
724 {"----", NULL, NothingProc},
725 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
726 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
727 {"----", NULL, NothingProc},
728 {N_("Hint"), "Hint", HintProc},
729 {N_("Book"), "Book", BookProc},
730 {"----", NULL, NothingProc},
731 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
732 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
736 MenuItem optionsMenu[] = {
737 #define OPTIONSDIALOG
739 {N_("General ..."), "General", OptionsProc},
741 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
742 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
743 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
744 {N_("ICS ..."), "ICS", IcsOptionsProc},
745 {N_("Match ..."), "Match", MatchOptionsProc},
746 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
747 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
748 // {N_(" ..."), "", OptionsProc},
749 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
750 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
751 {"----", NULL, NothingProc},
752 #ifndef OPTIONSDIALOG
753 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
754 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
755 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
756 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
757 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
758 {N_("Blindfold"), "Blindfold", BlindfoldProc},
759 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
761 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
763 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
764 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
765 {N_("Move Sound"), "Move Sound", MoveSoundProc},
766 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
767 {N_("One-Click Moving"), "OneClick", OneClickProc},
768 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
769 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
770 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
771 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
772 // {N_("Premove"), "Premove", PremoveProc},
773 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
774 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
775 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
776 {"----", NULL, NothingProc},
778 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
779 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
783 MenuItem helpMenu[] = {
784 {N_("Info XBoard"), "Info XBoard", InfoProc},
785 {N_("Man XBoard F1"), "Man XBoard", ManProc},
786 {"----", NULL, NothingProc},
787 {N_("About XBoard"), "About XBoard", AboutProc},
792 {N_("File"), "File", fileMenu},
793 {N_("Edit"), "Edit", editMenu},
794 {N_("View"), "View", viewMenu},
795 {N_("Mode"), "Mode", modeMenu},
796 {N_("Action"), "Action", actionMenu},
797 {N_("Engine"), "Engine", engineMenu},
798 {N_("Options"), "Options", optionsMenu},
799 {N_("Help"), "Help", helpMenu},
803 #define PAUSE_BUTTON "P"
804 MenuItem buttonBar[] = {
805 {"<<", "<<", ToStartProc},
806 {"<", "<", BackwardProc},
807 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
808 {">", ">", ForwardProc},
809 {">>", ">>", ToEndProc},
813 #define PIECE_MENU_SIZE 18
814 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
815 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
816 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
817 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
818 N_("Empty square"), N_("Clear board") },
819 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
820 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
821 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
822 N_("Empty square"), N_("Clear board") }
824 /* must be in same order as pieceMenuStrings! */
825 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
826 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
827 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
828 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
829 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
830 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
831 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
832 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
833 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
836 #define DROP_MENU_SIZE 6
837 String dropMenuStrings[DROP_MENU_SIZE] = {
838 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
840 /* must be in same order as dropMenuStrings! */
841 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
842 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
843 WhiteRook, WhiteQueen
851 DropMenuEnables dmEnables[] = {
869 { XtNborderWidth, 0 },
870 { XtNdefaultDistance, 0 },
874 { XtNborderWidth, 0 },
875 { XtNresizable, (XtArgVal) True },
879 { XtNborderWidth, 0 },
885 { XtNjustify, (XtArgVal) XtJustifyRight },
886 { XtNlabel, (XtArgVal) "..." },
887 { XtNresizable, (XtArgVal) True },
888 { XtNresize, (XtArgVal) False }
891 Arg messageArgs[] = {
892 { XtNjustify, (XtArgVal) XtJustifyLeft },
893 { XtNlabel, (XtArgVal) "..." },
894 { XtNresizable, (XtArgVal) True },
895 { XtNresize, (XtArgVal) False }
899 { XtNborderWidth, 0 },
900 { XtNjustify, (XtArgVal) XtJustifyLeft }
903 XtResource clientResources[] = {
904 { "flashCount", "flashCount", XtRInt, sizeof(int),
905 XtOffset(AppDataPtr, flashCount), XtRImmediate,
906 (XtPointer) FLASH_COUNT },
909 XrmOptionDescRec shellOptions[] = {
910 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
911 { "-flash", "flashCount", XrmoptionNoArg, "3" },
912 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
915 XtActionsRec boardActions[] = {
916 { "DrawPosition", DrawPositionProc },
917 { "HandleUserMove", HandleUserMove },
918 { "AnimateUserMove", AnimateUserMove },
919 { "HandlePV", HandlePV },
920 { "SelectPV", SelectPV },
921 { "StopPV", StopPV },
922 { "FileNameAction", FileNameAction },
923 { "AskQuestionProc", AskQuestionProc },
924 { "AskQuestionReplyAction", AskQuestionReplyAction },
925 { "PieceMenuPopup", PieceMenuPopup },
926 { "WhiteClock", WhiteClock },
927 { "BlackClock", BlackClock },
928 { "ResetProc", ResetProc },
929 { "NewVariantProc", NewVariantProc },
930 { "LoadGameProc", LoadGameProc },
931 { "LoadNextGameProc", LoadNextGameProc },
932 { "LoadPrevGameProc", LoadPrevGameProc },
933 { "LoadSelectedProc", LoadSelectedProc },
934 { "SetFilterProc", SetFilterProc },
935 { "ReloadGameProc", ReloadGameProc },
936 { "LoadPositionProc", LoadPositionProc },
937 { "LoadNextPositionProc", LoadNextPositionProc },
938 { "LoadPrevPositionProc", LoadPrevPositionProc },
939 { "ReloadPositionProc", ReloadPositionProc },
940 { "CopyPositionProc", CopyPositionProc },
941 { "PastePositionProc", PastePositionProc },
942 { "CopyGameProc", CopyGameProc },
943 { "CopyGameListProc", CopyGameListProc },
944 { "PasteGameProc", PasteGameProc },
945 { "SaveGameProc", SaveGameProc },
946 { "SavePositionProc", SavePositionProc },
947 { "MailMoveProc", MailMoveProc },
948 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
949 { "QuitProc", QuitProc },
950 { "MachineWhiteProc", MachineWhiteProc },
951 { "MachineBlackProc", MachineBlackProc },
952 { "AnalysisModeProc", AnalyzeModeProc },
953 { "AnalyzeFileProc", AnalyzeFileProc },
954 { "TwoMachinesProc", TwoMachinesProc },
955 { "IcsClientProc", IcsClientProc },
956 { "EditGameProc", EditGameProc },
957 { "EditPositionProc", EditPositionProc },
958 { "TrainingProc", EditPositionProc },
959 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
960 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
961 { "ShowGameListProc", ShowGameListProc },
962 { "ShowMoveListProc", HistoryShowProc},
963 { "EditTagsProc", EditCommentProc },
964 { "EditBookProc", EditBookProc },
965 { "EditCommentProc", EditCommentProc },
966 { "IcsInputBoxProc", IcsInputBoxProc },
967 { "PauseProc", PauseProc },
968 { "AcceptProc", AcceptProc },
969 { "DeclineProc", DeclineProc },
970 { "RematchProc", RematchProc },
971 { "CallFlagProc", CallFlagProc },
972 { "DrawProc", DrawProc },
973 { "AdjournProc", AdjournProc },
974 { "AbortProc", AbortProc },
975 { "ResignProc", ResignProc },
976 { "AdjuWhiteProc", AdjuWhiteProc },
977 { "AdjuBlackProc", AdjuBlackProc },
978 { "AdjuDrawProc", AdjuDrawProc },
979 { "TypeInProc", TypeInProc },
980 { "EnterKeyProc", EnterKeyProc },
981 { "UpKeyProc", UpKeyProc },
982 { "DownKeyProc", DownKeyProc },
983 { "StopObservingProc", StopObservingProc },
984 { "StopExaminingProc", StopExaminingProc },
985 { "UploadProc", UploadProc },
986 { "BackwardProc", BackwardProc },
987 { "ForwardProc", ForwardProc },
988 { "TempBackwardProc", TempBackwardProc },
989 { "TempForwardProc", TempForwardProc },
990 { "ToStartProc", ToStartProc },
991 { "ToEndProc", ToEndProc },
992 { "RevertProc", RevertProc },
993 { "AnnotateProc", AnnotateProc },
994 { "TruncateGameProc", TruncateGameProc },
995 { "MoveNowProc", MoveNowProc },
996 { "RetractMoveProc", RetractMoveProc },
997 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
998 { "UciMenuProc", (XtActionProc) UciMenuProc },
999 { "TimeControlProc", (XtActionProc) TimeControlProc },
1000 { "FlipViewProc", FlipViewProc },
1001 { "PonderNextMoveProc", PonderNextMoveProc },
1002 #ifndef OPTIONSDIALOG
1003 { "AlwaysQueenProc", AlwaysQueenProc },
1004 { "AnimateDraggingProc", AnimateDraggingProc },
1005 { "AnimateMovingProc", AnimateMovingProc },
1006 { "AutoflagProc", AutoflagProc },
1007 { "AutoflipProc", AutoflipProc },
1008 { "BlindfoldProc", BlindfoldProc },
1009 { "FlashMovesProc", FlashMovesProc },
1011 { "HighlightDraggingProc", HighlightDraggingProc },
1013 { "HighlightLastMoveProc", HighlightLastMoveProc },
1014 // { "IcsAlarmProc", IcsAlarmProc },
1015 { "MoveSoundProc", MoveSoundProc },
1016 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1017 { "PopupExitMessageProc", PopupExitMessageProc },
1018 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1019 // { "PremoveProc", PremoveProc },
1020 { "ShowCoordsProc", ShowCoordsProc },
1021 { "ShowThinkingProc", ShowThinkingProc },
1022 { "HideThinkingProc", HideThinkingProc },
1023 { "TestLegalityProc", TestLegalityProc },
1025 { "SaveSettingsProc", SaveSettingsProc },
1026 { "SaveOnExitProc", SaveOnExitProc },
1027 { "InfoProc", InfoProc },
1028 { "ManProc", ManProc },
1029 { "HintProc", HintProc },
1030 { "BookProc", BookProc },
1031 { "AboutGameProc", AboutGameProc },
1032 { "AboutProc", AboutProc },
1033 { "DebugProc", DebugProc },
1034 { "NothingProc", NothingProc },
1035 { "CommentClick", (XtActionProc) CommentClick },
1036 { "CommentPopDown", (XtActionProc) CommentPopDown },
1037 { "TagsPopDown", (XtActionProc) TagsPopDown },
1038 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1039 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1040 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1041 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1042 { "GameListPopDown", (XtActionProc) GameListPopDown },
1043 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1044 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1045 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1046 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1047 { "GenericPopDown", (XtActionProc) GenericPopDown },
1048 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1049 { "SelectMove", (XtActionProc) SelectMove },
1052 char globalTranslations[] =
1053 ":<Key>F9: ResignProc() \n \
1054 :Ctrl<Key>n: ResetProc() \n \
1055 :Meta<Key>V: NewVariantProc() \n \
1056 :Ctrl<Key>o: LoadGameProc() \n \
1057 :Meta<Key>Next: LoadNextGameProc() \n \
1058 :Meta<Key>Prior: LoadPrevGameProc() \n \
1059 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1060 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1061 :Ctrl<Key>s: SaveGameProc() \n \
1062 :Ctrl<Key>c: CopyGameProc() \n \
1063 :Ctrl<Key>v: PasteGameProc() \n \
1064 :Ctrl<Key>O: LoadPositionProc() \n \
1065 :Shift<Key>Next: LoadNextPositionProc() \n \
1066 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1067 :Ctrl<Key>S: SavePositionProc() \n \
1068 :Ctrl<Key>C: CopyPositionProc() \n \
1069 :Ctrl<Key>V: PastePositionProc() \n \
1070 :Ctrl<Key>q: QuitProc() \n \
1071 :Ctrl<Key>w: MachineWhiteProc() \n \
1072 :Ctrl<Key>b: MachineBlackProc() \n \
1073 :Ctrl<Key>t: TwoMachinesProc() \n \
1074 :Ctrl<Key>a: AnalysisModeProc() \n \
1075 :Ctrl<Key>g: AnalyzeFileProc() \n \
1076 :Ctrl<Key>e: EditGameProc() \n \
1077 :Ctrl<Key>E: EditPositionProc() \n \
1078 :Meta<Key>O: EngineOutputProc() \n \
1079 :Meta<Key>E: EvalGraphProc() \n \
1080 :Meta<Key>G: ShowGameListProc() \n \
1081 :Meta<Key>H: ShowMoveListProc() \n \
1082 :<Key>Pause: PauseProc() \n \
1083 :<Key>F3: AcceptProc() \n \
1084 :<Key>F4: DeclineProc() \n \
1085 :<Key>F12: RematchProc() \n \
1086 :<Key>F5: CallFlagProc() \n \
1087 :<Key>F6: DrawProc() \n \
1088 :<Key>F7: AdjournProc() \n \
1089 :<Key>F8: AbortProc() \n \
1090 :<Key>F10: StopObservingProc() \n \
1091 :<Key>F11: StopExaminingProc() \n \
1092 :Meta Ctrl<Key>F12: DebugProc() \n \
1093 :Meta<Key>End: ToEndProc() \n \
1094 :Meta<Key>Right: ForwardProc() \n \
1095 :Meta<Key>Home: ToStartProc() \n \
1096 :Meta<Key>Left: BackwardProc() \n \
1097 :<Key>Left: BackwardProc() \n \
1098 :<Key>Right: ForwardProc() \n \
1099 :<Key>Home: RevertProc() \n \
1100 :<Key>End: TruncateGameProc() \n \
1101 :Ctrl<Key>m: MoveNowProc() \n \
1102 :Ctrl<Key>x: RetractMoveProc() \n \
1103 :Meta<Key>J: EngineMenuProc() \n \
1104 :Meta<Key>U: UciMenuProc() \n \
1105 :Meta<Key>T: TimeControlProc() \n \
1106 :Ctrl<Key>P: PonderNextMoveProc() \n "
1107 #ifndef OPTIONSDIALOG
1109 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1110 :Ctrl<Key>F: AutoflagProc() \n \
1111 :Ctrl<Key>A: AnimateMovingProc() \n \
1112 :Ctrl<Key>L: TestLegalityProc() \n \
1113 :Ctrl<Key>H: HideThinkingProc() \n "
1116 :<Key>F1: ManProc() \n \
1117 :<Key>F2: FlipViewProc() \n \
1118 :<KeyDown>Return: TempBackwardProc() \n \
1119 :<KeyUp>Return: TempForwardProc() \n \
1120 :Ctrl<Key>1: AskQuestionProc(\"Direct command\",\
1121 \"Send to chess program:\",,1) \n \
1122 :Ctrl<Key>2: AskQuestionProc(\"Direct command\",\
1123 \"Send to second chess program:\",,2) \n";
1125 char boardTranslations[] =
1126 "<Btn1Down>: HandleUserMove(0) \n \
1127 Shift<Btn1Up>: HandleUserMove(1) \n \
1128 <Btn1Up>: HandleUserMove(0) \n \
1129 <Btn1Motion>: AnimateUserMove() \n \
1130 <Btn3Motion>: HandlePV() \n \
1131 <Btn3Up>: PieceMenuPopup(menuB) \n \
1132 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1133 PieceMenuPopup(menuB) \n \
1134 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1135 PieceMenuPopup(menuW) \n \
1136 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1137 PieceMenuPopup(menuW) \n \
1138 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1139 PieceMenuPopup(menuB) \n";
1141 char whiteTranslations[] =
1142 "Shift<BtnDown>: WhiteClock(1)\n \
1143 <BtnDown>: WhiteClock(0)\n";
1144 char blackTranslations[] =
1145 "Shift<BtnDown>: BlackClock(1)\n \
1146 <BtnDown>: BlackClock(0)\n";
1148 char ICSInputTranslations[] =
1149 "<Key>Up: UpKeyProc() \n "
1150 "<Key>Down: DownKeyProc() \n "
1151 "<Key>Return: EnterKeyProc() \n";
1153 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1154 // as the widget is destroyed before the up-click can call extend-end
1155 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1157 String xboardResources[] = {
1158 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1159 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1160 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1165 /* Max possible square size */
1166 #define MAXSQSIZE 256
1168 static int xpm_avail[MAXSQSIZE];
1170 #ifdef HAVE_DIR_STRUCT
1172 /* Extract piece size from filename */
1174 xpm_getsize(name, len, ext)
1185 if ((p=strchr(name, '.')) == NULL ||
1186 StrCaseCmp(p+1, ext) != 0)
1192 while (*p && isdigit(*p))
1199 /* Setup xpm_avail */
1201 xpm_getavail(dirname, ext)
1209 for (i=0; i<MAXSQSIZE; ++i)
1212 if (appData.debugMode)
1213 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1215 dir = opendir(dirname);
1218 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1219 programName, dirname);
1223 while ((ent=readdir(dir)) != NULL) {
1224 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1225 if (i > 0 && i < MAXSQSIZE)
1235 xpm_print_avail(fp, ext)
1241 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1242 for (i=1; i<MAXSQSIZE; ++i) {
1248 /* Return XPM piecesize closest to size */
1250 xpm_closest_to(dirname, size, ext)
1256 int sm_diff = MAXSQSIZE;
1260 xpm_getavail(dirname, ext);
1262 if (appData.debugMode)
1263 xpm_print_avail(stderr, ext);
1265 for (i=1; i<MAXSQSIZE; ++i) {
1268 diff = (diff<0) ? -diff : diff;
1269 if (diff < sm_diff) {
1277 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1283 #else /* !HAVE_DIR_STRUCT */
1284 /* If we are on a system without a DIR struct, we can't
1285 read the directory, so we can't collect a list of
1286 filenames, etc., so we can't do any size-fitting. */
1288 xpm_closest_to(dirname, size, ext)
1293 fprintf(stderr, _("\
1294 Warning: No DIR structure found on this system --\n\
1295 Unable to autosize for XPM/XIM pieces.\n\
1296 Please report this error to %s.\n\
1297 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1300 #endif /* HAVE_DIR_STRUCT */
1302 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1303 "magenta", "cyan", "white" };
1307 TextColors textColors[(int)NColorClasses];
1309 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1311 parse_color(str, which)
1315 char *p, buf[100], *d;
1318 if (strlen(str) > 99) /* watch bounds on buf */
1323 for (i=0; i<which; ++i) {
1330 /* Could be looking at something like:
1332 .. in which case we want to stop on a comma also */
1333 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1337 return -1; /* Use default for empty field */
1340 if (which == 2 || isdigit(*p))
1343 while (*p && isalpha(*p))
1348 for (i=0; i<8; ++i) {
1349 if (!StrCaseCmp(buf, cnames[i]))
1350 return which? (i+40) : (i+30);
1352 if (!StrCaseCmp(buf, "default")) return -1;
1354 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1359 parse_cpair(cc, str)
1363 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1364 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1369 /* bg and attr are optional */
1370 textColors[(int)cc].bg = parse_color(str, 1);
1371 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1372 textColors[(int)cc].attr = 0;
1378 /* Arrange to catch delete-window events */
1379 Atom wm_delete_window;
1381 CatchDeleteWindow(Widget w, String procname)
1384 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1385 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1386 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1393 XtSetArg(args[0], XtNiconic, False);
1394 XtSetValues(shellWidget, args, 1);
1396 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1399 //---------------------------------------------------------------------------------------------------------
1400 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1403 #define CW_USEDEFAULT (1<<31)
1404 #define ICS_TEXT_MENU_SIZE 90
1405 #define DEBUG_FILE "xboard.debug"
1406 #define SetCurrentDirectory chdir
1407 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1411 // these two must some day move to frontend.h, when they are implemented
1412 Boolean GameListIsUp();
1414 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1417 // front-end part of option handling
1419 // [HGM] This platform-dependent table provides the location for storing the color info
1420 extern char *crWhite, * crBlack;
1424 &appData.whitePieceColor,
1425 &appData.blackPieceColor,
1426 &appData.lightSquareColor,
1427 &appData.darkSquareColor,
1428 &appData.highlightSquareColor,
1429 &appData.premoveHighlightColor,
1430 &appData.lowTimeWarningColor,
1441 // [HGM] font: keep a font for each square size, even non-stndard ones
1442 #define NUM_SIZES 18
1443 #define MAX_SIZE 130
1444 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1445 char *fontTable[NUM_FONTS][MAX_SIZE];
1448 ParseFont(char *name, int number)
1449 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1451 if(sscanf(name, "size%d:", &size)) {
1452 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1453 // defer processing it until we know if it matches our board size
1454 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1455 fontTable[number][size] = strdup(strchr(name, ':')+1);
1456 fontValid[number][size] = True;
1461 case 0: // CLOCK_FONT
1462 appData.clockFont = strdup(name);
1464 case 1: // MESSAGE_FONT
1465 appData.font = strdup(name);
1467 case 2: // COORD_FONT
1468 appData.coordFont = strdup(name);
1473 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1478 { // only 2 fonts currently
1479 appData.clockFont = CLOCK_FONT_NAME;
1480 appData.coordFont = COORD_FONT_NAME;
1481 appData.font = DEFAULT_FONT_NAME;
1486 { // no-op, until we identify the code for this already in XBoard and move it here
1490 ParseColor(int n, char *name)
1491 { // in XBoard, just copy the color-name string
1492 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1496 ParseTextAttribs(ColorClass cc, char *s)
1498 (&appData.colorShout)[cc] = strdup(s);
1502 ParseBoardSize(void *addr, char *name)
1504 appData.boardSize = strdup(name);
1509 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1513 SetCommPortDefaults()
1514 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1517 // [HGM] args: these three cases taken out to stay in front-end
1519 SaveFontArg(FILE *f, ArgDescriptor *ad)
1522 int i, n = (int)(intptr_t)ad->argLoc;
1524 case 0: // CLOCK_FONT
1525 name = appData.clockFont;
1527 case 1: // MESSAGE_FONT
1528 name = appData.font;
1530 case 2: // COORD_FONT
1531 name = appData.coordFont;
1536 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1537 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1538 fontTable[n][squareSize] = strdup(name);
1539 fontValid[n][squareSize] = True;
1542 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1543 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1548 { // nothing to do, as the sounds are at all times represented by their text-string names already
1552 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1553 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1554 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1558 SaveColor(FILE *f, ArgDescriptor *ad)
1559 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1560 if(colorVariable[(int)(intptr_t)ad->argLoc])
1561 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1565 SaveBoardSize(FILE *f, char *name, void *addr)
1566 { // wrapper to shield back-end from BoardSize & sizeInfo
1567 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1571 ParseCommPortSettings(char *s)
1572 { // no such option in XBoard (yet)
1575 extern Widget engineOutputShell;
1578 GetActualPlacement(Widget wg, WindowPlacement *wp)
1588 XtSetArg(args[i], XtNx, &x); i++;
1589 XtSetArg(args[i], XtNy, &y); i++;
1590 XtSetArg(args[i], XtNwidth, &w); i++;
1591 XtSetArg(args[i], XtNheight, &h); i++;
1592 XtGetValues(wg, args, i);
1601 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1602 // In XBoard this will have to wait until awareness of window parameters is implemented
1603 GetActualPlacement(shellWidget, &wpMain);
1604 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1605 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1606 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1607 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1608 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1609 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1613 PrintCommPortSettings(FILE *f, char *name)
1614 { // This option does not exist in XBoard
1618 MySearchPath(char *installDir, char *name, char *fullname)
1619 { // just append installDir and name. Perhaps ExpandPath should be used here?
1620 name = ExpandPathName(name);
1621 if(name && name[0] == '/')
1622 safeStrCpy(fullname, name, MSG_SIZ );
1624 sprintf(fullname, "%s%c%s", installDir, '/', name);
1630 MyGetFullPathName(char *name, char *fullname)
1631 { // should use ExpandPath?
1632 name = ExpandPathName(name);
1633 safeStrCpy(fullname, name, MSG_SIZ );
1638 EnsureOnScreen(int *x, int *y, int minX, int minY)
1645 { // [HGM] args: allows testing if main window is realized from back-end
1646 return xBoardWindow != 0;
1650 PopUpStartupDialog()
1651 { // start menu not implemented in XBoard
1655 ConvertToLine(int argc, char **argv)
1657 static char line[128*1024], buf[1024];
1661 for(i=1; i<argc; i++)
1663 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1664 && argv[i][0] != '{' )
1665 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1667 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1668 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1671 line[strlen(line)-1] = NULLCHAR;
1675 //--------------------------------------------------------------------------------------------
1677 extern Boolean twoBoards, partnerUp;
1680 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1682 #define BoardSize int
1683 void InitDrawingSizes(BoardSize boardSize, int flags)
1684 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1685 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1687 XtGeometryResult gres;
1689 static Dimension oldWidth, oldHeight;
1690 static VariantClass oldVariant;
1691 static int oldDual = -1, oldMono = -1;
1693 if(!formWidget) return;
1695 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1696 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1697 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1699 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1701 * Enable shell resizing.
1703 shellArgs[0].value = (XtArgVal) &w;
1704 shellArgs[1].value = (XtArgVal) &h;
1705 XtGetValues(shellWidget, shellArgs, 2);
1707 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1708 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1709 XtSetValues(shellWidget, &shellArgs[2], 4);
1711 XtSetArg(args[0], XtNdefaultDistance, &sep);
1712 XtGetValues(formWidget, args, 1);
1714 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1716 hOffset = boardWidth + 10;
1717 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1718 secondSegments[i] = gridSegments[i];
1719 secondSegments[i].x1 += hOffset;
1720 secondSegments[i].x2 += hOffset;
1723 XtSetArg(args[0], XtNwidth, boardWidth);
1724 XtSetArg(args[1], XtNheight, boardHeight);
1725 XtSetValues(boardWidget, args, 2);
1727 timerWidth = (boardWidth - sep) / 2;
1728 XtSetArg(args[0], XtNwidth, timerWidth);
1729 XtSetValues(whiteTimerWidget, args, 1);
1730 XtSetValues(blackTimerWidget, args, 1);
1732 XawFormDoLayout(formWidget, False);
1734 if (appData.titleInWindow) {
1736 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1737 XtSetArg(args[i], XtNheight, &h); i++;
1738 XtGetValues(titleWidget, args, i);
1740 w = boardWidth - 2*bor;
1742 XtSetArg(args[0], XtNwidth, &w);
1743 XtGetValues(menuBarWidget, args, 1);
1744 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1747 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1748 if (gres != XtGeometryYes && appData.debugMode) {
1750 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1751 programName, gres, w, h, wr, hr);
1755 XawFormDoLayout(formWidget, True);
1758 * Inhibit shell resizing.
1760 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1761 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1762 shellArgs[4].value = shellArgs[2].value = w;
1763 shellArgs[5].value = shellArgs[3].value = h;
1764 XtSetValues(shellWidget, &shellArgs[0], 6);
1767 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1770 if(gameInfo.variant != oldVariant) { // and only if variant changed
1773 for(i=0; i<4; i++) {
1775 for(p=0; p<=(int)WhiteKing; p++)
1776 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1777 if(gameInfo.variant == VariantShogi) {
1778 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1779 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1780 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1781 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1782 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1785 if(gameInfo.variant == VariantGothic) {
1786 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1789 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1790 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1791 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1794 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1795 for(p=0; p<=(int)WhiteKing; p++)
1796 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1797 if(gameInfo.variant == VariantShogi) {
1798 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1799 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1800 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1801 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1802 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1805 if(gameInfo.variant == VariantGothic) {
1806 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1809 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1810 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1811 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1816 for(i=0; i<2; i++) {
1818 for(p=0; p<=(int)WhiteKing; p++)
1819 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1820 if(gameInfo.variant == VariantShogi) {
1821 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1822 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1823 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1824 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1825 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1828 if(gameInfo.variant == VariantGothic) {
1829 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1832 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1833 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1834 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1840 if(appData.monoMode == oldMono)
1843 oldMono = appData.monoMode;
1847 void ParseIcsTextColors()
1848 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1849 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1850 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1851 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1852 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1853 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1854 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1855 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1856 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1857 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1858 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1860 if (appData.colorize) {
1862 _("%s: can't parse color names; disabling colorization\n"),
1865 appData.colorize = FALSE;
1870 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1871 XrmValue vFrom, vTo;
1872 int forceMono = False;
1874 if (!appData.monoMode) {
1875 vFrom.addr = (caddr_t) appData.lightSquareColor;
1876 vFrom.size = strlen(appData.lightSquareColor);
1877 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1878 if (vTo.addr == NULL) {
1879 appData.monoMode = True;
1882 lightSquareColor = *(Pixel *) vTo.addr;
1885 if (!appData.monoMode) {
1886 vFrom.addr = (caddr_t) appData.darkSquareColor;
1887 vFrom.size = strlen(appData.darkSquareColor);
1888 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1889 if (vTo.addr == NULL) {
1890 appData.monoMode = True;
1893 darkSquareColor = *(Pixel *) vTo.addr;
1896 if (!appData.monoMode) {
1897 vFrom.addr = (caddr_t) appData.whitePieceColor;
1898 vFrom.size = strlen(appData.whitePieceColor);
1899 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1900 if (vTo.addr == NULL) {
1901 appData.monoMode = True;
1904 whitePieceColor = *(Pixel *) vTo.addr;
1907 if (!appData.monoMode) {
1908 vFrom.addr = (caddr_t) appData.blackPieceColor;
1909 vFrom.size = strlen(appData.blackPieceColor);
1910 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1911 if (vTo.addr == NULL) {
1912 appData.monoMode = True;
1915 blackPieceColor = *(Pixel *) vTo.addr;
1919 if (!appData.monoMode) {
1920 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1921 vFrom.size = strlen(appData.highlightSquareColor);
1922 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1923 if (vTo.addr == NULL) {
1924 appData.monoMode = True;
1927 highlightSquareColor = *(Pixel *) vTo.addr;
1931 if (!appData.monoMode) {
1932 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1933 vFrom.size = strlen(appData.premoveHighlightColor);
1934 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1935 if (vTo.addr == NULL) {
1936 appData.monoMode = True;
1939 premoveHighlightColor = *(Pixel *) vTo.addr;
1947 { // [HGM] taken out of main
1949 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1950 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1951 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1953 if (appData.bitmapDirectory[0] != NULLCHAR) {
1957 CreateXPMBoard(appData.liteBackTextureFile, 1);
1958 CreateXPMBoard(appData.darkBackTextureFile, 0);
1962 /* Create regular pieces */
1963 if (!useImages) CreatePieces();
1972 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1973 XSetWindowAttributes window_attributes;
1975 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1976 XrmValue vFrom, vTo;
1977 XtGeometryResult gres;
1980 int forceMono = False;
1982 srandom(time(0)); // [HGM] book: make random truly random
1984 setbuf(stdout, NULL);
1985 setbuf(stderr, NULL);
1988 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1989 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1993 programName = strrchr(argv[0], '/');
1994 if (programName == NULL)
1995 programName = argv[0];
2000 XtSetLanguageProc(NULL, NULL, NULL);
2001 bindtextdomain(PACKAGE, LOCALEDIR);
2002 textdomain(PACKAGE);
2006 XtAppInitialize(&appContext, "XBoard", shellOptions,
2007 XtNumber(shellOptions),
2008 &argc, argv, xboardResources, NULL, 0);
2009 appData.boardSize = "";
2010 InitAppData(ConvertToLine(argc, argv));
2012 if (p == NULL) p = "/tmp";
2013 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2014 gameCopyFilename = (char*) malloc(i);
2015 gamePasteFilename = (char*) malloc(i);
2016 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2017 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2019 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2020 clientResources, XtNumber(clientResources),
2023 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2024 static char buf[MSG_SIZ];
2025 EscapeExpand(buf, appData.firstInitString);
2026 appData.firstInitString = strdup(buf);
2027 EscapeExpand(buf, appData.secondInitString);
2028 appData.secondInitString = strdup(buf);
2029 EscapeExpand(buf, appData.firstComputerString);
2030 appData.firstComputerString = strdup(buf);
2031 EscapeExpand(buf, appData.secondComputerString);
2032 appData.secondComputerString = strdup(buf);
2035 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2038 if (chdir(chessDir) != 0) {
2039 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2045 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2046 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2047 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2048 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2051 setbuf(debugFP, NULL);
2055 if (appData.debugMode) {
2056 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2060 /* [HGM,HR] make sure board size is acceptable */
2061 if(appData.NrFiles > BOARD_FILES ||
2062 appData.NrRanks > BOARD_RANKS )
2063 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2066 /* This feature does not work; animation needs a rewrite */
2067 appData.highlightDragging = FALSE;
2071 xDisplay = XtDisplay(shellWidget);
2072 xScreen = DefaultScreen(xDisplay);
2073 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2075 gameInfo.variant = StringToVariant(appData.variant);
2076 InitPosition(FALSE);
2079 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2081 if (isdigit(appData.boardSize[0])) {
2082 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2083 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2084 &fontPxlSize, &smallLayout, &tinyLayout);
2086 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2087 programName, appData.boardSize);
2091 /* Find some defaults; use the nearest known size */
2092 SizeDefaults *szd, *nearest;
2093 int distance = 99999;
2094 nearest = szd = sizeDefaults;
2095 while (szd->name != NULL) {
2096 if (abs(szd->squareSize - squareSize) < distance) {
2098 distance = abs(szd->squareSize - squareSize);
2099 if (distance == 0) break;
2103 if (i < 2) lineGap = nearest->lineGap;
2104 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2105 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2106 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2107 if (i < 6) smallLayout = nearest->smallLayout;
2108 if (i < 7) tinyLayout = nearest->tinyLayout;
2111 SizeDefaults *szd = sizeDefaults;
2112 if (*appData.boardSize == NULLCHAR) {
2113 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2114 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2117 if (szd->name == NULL) szd--;
2118 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2120 while (szd->name != NULL &&
2121 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2122 if (szd->name == NULL) {
2123 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2124 programName, appData.boardSize);
2128 squareSize = szd->squareSize;
2129 lineGap = szd->lineGap;
2130 clockFontPxlSize = szd->clockFontPxlSize;
2131 coordFontPxlSize = szd->coordFontPxlSize;
2132 fontPxlSize = szd->fontPxlSize;
2133 smallLayout = szd->smallLayout;
2134 tinyLayout = szd->tinyLayout;
2135 // [HGM] font: use defaults from settings file if available and not overruled
2137 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2138 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2139 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2140 appData.font = fontTable[MESSAGE_FONT][squareSize];
2141 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2142 appData.coordFont = fontTable[COORD_FONT][squareSize];
2144 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2145 if (strlen(appData.pixmapDirectory) > 0) {
2146 p = ExpandPathName(appData.pixmapDirectory);
2148 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2149 appData.pixmapDirectory);
2152 if (appData.debugMode) {
2153 fprintf(stderr, _("\
2154 XBoard square size (hint): %d\n\
2155 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2157 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2158 if (appData.debugMode) {
2159 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2162 defaultLineGap = lineGap;
2163 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2165 /* [HR] height treated separately (hacked) */
2166 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2167 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2168 if (appData.showJail == 1) {
2169 /* Jail on top and bottom */
2170 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2171 XtSetArg(boardArgs[2], XtNheight,
2172 boardHeight + 2*(lineGap + squareSize));
2173 } else if (appData.showJail == 2) {
2175 XtSetArg(boardArgs[1], XtNwidth,
2176 boardWidth + 2*(lineGap + squareSize));
2177 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2180 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2181 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2185 * Determine what fonts to use.
2188 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2189 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2190 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2191 fontSet = CreateFontSet(appData.font);
2192 clockFontSet = CreateFontSet(appData.clockFont);
2194 /* For the coordFont, use the 0th font of the fontset. */
2195 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2196 XFontStruct **font_struct_list;
2197 char **font_name_list;
2198 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2199 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2200 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2203 appData.font = FindFont(appData.font, fontPxlSize);
2204 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2205 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2206 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2207 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2208 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2209 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2211 countFontID = coordFontID; // [HGM] holdings
2212 countFontStruct = coordFontStruct;
2214 xdb = XtDatabase(xDisplay);
2216 XrmPutLineResource(&xdb, "*international: True");
2217 vTo.size = sizeof(XFontSet);
2218 vTo.addr = (XtPointer) &fontSet;
2219 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2221 XrmPutStringResource(&xdb, "*font", appData.font);
2225 * Detect if there are not enough colors available and adapt.
2227 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2228 appData.monoMode = True;
2231 forceMono = MakeColors();
2234 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2236 appData.monoMode = True;
2239 if (appData.lowTimeWarning && !appData.monoMode) {
2240 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2241 vFrom.size = strlen(appData.lowTimeWarningColor);
2242 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2243 if (vTo.addr == NULL)
2244 appData.monoMode = True;
2246 lowTimeWarningColor = *(Pixel *) vTo.addr;
2249 if (appData.monoMode && appData.debugMode) {
2250 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2251 (unsigned long) XWhitePixel(xDisplay, xScreen),
2252 (unsigned long) XBlackPixel(xDisplay, xScreen));
2255 ParseIcsTextColors();
2256 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2257 textColors[ColorNone].attr = 0;
2259 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2265 layoutName = "tinyLayout";
2266 } else if (smallLayout) {
2267 layoutName = "smallLayout";
2269 layoutName = "normalLayout";
2271 /* Outer layoutWidget is there only to provide a name for use in
2272 resources that depend on the layout style */
2274 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2275 layoutArgs, XtNumber(layoutArgs));
2277 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2278 formArgs, XtNumber(formArgs));
2279 XtSetArg(args[0], XtNdefaultDistance, &sep);
2280 XtGetValues(formWidget, args, 1);
2283 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2284 XtSetArg(args[0], XtNtop, XtChainTop);
2285 XtSetArg(args[1], XtNbottom, XtChainTop);
2286 XtSetArg(args[2], XtNright, XtChainLeft);
2287 XtSetValues(menuBarWidget, args, 3);
2289 widgetList[j++] = whiteTimerWidget =
2290 XtCreateWidget("whiteTime", labelWidgetClass,
2291 formWidget, timerArgs, XtNumber(timerArgs));
2293 XtSetArg(args[0], XtNfontSet, clockFontSet);
2295 XtSetArg(args[0], XtNfont, clockFontStruct);
2297 XtSetArg(args[1], XtNtop, XtChainTop);
2298 XtSetArg(args[2], XtNbottom, XtChainTop);
2299 XtSetValues(whiteTimerWidget, args, 3);
2301 widgetList[j++] = blackTimerWidget =
2302 XtCreateWidget("blackTime", labelWidgetClass,
2303 formWidget, timerArgs, XtNumber(timerArgs));
2305 XtSetArg(args[0], XtNfontSet, clockFontSet);
2307 XtSetArg(args[0], XtNfont, clockFontStruct);
2309 XtSetArg(args[1], XtNtop, XtChainTop);
2310 XtSetArg(args[2], XtNbottom, XtChainTop);
2311 XtSetValues(blackTimerWidget, args, 3);
2313 if (appData.titleInWindow) {
2314 widgetList[j++] = titleWidget =
2315 XtCreateWidget("title", labelWidgetClass, formWidget,
2316 titleArgs, XtNumber(titleArgs));
2317 XtSetArg(args[0], XtNtop, XtChainTop);
2318 XtSetArg(args[1], XtNbottom, XtChainTop);
2319 XtSetValues(titleWidget, args, 2);
2322 if (appData.showButtonBar) {
2323 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2324 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2325 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2326 XtSetArg(args[2], XtNtop, XtChainTop);
2327 XtSetArg(args[3], XtNbottom, XtChainTop);
2328 XtSetValues(buttonBarWidget, args, 4);
2331 widgetList[j++] = messageWidget =
2332 XtCreateWidget("message", labelWidgetClass, formWidget,
2333 messageArgs, XtNumber(messageArgs));
2334 XtSetArg(args[0], XtNtop, XtChainTop);
2335 XtSetArg(args[1], XtNbottom, XtChainTop);
2336 XtSetValues(messageWidget, args, 2);
2338 widgetList[j++] = boardWidget =
2339 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2340 XtNumber(boardArgs));
2342 XtManageChildren(widgetList, j);
2344 timerWidth = (boardWidth - sep) / 2;
2345 XtSetArg(args[0], XtNwidth, timerWidth);
2346 XtSetValues(whiteTimerWidget, args, 1);
2347 XtSetValues(blackTimerWidget, args, 1);
2349 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2350 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2351 XtGetValues(whiteTimerWidget, args, 2);
2353 if (appData.showButtonBar) {
2354 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2355 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2356 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2360 * formWidget uses these constraints but they are stored
2364 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2365 XtSetValues(menuBarWidget, args, i);
2366 if (appData.titleInWindow) {
2369 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2370 XtSetValues(whiteTimerWidget, args, i);
2372 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2373 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2374 XtSetValues(blackTimerWidget, args, i);
2376 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2377 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2378 XtSetValues(titleWidget, args, i);
2380 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2381 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2382 XtSetValues(messageWidget, args, i);
2383 if (appData.showButtonBar) {
2385 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2386 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2387 XtSetValues(buttonBarWidget, args, i);
2391 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2392 XtSetValues(whiteTimerWidget, args, i);
2394 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2395 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2396 XtSetValues(blackTimerWidget, args, i);
2398 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2399 XtSetValues(titleWidget, args, i);
2401 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2402 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2403 XtSetValues(messageWidget, args, i);
2404 if (appData.showButtonBar) {
2406 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2407 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2408 XtSetValues(buttonBarWidget, args, i);
2413 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2414 XtSetValues(whiteTimerWidget, args, i);
2416 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2417 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2418 XtSetValues(blackTimerWidget, args, i);
2420 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2421 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2422 XtSetValues(messageWidget, args, i);
2423 if (appData.showButtonBar) {
2425 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2426 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2427 XtSetValues(buttonBarWidget, args, i);
2431 XtSetArg(args[0], XtNfromVert, messageWidget);
2432 XtSetArg(args[1], XtNtop, XtChainTop);
2433 XtSetArg(args[2], XtNbottom, XtChainBottom);
2434 XtSetArg(args[3], XtNleft, XtChainLeft);
2435 XtSetArg(args[4], XtNright, XtChainRight);
2436 XtSetValues(boardWidget, args, 5);
2438 XtRealizeWidget(shellWidget);
2441 XtSetArg(args[0], XtNx, wpMain.x);
2442 XtSetArg(args[1], XtNy, wpMain.y);
2443 XtSetValues(shellWidget, args, 2);
2447 * Correct the width of the message and title widgets.
2448 * It is not known why some systems need the extra fudge term.
2449 * The value "2" is probably larger than needed.
2451 XawFormDoLayout(formWidget, False);
2453 #define WIDTH_FUDGE 2
2455 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2456 XtSetArg(args[i], XtNheight, &h); i++;
2457 XtGetValues(messageWidget, args, i);
2458 if (appData.showButtonBar) {
2460 XtSetArg(args[i], XtNwidth, &w); i++;
2461 XtGetValues(buttonBarWidget, args, i);
2462 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2464 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2467 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2468 if (gres != XtGeometryYes && appData.debugMode) {
2469 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2470 programName, gres, w, h, wr, hr);
2473 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2474 /* The size used for the child widget in layout lags one resize behind
2475 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2477 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2478 if (gres != XtGeometryYes && appData.debugMode) {
2479 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2480 programName, gres, w, h, wr, hr);
2483 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2484 XtSetArg(args[1], XtNright, XtChainRight);
2485 XtSetValues(messageWidget, args, 2);
2487 if (appData.titleInWindow) {
2489 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2490 XtSetArg(args[i], XtNheight, &h); i++;
2491 XtGetValues(titleWidget, args, i);
2493 w = boardWidth - 2*bor;
2495 XtSetArg(args[0], XtNwidth, &w);
2496 XtGetValues(menuBarWidget, args, 1);
2497 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2500 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2501 if (gres != XtGeometryYes && appData.debugMode) {
2503 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2504 programName, gres, w, h, wr, hr);
2507 XawFormDoLayout(formWidget, True);
2509 xBoardWindow = XtWindow(boardWidget);
2511 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2512 // not need to go into InitDrawingSizes().
2516 * Create X checkmark bitmap and initialize option menu checks.
2518 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2519 checkmark_bits, checkmark_width, checkmark_height);
2520 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2521 #ifndef OPTIONSDIALOG
2522 if (appData.alwaysPromoteToQueen) {
2523 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2526 if (appData.animateDragging) {
2527 XtSetValues(XtNameToWidget(menuBarWidget,
2528 "menuOptions.Animate Dragging"),
2531 if (appData.animate) {
2532 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2535 if (appData.autoCallFlag) {
2536 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2539 if (appData.autoFlipView) {
2540 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2543 if (appData.blindfold) {
2544 XtSetValues(XtNameToWidget(menuBarWidget,
2545 "menuOptions.Blindfold"), args, 1);
2547 if (appData.flashCount > 0) {
2548 XtSetValues(XtNameToWidget(menuBarWidget,
2549 "menuOptions.Flash Moves"),
2553 if (appData.highlightDragging) {
2554 XtSetValues(XtNameToWidget(menuBarWidget,
2555 "menuOptions.Highlight Dragging"),
2559 if (appData.highlightLastMove) {
2560 XtSetValues(XtNameToWidget(menuBarWidget,
2561 "menuOptions.Highlight Last Move"),
2564 if (appData.highlightMoveWithArrow) {
2565 XtSetValues(XtNameToWidget(menuBarWidget,
2566 "menuOptions.Arrow"),
2569 // if (appData.icsAlarm) {
2570 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2573 if (appData.ringBellAfterMoves) {
2574 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2577 if (appData.oneClick) {
2578 XtSetValues(XtNameToWidget(menuBarWidget,
2579 "menuOptions.OneClick"), args, 1);
2581 if (appData.periodicUpdates) {
2582 XtSetValues(XtNameToWidget(menuBarWidget,
2583 "menuOptions.Periodic Updates"), args, 1);
2585 if (appData.ponderNextMove) {
2586 XtSetValues(XtNameToWidget(menuBarWidget,
2587 "menuOptions.Ponder Next Move"), args, 1);
2589 if (appData.popupExitMessage) {
2590 XtSetValues(XtNameToWidget(menuBarWidget,
2591 "menuOptions.Popup Exit Message"), args, 1);
2593 if (appData.popupMoveErrors) {
2594 XtSetValues(XtNameToWidget(menuBarWidget,
2595 "menuOptions.Popup Move Errors"), args, 1);
2597 // if (appData.premove) {
2598 // XtSetValues(XtNameToWidget(menuBarWidget,
2599 // "menuOptions.Premove"), args, 1);
2601 if (appData.showCoords) {
2602 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2605 if (appData.hideThinkingFromHuman) {
2606 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2609 if (appData.testLegality) {
2610 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2614 if (saveSettingsOnExit) {
2615 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2622 ReadBitmap(&wIconPixmap, "icon_white.bm",
2623 icon_white_bits, icon_white_width, icon_white_height);
2624 ReadBitmap(&bIconPixmap, "icon_black.bm",
2625 icon_black_bits, icon_black_width, icon_black_height);
2626 iconPixmap = wIconPixmap;
2628 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2629 XtSetValues(shellWidget, args, i);
2632 * Create a cursor for the board widget.
2634 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2635 XChangeWindowAttributes(xDisplay, xBoardWindow,
2636 CWCursor, &window_attributes);
2639 * Inhibit shell resizing.
2641 shellArgs[0].value = (XtArgVal) &w;
2642 shellArgs[1].value = (XtArgVal) &h;
2643 XtGetValues(shellWidget, shellArgs, 2);
2644 shellArgs[4].value = shellArgs[2].value = w;
2645 shellArgs[5].value = shellArgs[3].value = h;
2646 XtSetValues(shellWidget, &shellArgs[2], 4);
2647 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2648 marginH = h - boardHeight;
2650 CatchDeleteWindow(shellWidget, "QuitProc");
2658 if (appData.animate || appData.animateDragging)
2661 XtAugmentTranslations(formWidget,
2662 XtParseTranslationTable(globalTranslations));
2663 XtAugmentTranslations(boardWidget,
2664 XtParseTranslationTable(boardTranslations));
2665 XtAugmentTranslations(whiteTimerWidget,
2666 XtParseTranslationTable(whiteTranslations));
2667 XtAugmentTranslations(blackTimerWidget,
2668 XtParseTranslationTable(blackTranslations));
2670 /* Why is the following needed on some versions of X instead
2671 * of a translation? */
2672 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2673 (XtEventHandler) EventProc, NULL);
2675 XtAddEventHandler(formWidget, KeyPressMask, False,
2676 (XtEventHandler) MoveTypeInProc, NULL);
2678 /* [AS] Restore layout */
2679 if( wpMoveHistory.visible ) {
2683 if( wpEvalGraph.visible )
2688 if( wpEngineOutput.visible ) {
2689 EngineOutputPopUp();
2694 if (errorExitStatus == -1) {
2695 if (appData.icsActive) {
2696 /* We now wait until we see "login:" from the ICS before
2697 sending the logon script (problems with timestamp otherwise) */
2698 /*ICSInitScript();*/
2699 if (appData.icsInputBox) ICSInputBoxPopUp();
2703 signal(SIGWINCH, TermSizeSigHandler);
2705 signal(SIGINT, IntSigHandler);
2706 signal(SIGTERM, IntSigHandler);
2707 if (*appData.cmailGameName != NULLCHAR) {
2708 signal(SIGUSR1, CmailSigHandler);
2711 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2713 // XtSetKeyboardFocus(shellWidget, formWidget);
2714 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2716 XtAppMainLoop(appContext);
2717 if (appData.debugMode) fclose(debugFP); // [DM] debug
2721 static Boolean noEcho;
2726 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2727 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2729 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2730 unlink(gameCopyFilename);
2731 unlink(gamePasteFilename);
2732 if(noEcho) EchoOn();
2735 RETSIGTYPE TermSizeSigHandler(int sig)
2748 CmailSigHandler(sig)
2754 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2756 /* Activate call-back function CmailSigHandlerCallBack() */
2757 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2759 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2763 CmailSigHandlerCallBack(isr, closure, message, count, error)
2771 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2773 /**** end signal code ****/
2779 /* try to open the icsLogon script, either in the location given
2780 * or in the users HOME directory
2787 f = fopen(appData.icsLogon, "r");
2790 homedir = getenv("HOME");
2791 if (homedir != NULL)
2793 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2794 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2795 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2796 f = fopen(buf, "r");
2801 ProcessICSInitScript(f);
2803 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2826 if (!menuBarWidget) return;
2827 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2829 DisplayError("menuEdit.Revert", 0);
2831 XtSetSensitive(w, !grey);
2833 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2835 DisplayError("menuEdit.Annotate", 0);
2837 XtSetSensitive(w, !grey);
2842 SetMenuEnables(enab)
2846 if (!menuBarWidget) return;
2847 while (enab->name != NULL) {
2848 w = XtNameToWidget(menuBarWidget, enab->name);
2850 DisplayError(enab->name, 0);
2852 XtSetSensitive(w, enab->value);
2858 Enables icsEnables[] = {
2859 { "menuFile.Mail Move", False },
2860 { "menuFile.Reload CMail Message", False },
2861 { "menuMode.Machine Black", False },
2862 { "menuMode.Machine White", False },
2863 { "menuMode.Analysis Mode", False },
2864 { "menuMode.Analyze File", False },
2865 { "menuMode.Two Machines", False },
2866 { "menuMode.Machine Match", False },
2868 { "menuEngine.Hint", False },
2869 { "menuEngine.Book", False },
2870 { "menuEngine.Move Now", False },
2871 #ifndef OPTIONSDIALOG
2872 { "menuOptions.Periodic Updates", False },
2873 { "menuOptions.Hide Thinking", False },
2874 { "menuOptions.Ponder Next Move", False },
2877 { "menuEngine.Engine #1 Settings", False },
2878 { "menuEngine.Engine #2 Settings", False },
2879 { "menuEngine.Load Engine", False },
2880 { "menuEdit.Annotate", False },
2881 { "menuOptions.Match", False },
2885 Enables ncpEnables[] = {
2886 { "menuFile.Mail Move", False },
2887 { "menuFile.Reload CMail Message", False },
2888 { "menuMode.Machine White", False },
2889 { "menuMode.Machine Black", False },
2890 { "menuMode.Analysis Mode", False },
2891 { "menuMode.Analyze File", False },
2892 { "menuMode.Two Machines", False },
2893 { "menuMode.Machine Match", False },
2894 { "menuMode.ICS Client", False },
2895 { "menuView.ICStex", False },
2896 { "menuView.ICS Input Box", False },
2897 { "Action", False },
2898 { "menuEdit.Revert", False },
2899 { "menuEdit.Annotate", False },
2900 { "menuEngine.Engine #1 Settings", False },
2901 { "menuEngine.Engine #2 Settings", False },
2902 { "menuEngine.Move Now", False },
2903 { "menuEngine.Retract Move", False },
2904 { "menuOptions.ICS", False },
2905 #ifndef OPTIONSDIALOG
2906 { "menuOptions.Auto Flag", False },
2907 { "menuOptions.Auto Flip View", False },
2908 // { "menuOptions.ICS Alarm", False },
2909 { "menuOptions.Move Sound", False },
2910 { "menuOptions.Hide Thinking", False },
2911 { "menuOptions.Periodic Updates", False },
2912 { "menuOptions.Ponder Next Move", False },
2914 { "menuEngine.Hint", False },
2915 { "menuEngine.Book", False },
2919 Enables gnuEnables[] = {
2920 { "menuMode.ICS Client", False },
2921 { "menuView.ICStex", False },
2922 { "menuView.ICS Input Box", False },
2923 { "menuAction.Accept", False },
2924 { "menuAction.Decline", False },
2925 { "menuAction.Rematch", False },
2926 { "menuAction.Adjourn", False },
2927 { "menuAction.Stop Examining", False },
2928 { "menuAction.Stop Observing", False },
2929 { "menuAction.Upload to Examine", False },
2930 { "menuEdit.Revert", False },
2931 { "menuEdit.Annotate", False },
2932 { "menuOptions.ICS", False },
2934 /* The next two options rely on SetCmailMode being called *after* */
2935 /* SetGNUMode so that when GNU is being used to give hints these */
2936 /* menu options are still available */
2938 { "menuFile.Mail Move", False },
2939 { "menuFile.Reload CMail Message", False },
2940 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2941 { "menuMode.Machine White", True },
2942 { "menuMode.Machine Black", True },
2943 { "menuMode.Analysis Mode", True },
2944 { "menuMode.Analyze File", True },
2945 { "menuMode.Two Machines", True },
2946 { "menuMode.Machine Match", True },
2947 { "menuEngine.Engine #1 Settings", True },
2948 { "menuEngine.Engine #2 Settings", True },
2949 { "menuEngine.Hint", True },
2950 { "menuEngine.Book", True },
2951 { "menuEngine.Move Now", True },
2952 { "menuEngine.Retract Move", True },
2957 Enables cmailEnables[] = {
2959 { "menuAction.Call Flag", False },
2960 { "menuAction.Draw", True },
2961 { "menuAction.Adjourn", False },
2962 { "menuAction.Abort", False },
2963 { "menuAction.Stop Observing", False },
2964 { "menuAction.Stop Examining", False },
2965 { "menuFile.Mail Move", True },
2966 { "menuFile.Reload CMail Message", True },
2970 Enables trainingOnEnables[] = {
2971 { "menuMode.Edit Comment", False },
2972 { "menuMode.Pause", False },
2973 { "menuEdit.Forward", False },
2974 { "menuEdit.Backward", False },
2975 { "menuEdit.Forward to End", False },
2976 { "menuEdit.Back to Start", False },
2977 { "menuEngine.Move Now", False },
2978 { "menuEdit.Truncate Game", False },
2982 Enables trainingOffEnables[] = {
2983 { "menuMode.Edit Comment", True },
2984 { "menuMode.Pause", True },
2985 { "menuEdit.Forward", True },
2986 { "menuEdit.Backward", True },
2987 { "menuEdit.Forward to End", True },
2988 { "menuEdit.Back to Start", True },
2989 { "menuEngine.Move Now", True },
2990 { "menuEdit.Truncate Game", True },
2994 Enables machineThinkingEnables[] = {
2995 { "menuFile.Load Game", False },
2996 // { "menuFile.Load Next Game", False },
2997 // { "menuFile.Load Previous Game", False },
2998 // { "menuFile.Reload Same Game", False },
2999 { "menuEdit.Paste Game", False },
3000 { "menuFile.Load Position", False },
3001 // { "menuFile.Load Next Position", False },
3002 // { "menuFile.Load Previous Position", False },
3003 // { "menuFile.Reload Same Position", False },
3004 { "menuEdit.Paste Position", False },
3005 { "menuMode.Machine White", False },
3006 { "menuMode.Machine Black", False },
3007 { "menuMode.Two Machines", False },
3008 // { "menuMode.Machine Match", False },
3009 { "menuEngine.Retract Move", False },
3013 Enables userThinkingEnables[] = {
3014 { "menuFile.Load Game", True },
3015 // { "menuFile.Load Next Game", True },
3016 // { "menuFile.Load Previous Game", True },
3017 // { "menuFile.Reload Same Game", True },
3018 { "menuEdit.Paste Game", True },
3019 { "menuFile.Load Position", True },
3020 // { "menuFile.Load Next Position", True },
3021 // { "menuFile.Load Previous Position", True },
3022 // { "menuFile.Reload Same Position", True },
3023 { "menuEdit.Paste Position", True },
3024 { "menuMode.Machine White", True },
3025 { "menuMode.Machine Black", True },
3026 { "menuMode.Two Machines", True },
3027 // { "menuMode.Machine Match", True },
3028 { "menuEngine.Retract Move", True },
3034 SetMenuEnables(icsEnables);
3037 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3038 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3039 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3047 SetMenuEnables(ncpEnables);
3053 SetMenuEnables(gnuEnables);
3059 SetMenuEnables(cmailEnables);
3065 SetMenuEnables(trainingOnEnables);
3066 if (appData.showButtonBar) {
3067 XtSetSensitive(buttonBarWidget, False);
3073 SetTrainingModeOff()
3075 SetMenuEnables(trainingOffEnables);
3076 if (appData.showButtonBar) {
3077 XtSetSensitive(buttonBarWidget, True);
3082 SetUserThinkingEnables()
3084 if (appData.noChessProgram) return;
3085 SetMenuEnables(userThinkingEnables);
3089 SetMachineThinkingEnables()
3091 if (appData.noChessProgram) return;
3092 SetMenuEnables(machineThinkingEnables);
3094 case MachinePlaysBlack:
3095 case MachinePlaysWhite:
3096 case TwoMachinesPlay:
3097 XtSetSensitive(XtNameToWidget(menuBarWidget,
3098 ModeToWidgetName(gameMode)), True);
3105 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3106 #define HISTORY_SIZE 64
3107 static char *history[HISTORY_SIZE];
3108 int histIn = 0, histP = 0;
3111 SaveInHistory(char *cmd)
3113 if (history[histIn] != NULL) {
3114 free(history[histIn]);
3115 history[histIn] = NULL;
3117 if (*cmd == NULLCHAR) return;
3118 history[histIn] = StrSave(cmd);
3119 histIn = (histIn + 1) % HISTORY_SIZE;
3120 if (history[histIn] != NULL) {
3121 free(history[histIn]);
3122 history[histIn] = NULL;
3128 PrevInHistory(char *cmd)
3131 if (histP == histIn) {
3132 if (history[histIn] != NULL) free(history[histIn]);
3133 history[histIn] = StrSave(cmd);
3135 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3136 if (newhp == histIn || history[newhp] == NULL) return NULL;
3138 return history[histP];
3144 if (histP == histIn) return NULL;
3145 histP = (histP + 1) % HISTORY_SIZE;
3146 return history[histP];
3148 // end of borrowed code
3150 #define Abs(n) ((n)<0 ? -(n) : (n))
3154 InsertPxlSize(pattern, targetPxlSize)
3158 char *base_fnt_lst, strInt[12], *p, *q;
3159 int alternatives, i, len, strIntLen;
3162 * Replace the "*" (if present) in the pixel-size slot of each
3163 * alternative with the targetPxlSize.
3167 while ((p = strchr(p, ',')) != NULL) {
3171 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3172 strIntLen = strlen(strInt);
3173 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3177 while (alternatives--) {
3178 char *comma = strchr(p, ',');
3179 for (i=0; i<14; i++) {
3180 char *hyphen = strchr(p, '-');
3182 if (comma && hyphen > comma) break;
3183 len = hyphen + 1 - p;
3184 if (i == 7 && *p == '*' && len == 2) {
3186 memcpy(q, strInt, strIntLen);
3196 len = comma + 1 - p;
3203 return base_fnt_lst;
3207 CreateFontSet(base_fnt_lst)
3211 char **missing_list;
3215 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3216 &missing_list, &missing_count, &def_string);
3217 if (appData.debugMode) {
3219 XFontStruct **font_struct_list;
3220 char **font_name_list;
3221 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3223 fprintf(debugFP, " got list %s, locale %s\n",
3224 XBaseFontNameListOfFontSet(fntSet),
3225 XLocaleOfFontSet(fntSet));
3226 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3227 for (i = 0; i < count; i++) {
3228 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3231 for (i = 0; i < missing_count; i++) {
3232 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3235 if (fntSet == NULL) {
3236 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3241 #else // not ENABLE_NLS
3243 * Find a font that matches "pattern" that is as close as
3244 * possible to the targetPxlSize. Prefer fonts that are k
3245 * pixels smaller to fonts that are k pixels larger. The
3246 * pattern must be in the X Consortium standard format,
3247 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3248 * The return value should be freed with XtFree when no
3252 FindFont(pattern, targetPxlSize)
3256 char **fonts, *p, *best, *scalable, *scalableTail;
3257 int i, j, nfonts, minerr, err, pxlSize;
3259 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3261 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3262 programName, pattern);
3269 for (i=0; i<nfonts; i++) {
3272 if (*p != '-') continue;
3274 if (*p == NULLCHAR) break;
3275 if (*p++ == '-') j++;
3277 if (j < 7) continue;
3280 scalable = fonts[i];
3283 err = pxlSize - targetPxlSize;
3284 if (Abs(err) < Abs(minerr) ||
3285 (minerr > 0 && err < 0 && -err == minerr)) {
3291 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3292 /* If the error is too big and there is a scalable font,
3293 use the scalable font. */
3294 int headlen = scalableTail - scalable;
3295 p = (char *) XtMalloc(strlen(scalable) + 10);
3296 while (isdigit(*scalableTail)) scalableTail++;
3297 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3299 p = (char *) XtMalloc(strlen(best) + 2);
3300 safeStrCpy(p, best, strlen(best)+1 );
3302 if (appData.debugMode) {
3303 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3304 pattern, targetPxlSize, p);
3306 XFreeFontNames(fonts);
3312 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3313 // must be called before all non-first callse to CreateGCs()
3314 XtReleaseGC(shellWidget, highlineGC);
3315 XtReleaseGC(shellWidget, lightSquareGC);
3316 XtReleaseGC(shellWidget, darkSquareGC);
3317 XtReleaseGC(shellWidget, lineGC);
3318 if (appData.monoMode) {
3319 if (DefaultDepth(xDisplay, xScreen) == 1) {
3320 XtReleaseGC(shellWidget, wbPieceGC);
3322 XtReleaseGC(shellWidget, bwPieceGC);
3325 XtReleaseGC(shellWidget, prelineGC);
3326 XtReleaseGC(shellWidget, jailSquareGC);
3327 XtReleaseGC(shellWidget, wdPieceGC);
3328 XtReleaseGC(shellWidget, wlPieceGC);
3329 XtReleaseGC(shellWidget, wjPieceGC);
3330 XtReleaseGC(shellWidget, bdPieceGC);
3331 XtReleaseGC(shellWidget, blPieceGC);
3332 XtReleaseGC(shellWidget, bjPieceGC);
3336 void CreateGCs(int redo)
3338 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3339 | GCBackground | GCFunction | GCPlaneMask;
3340 XGCValues gc_values;
3343 gc_values.plane_mask = AllPlanes;
3344 gc_values.line_width = lineGap;
3345 gc_values.line_style = LineSolid;
3346 gc_values.function = GXcopy;
3349 DeleteGCs(); // called a second time; clean up old GCs first
3350 } else { // [HGM] grid and font GCs created on first call only
3351 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3352 gc_values.background = XWhitePixel(xDisplay, xScreen);
3353 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3354 XSetFont(xDisplay, coordGC, coordFontID);
3356 // [HGM] make font for holdings counts (white on black)
3357 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3358 gc_values.background = XBlackPixel(xDisplay, xScreen);
3359 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3360 XSetFont(xDisplay, countGC, countFontID);
3362 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3363 gc_values.background = XBlackPixel(xDisplay, xScreen);
3364 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3366 if (appData.monoMode) {
3367 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3368 gc_values.background = XWhitePixel(xDisplay, xScreen);
3369 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3371 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3372 gc_values.background = XBlackPixel(xDisplay, xScreen);
3373 lightSquareGC = wbPieceGC
3374 = XtGetGC(shellWidget, value_mask, &gc_values);
3376 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3377 gc_values.background = XWhitePixel(xDisplay, xScreen);
3378 darkSquareGC = bwPieceGC
3379 = XtGetGC(shellWidget, value_mask, &gc_values);
3381 if (DefaultDepth(xDisplay, xScreen) == 1) {
3382 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3383 gc_values.function = GXcopyInverted;
3384 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3385 gc_values.function = GXcopy;
3386 if (XBlackPixel(xDisplay, xScreen) == 1) {
3387 bwPieceGC = darkSquareGC;
3388 wbPieceGC = copyInvertedGC;
3390 bwPieceGC = copyInvertedGC;
3391 wbPieceGC = lightSquareGC;
3395 gc_values.foreground = highlightSquareColor;
3396 gc_values.background = highlightSquareColor;
3397 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3399 gc_values.foreground = premoveHighlightColor;
3400 gc_values.background = premoveHighlightColor;
3401 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3403 gc_values.foreground = lightSquareColor;
3404 gc_values.background = darkSquareColor;
3405 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3407 gc_values.foreground = darkSquareColor;
3408 gc_values.background = lightSquareColor;
3409 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3411 gc_values.foreground = jailSquareColor;
3412 gc_values.background = jailSquareColor;
3413 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3415 gc_values.foreground = whitePieceColor;
3416 gc_values.background = darkSquareColor;
3417 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3419 gc_values.foreground = whitePieceColor;
3420 gc_values.background = lightSquareColor;
3421 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3423 gc_values.foreground = whitePieceColor;
3424 gc_values.background = jailSquareColor;
3425 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3427 gc_values.foreground = blackPieceColor;
3428 gc_values.background = darkSquareColor;
3429 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3431 gc_values.foreground = blackPieceColor;
3432 gc_values.background = lightSquareColor;
3433 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3435 gc_values.foreground = blackPieceColor;
3436 gc_values.background = jailSquareColor;
3437 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3441 void loadXIM(xim, xmask, filename, dest, mask)
3454 fp = fopen(filename, "rb");
3456 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3463 for (y=0; y<h; ++y) {
3464 for (x=0; x<h; ++x) {
3469 XPutPixel(xim, x, y, blackPieceColor);
3471 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3474 XPutPixel(xim, x, y, darkSquareColor);
3476 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3479 XPutPixel(xim, x, y, whitePieceColor);
3481 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3484 XPutPixel(xim, x, y, lightSquareColor);
3486 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3494 /* create Pixmap of piece */
3495 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3497 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3500 /* create Pixmap of clipmask
3501 Note: We assume the white/black pieces have the same
3502 outline, so we make only 6 masks. This is okay
3503 since the XPM clipmask routines do the same. */
3505 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3507 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3510 /* now create the 1-bit version */
3511 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3514 values.foreground = 1;
3515 values.background = 0;
3517 /* Don't use XtGetGC, not read only */
3518 maskGC = XCreateGC(xDisplay, *mask,
3519 GCForeground | GCBackground, &values);
3520 XCopyPlane(xDisplay, temp, *mask, maskGC,
3521 0, 0, squareSize, squareSize, 0, 0, 1);
3522 XFreePixmap(xDisplay, temp);
3527 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3529 void CreateXIMPieces()
3534 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3539 /* The XSynchronize calls were copied from CreatePieces.
3540 Not sure if needed, but can't hurt */
3541 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3544 /* temp needed by loadXIM() */
3545 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3546 0, 0, ss, ss, AllPlanes, XYPixmap);
3548 if (strlen(appData.pixmapDirectory) == 0) {
3552 if (appData.monoMode) {
3553 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3557 fprintf(stderr, _("\nLoading XIMs...\n"));
3559 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3560 fprintf(stderr, "%d", piece+1);
3561 for (kind=0; kind<4; kind++) {
3562 fprintf(stderr, ".");
3563 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3564 ExpandPathName(appData.pixmapDirectory),
3565 piece <= (int) WhiteKing ? "" : "w",
3566 pieceBitmapNames[piece],
3568 ximPieceBitmap[kind][piece] =
3569 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3570 0, 0, ss, ss, AllPlanes, XYPixmap);
3571 if (appData.debugMode)
3572 fprintf(stderr, _("(File:%s:) "), buf);
3573 loadXIM(ximPieceBitmap[kind][piece],
3575 &(xpmPieceBitmap2[kind][piece]),
3576 &(ximMaskPm2[piece]));
3577 if(piece <= (int)WhiteKing)
3578 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3580 fprintf(stderr," ");
3582 /* Load light and dark squares */
3583 /* If the LSQ and DSQ pieces don't exist, we will
3584 draw them with solid squares. */
3585 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3586 if (access(buf, 0) != 0) {
3590 fprintf(stderr, _("light square "));
3592 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3593 0, 0, ss, ss, AllPlanes, XYPixmap);
3594 if (appData.debugMode)
3595 fprintf(stderr, _("(File:%s:) "), buf);
3597 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3598 fprintf(stderr, _("dark square "));
3599 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3600 ExpandPathName(appData.pixmapDirectory), ss);
3601 if (appData.debugMode)
3602 fprintf(stderr, _("(File:%s:) "), buf);
3604 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3605 0, 0, ss, ss, AllPlanes, XYPixmap);
3606 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3607 xpmJailSquare = xpmLightSquare;
3609 fprintf(stderr, _("Done.\n"));
3611 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3614 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3617 void CreateXPMBoard(char *s, int kind)
3621 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3622 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3623 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3627 void FreeXPMPieces()
3628 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3629 // thisroutine has to be called t free the old piece pixmaps
3631 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3632 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3634 XFreePixmap(xDisplay, xpmLightSquare);
3635 XFreePixmap(xDisplay, xpmDarkSquare);
3639 void CreateXPMPieces()
3643 u_int ss = squareSize;
3645 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3646 XpmColorSymbol symbols[4];
3647 static int redo = False;
3649 if(redo) FreeXPMPieces(); else redo = 1;
3651 /* The XSynchronize calls were copied from CreatePieces.
3652 Not sure if needed, but can't hurt */
3653 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3655 /* Setup translations so piece colors match square colors */
3656 symbols[0].name = "light_piece";
3657 symbols[0].value = appData.whitePieceColor;
3658 symbols[1].name = "dark_piece";
3659 symbols[1].value = appData.blackPieceColor;
3660 symbols[2].name = "light_square";
3661 symbols[2].value = appData.lightSquareColor;
3662 symbols[3].name = "dark_square";
3663 symbols[3].value = appData.darkSquareColor;
3665 attr.valuemask = XpmColorSymbols;
3666 attr.colorsymbols = symbols;
3667 attr.numsymbols = 4;
3669 if (appData.monoMode) {
3670 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3674 if (strlen(appData.pixmapDirectory) == 0) {
3675 XpmPieces* pieces = builtInXpms;
3678 while (pieces->size != squareSize && pieces->size) pieces++;
3679 if (!pieces->size) {
3680 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3683 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3684 for (kind=0; kind<4; kind++) {
3686 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3687 pieces->xpm[piece][kind],
3688 &(xpmPieceBitmap2[kind][piece]),
3689 NULL, &attr)) != 0) {
3690 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3694 if(piece <= (int) WhiteKing)
3695 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3699 xpmJailSquare = xpmLightSquare;
3703 fprintf(stderr, _("\nLoading XPMs...\n"));
3706 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3707 fprintf(stderr, "%d ", piece+1);
3708 for (kind=0; kind<4; kind++) {
3709 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3710 ExpandPathName(appData.pixmapDirectory),
3711 piece > (int) WhiteKing ? "w" : "",
3712 pieceBitmapNames[piece],
3714 if (appData.debugMode) {
3715 fprintf(stderr, _("(File:%s:) "), buf);
3717 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3718 &(xpmPieceBitmap2[kind][piece]),
3719 NULL, &attr)) != 0) {
3720 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3721 // [HGM] missing: read of unorthodox piece failed; substitute King.
3722 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3723 ExpandPathName(appData.pixmapDirectory),
3725 if (appData.debugMode) {
3726 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3728 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3729 &(xpmPieceBitmap2[kind][piece]),
3733 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3738 if(piece <= (int) WhiteKing)
3739 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3742 /* Load light and dark squares */
3743 /* If the LSQ and DSQ pieces don't exist, we will
3744 draw them with solid squares. */
3745 fprintf(stderr, _("light square "));
3746 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3747 if (access(buf, 0) != 0) {
3751 if (appData.debugMode)
3752 fprintf(stderr, _("(File:%s:) "), buf);
3754 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3755 &xpmLightSquare, NULL, &attr)) != 0) {
3756 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3759 fprintf(stderr, _("dark square "));
3760 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3761 ExpandPathName(appData.pixmapDirectory), ss);
3762 if (appData.debugMode) {
3763 fprintf(stderr, _("(File:%s:) "), buf);
3765 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3766 &xpmDarkSquare, NULL, &attr)) != 0) {
3767 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3771 xpmJailSquare = xpmLightSquare;
3772 fprintf(stderr, _("Done.\n"));
3774 oldVariant = -1; // kludge to force re-makig of animation masks
3775 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3778 #endif /* HAVE_LIBXPM */
3781 /* No built-in bitmaps */
3786 u_int ss = squareSize;
3788 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3791 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3792 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3793 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3794 pieceBitmapNames[piece],
3795 ss, kind == SOLID ? 's' : 'o');
3796 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3797 if(piece <= (int)WhiteKing)
3798 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3802 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3806 /* With built-in bitmaps */
3809 BuiltInBits* bib = builtInBits;
3812 u_int ss = squareSize;
3814 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3817 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3819 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3820 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3821 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3822 pieceBitmapNames[piece],
3823 ss, kind == SOLID ? 's' : 'o');
3824 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3825 bib->bits[kind][piece], ss, ss);
3826 if(piece <= (int)WhiteKing)
3827 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3831 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3836 void ReadBitmap(pm, name, bits, wreq, hreq)
3839 unsigned char bits[];
3845 char msg[MSG_SIZ], fullname[MSG_SIZ];
3847 if (*appData.bitmapDirectory != NULLCHAR) {
3848 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3849 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3850 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3851 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3852 &w, &h, pm, &x_hot, &y_hot);
3853 fprintf(stderr, "load %s\n", name);
3854 if (errcode != BitmapSuccess) {
3856 case BitmapOpenFailed:
3857 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3859 case BitmapFileInvalid:
3860 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3862 case BitmapNoMemory:
3863 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3867 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3871 fprintf(stderr, _("%s: %s...using built-in\n"),
3873 } else if (w != wreq || h != hreq) {
3875 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3876 programName, fullname, w, h, wreq, hreq);
3882 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3891 if (lineGap == 0) return;
3893 /* [HR] Split this into 2 loops for non-square boards. */
3895 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3896 gridSegments[i].x1 = 0;
3897 gridSegments[i].x2 =
3898 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3899 gridSegments[i].y1 = gridSegments[i].y2
3900 = lineGap / 2 + (i * (squareSize + lineGap));
3903 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3904 gridSegments[j + i].y1 = 0;
3905 gridSegments[j + i].y2 =
3906 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3907 gridSegments[j + i].x1 = gridSegments[j + i].x2
3908 = lineGap / 2 + (j * (squareSize + lineGap));
3912 static void MenuBarSelect(w, addr, index)
3917 XtActionProc proc = (XtActionProc) addr;
3919 (proc)(NULL, NULL, NULL, NULL);
3922 void CreateMenuBarPopup(parent, name, mb)
3932 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3935 XtSetArg(args[j], XtNleftMargin, 20); j++;
3936 XtSetArg(args[j], XtNrightMargin, 20); j++;
3938 while (mi->string != NULL) {
3939 if (strcmp(mi->string, "----") == 0) {
3940 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3943 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3944 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3946 XtAddCallback(entry, XtNcallback,
3947 (XtCallbackProc) MenuBarSelect,
3948 (caddr_t) mi->proc);
3954 Widget CreateMenuBar(mb, boardWidth)
3958 int i, j, nr = 0, wtot = 0, widths[10];
3961 char menuName[MSG_SIZ];
3966 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3967 XtSetArg(args[j], XtNvSpace, 0); j++;
3968 XtSetArg(args[j], XtNborderWidth, 0); j++;
3969 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3970 formWidget, args, j);
3972 while (mb->name != NULL) {
3973 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3974 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3976 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3979 shortName[0] = mb->name[0];
3980 shortName[1] = NULLCHAR;
3981 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3984 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3987 XtSetArg(args[j], XtNborderWidth, 0); j++;
3988 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3990 CreateMenuBarPopup(menuBar, menuName, mb);
3992 XtSetArg(args[j], XtNwidth, &w); j++;
3993 XtGetValues(mb->subMenu, args, j);
3994 wtot += mb->textWidth = widths[nr++] = w;
3997 while(wtot > boardWidth - 40) {
3999 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
4003 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
4005 XtSetArg(args[j], XtNwidth, widths[i]); j++;
4006 XtSetValues(ma[i].subMenu, args, j);
4011 Widget CreateButtonBar(mi)
4015 Widget button, buttonBar;
4019 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4021 XtSetArg(args[j], XtNhSpace, 0); j++;
4023 XtSetArg(args[j], XtNborderWidth, 0); j++;
4024 XtSetArg(args[j], XtNvSpace, 0); j++;
4025 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4026 formWidget, args, j);
4028 while (mi->string != NULL) {
4031 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4032 XtSetArg(args[j], XtNborderWidth, 0); j++;
4034 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4035 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4036 buttonBar, args, j);
4037 XtAddCallback(button, XtNcallback,
4038 (XtCallbackProc) MenuBarSelect,
4039 (caddr_t) mi->proc);
4046 CreatePieceMenu(name, color)
4053 ChessSquare selection;
4055 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4056 boardWidget, args, 0);
4058 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4059 String item = pieceMenuStrings[color][i];
4061 if (strcmp(item, "----") == 0) {
4062 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4065 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4066 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4068 selection = pieceMenuTranslation[color][i];
4069 XtAddCallback(entry, XtNcallback,
4070 (XtCallbackProc) PieceMenuSelect,
4071 (caddr_t) selection);
4072 if (selection == WhitePawn || selection == BlackPawn) {
4073 XtSetArg(args[0], XtNpopupOnEntry, entry);
4074 XtSetValues(menu, args, 1);
4087 ChessSquare selection;
4089 whitePieceMenu = CreatePieceMenu("menuW", 0);
4090 blackPieceMenu = CreatePieceMenu("menuB", 1);
4092 XtRegisterGrabAction(PieceMenuPopup, True,
4093 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4094 GrabModeAsync, GrabModeAsync);
4096 XtSetArg(args[0], XtNlabel, _("Drop"));
4097 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4098 boardWidget, args, 1);
4099 for (i = 0; i < DROP_MENU_SIZE; i++) {
4100 String item = dropMenuStrings[i];
4102 if (strcmp(item, "----") == 0) {
4103 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4106 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4107 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4109 selection = dropMenuTranslation[i];
4110 XtAddCallback(entry, XtNcallback,
4111 (XtCallbackProc) DropMenuSelect,
4112 (caddr_t) selection);
4117 void SetupDropMenu()
4125 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4126 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4127 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4128 dmEnables[i].piece);
4129 XtSetSensitive(entry, p != NULL || !appData.testLegality
4130 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4131 && !appData.icsActive));
4133 while (p && *p++ == dmEnables[i].piece) count++;
4134 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4136 XtSetArg(args[j], XtNlabel, label); j++;
4137 XtSetValues(entry, args, j);
4141 void PieceMenuPopup(w, event, params, num_params)
4145 Cardinal *num_params;
4147 String whichMenu; int menuNr = -2;
4148 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4149 if (event->type == ButtonRelease)
4150 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4151 else if (event->type == ButtonPress)
4152 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4154 case 0: whichMenu = params[0]; break;
4155 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4157 case -1: if (errorUp) ErrorPopDown();
4160 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4163 static void PieceMenuSelect(w, piece, junk)
4168 if (pmFromX < 0 || pmFromY < 0) return;
4169 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4172 static void DropMenuSelect(w, piece, junk)
4177 if (pmFromX < 0 || pmFromY < 0) return;
4178 DropMenuEvent(piece, pmFromX, pmFromY);
4181 void WhiteClock(w, event, prms, nprms)
4187 shiftKey = prms[0][0] & 1;
4191 void BlackClock(w, event, prms, nprms)
4197 shiftKey = prms[0][0] & 1;
4203 * If the user selects on a border boundary, return -1; if off the board,
4204 * return -2. Otherwise map the event coordinate to the square.
4206 int EventToSquare(x, limit)
4214 if ((x % (squareSize + lineGap)) >= squareSize)
4216 x /= (squareSize + lineGap);
4222 static void do_flash_delay(msec)
4228 static void drawHighlight(file, rank, gc)
4234 if (lineGap == 0) return;
4237 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4238 (squareSize + lineGap);
4239 y = lineGap/2 + rank * (squareSize + lineGap);
4241 x = lineGap/2 + file * (squareSize + lineGap);
4242 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4243 (squareSize + lineGap);
4246 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4247 squareSize+lineGap, squareSize+lineGap);
4250 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4251 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4254 SetHighlights(fromX, fromY, toX, toY)
4255 int fromX, fromY, toX, toY;
4257 if (hi1X != fromX || hi1Y != fromY) {
4258 if (hi1X >= 0 && hi1Y >= 0) {
4259 drawHighlight(hi1X, hi1Y, lineGC);
4261 } // [HGM] first erase both, then draw new!
4262 if (hi2X != toX || hi2Y != toY) {
4263 if (hi2X >= 0 && hi2Y >= 0) {
4264 drawHighlight(hi2X, hi2Y, lineGC);
4267 if (hi1X != fromX || hi1Y != fromY) {
4268 if (fromX >= 0 && fromY >= 0) {
4269 drawHighlight(fromX, fromY, highlineGC);
4272 if (hi2X != toX || hi2Y != toY) {
4273 if (toX >= 0 && toY >= 0) {
4274 drawHighlight(toX, toY, highlineGC);
4286 SetHighlights(-1, -1, -1, -1);
4291 SetPremoveHighlights(fromX, fromY, toX, toY)
4292 int fromX, fromY, toX, toY;
4294 if (pm1X != fromX || pm1Y != fromY) {
4295 if (pm1X >= 0 && pm1Y >= 0) {
4296 drawHighlight(pm1X, pm1Y, lineGC);
4298 if (fromX >= 0 && fromY >= 0) {
4299 drawHighlight(fromX, fromY, prelineGC);
4302 if (pm2X != toX || pm2Y != toY) {
4303 if (pm2X >= 0 && pm2Y >= 0) {
4304 drawHighlight(pm2X, pm2Y, lineGC);
4306 if (toX >= 0 && toY >= 0) {
4307 drawHighlight(toX, toY, prelineGC);
4317 ClearPremoveHighlights()
4319 SetPremoveHighlights(-1, -1, -1, -1);
4322 static int CutOutSquare(x, y, x0, y0, kind)
4323 int x, y, *x0, *y0, kind;
4325 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4326 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4328 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4329 if(textureW[kind] < W*squareSize)
4330 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4332 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4333 if(textureH[kind] < H*squareSize)
4334 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4336 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4340 static void BlankSquare(x, y, color, piece, dest, fac)
4341 int x, y, color, fac;
4344 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4346 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4347 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4348 squareSize, squareSize, x*fac, y*fac);
4350 if (useImages && useImageSqs) {
4354 pm = xpmLightSquare;
4359 case 2: /* neutral */
4364 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4365 squareSize, squareSize, x*fac, y*fac);
4375 case 2: /* neutral */
4380 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4385 I split out the routines to draw a piece so that I could
4386 make a generic flash routine.
4388 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4390 int square_color, x, y;
4393 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4394 switch (square_color) {
4396 case 2: /* neutral */
4398 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4399 ? *pieceToOutline(piece)
4400 : *pieceToSolid(piece),
4401 dest, bwPieceGC, 0, 0,
4402 squareSize, squareSize, x, y);
4405 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4406 ? *pieceToSolid(piece)
4407 : *pieceToOutline(piece),
4408 dest, wbPieceGC, 0, 0,
4409 squareSize, squareSize, x, y);
4414 static void monoDrawPiece(piece, square_color, x, y, dest)
4416 int square_color, x, y;
4419 switch (square_color) {
4421 case 2: /* neutral */
4423 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4424 ? *pieceToOutline(piece)
4425 : *pieceToSolid(piece),
4426 dest, bwPieceGC, 0, 0,
4427 squareSize, squareSize, x, y, 1);
4430 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4431 ? *pieceToSolid(piece)
4432 : *pieceToOutline(piece),
4433 dest, wbPieceGC, 0, 0,
4434 squareSize, squareSize, x, y, 1);
4439 static void colorDrawPiece(piece, square_color, x, y, dest)
4441 int square_color, x, y;
4444 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4445 switch (square_color) {
4447 XCopyPlane(xDisplay, *pieceToSolid(piece),
4448 dest, (int) piece < (int) BlackPawn
4449 ? wlPieceGC : blPieceGC, 0, 0,
4450 squareSize, squareSize, x, y, 1);
4453 XCopyPlane(xDisplay, *pieceToSolid(piece),
4454 dest, (int) piece < (int) BlackPawn
4455 ? wdPieceGC : bdPieceGC, 0, 0,
4456 squareSize, squareSize, x, y, 1);
4458 case 2: /* neutral */
4460 XCopyPlane(xDisplay, *pieceToSolid(piece),
4461 dest, (int) piece < (int) BlackPawn
4462 ? wjPieceGC : bjPieceGC, 0, 0,
4463 squareSize, squareSize, x, y, 1);
4468 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4470 int square_color, x, y;
4473 int kind, p = piece;
4475 switch (square_color) {
4477 case 2: /* neutral */
4479 if ((int)piece < (int) BlackPawn) {
4487 if ((int)piece < (int) BlackPawn) {
4495 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4496 if(useTexture & square_color+1) {
4497 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4498 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4499 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4500 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4501 XSetClipMask(xDisplay, wlPieceGC, None);
4502 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4504 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4505 dest, wlPieceGC, 0, 0,
4506 squareSize, squareSize, x, y);
4509 typedef void (*DrawFunc)();
4511 DrawFunc ChooseDrawFunc()
4513 if (appData.monoMode) {
4514 if (DefaultDepth(xDisplay, xScreen) == 1) {
4515 return monoDrawPiece_1bit;
4517 return monoDrawPiece;
4521 return colorDrawPieceImage;
4523 return colorDrawPiece;
4527 /* [HR] determine square color depending on chess variant. */
4528 static int SquareColor(row, column)
4533 if (gameInfo.variant == VariantXiangqi) {
4534 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4536 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4538 } else if (row <= 4) {
4544 square_color = ((column + row) % 2) == 1;
4547 /* [hgm] holdings: next line makes all holdings squares light */
4548 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4550 return square_color;
4553 void DrawSquare(row, column, piece, do_flash)
4554 int row, column, do_flash;
4557 int square_color, x, y, direction, font_ascent, font_descent;
4560 XCharStruct overall;
4564 /* Calculate delay in milliseconds (2-delays per complete flash) */
4565 flash_delay = 500 / appData.flashRate;
4568 x = lineGap + ((BOARD_WIDTH-1)-column) *
4569 (squareSize + lineGap);
4570 y = lineGap + row * (squareSize + lineGap);
4572 x = lineGap + column * (squareSize + lineGap);
4573 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4574 (squareSize + lineGap);
4577 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4579 square_color = SquareColor(row, column);
4581 if ( // [HGM] holdings: blank out area between board and holdings
4582 column == BOARD_LEFT-1 || column == BOARD_RGHT
4583 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4584 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4585 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4587 // [HGM] print piece counts next to holdings
4588 string[1] = NULLCHAR;
4589 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4590 string[0] = '0' + piece;
4591 XTextExtents(countFontStruct, string, 1, &direction,
4592 &font_ascent, &font_descent, &overall);
4593 if (appData.monoMode) {
4594 XDrawImageString(xDisplay, xBoardWindow, countGC,
4595 x + squareSize - overall.width - 2,
4596 y + font_ascent + 1, string, 1);
4598 XDrawString(xDisplay, xBoardWindow, countGC,
4599 x + squareSize - overall.width - 2,
4600 y + font_ascent + 1, string, 1);
4603 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4604 string[0] = '0' + piece;
4605 XTextExtents(countFontStruct, string, 1, &direction,
4606 &font_ascent, &font_descent, &overall);
4607 if (appData.monoMode) {
4608 XDrawImageString(xDisplay, xBoardWindow, countGC,
4609 x + 2, y + font_ascent + 1, string, 1);
4611 XDrawString(xDisplay, xBoardWindow, countGC,
4612 x + 2, y + font_ascent + 1, string, 1);
4616 if (piece == EmptySquare || appData.blindfold) {
4617 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4619 drawfunc = ChooseDrawFunc();
4621 if (do_flash && appData.flashCount > 0) {
4622 for (i=0; i<appData.flashCount; ++i) {
4623 drawfunc(piece, square_color, x, y, xBoardWindow);
4624 XSync(xDisplay, False);
4625 do_flash_delay(flash_delay);
4627 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4628 XSync(xDisplay, False);
4629 do_flash_delay(flash_delay);
4632 drawfunc(piece, square_color, x, y, xBoardWindow);
4636 string[1] = NULLCHAR;
4637 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4638 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4639 string[0] = 'a' + column - BOARD_LEFT;
4640 XTextExtents(coordFontStruct, string, 1, &direction,
4641 &font_ascent, &font_descent, &overall);
4642 if (appData.monoMode) {
4643 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4644 x + squareSize - overall.width - 2,
4645 y + squareSize - font_descent - 1, string, 1);
4647 XDrawString(xDisplay, xBoardWindow, coordGC,
4648 x + squareSize - overall.width - 2,
4649 y + squareSize - font_descent - 1, string, 1);
4652 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4653 string[0] = ONE + row;
4654 XTextExtents(coordFontStruct, string, 1, &direction,
4655 &font_ascent, &font_descent, &overall);
4656 if (appData.monoMode) {
4657 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4658 x + 2, y + font_ascent + 1, string, 1);
4660 XDrawString(xDisplay, xBoardWindow, coordGC,
4661 x + 2, y + font_ascent + 1, string, 1);
4664 if(!partnerUp && marker[row][column]) {
4665 if(appData.monoMode) {
4666 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4667 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4668 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4669 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4671 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4672 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4677 /* Why is this needed on some versions of X? */
4678 void EventProc(widget, unused, event)
4683 if (!XtIsRealized(widget))
4686 switch (event->type) {
4688 if (event->xexpose.count > 0) return; /* no clipping is done */
4689 XDrawPosition(widget, True, NULL);
4690 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4691 flipView = !flipView; partnerUp = !partnerUp;
4692 XDrawPosition(widget, True, NULL);
4693 flipView = !flipView; partnerUp = !partnerUp;
4697 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4704 void DrawPosition(fullRedraw, board)
4705 /*Boolean*/int fullRedraw;
4708 XDrawPosition(boardWidget, fullRedraw, board);
4711 /* Returns 1 if there are "too many" differences between b1 and b2
4712 (i.e. more than 1 move was made) */
4713 static int too_many_diffs(b1, b2)
4719 for (i=0; i<BOARD_HEIGHT; ++i) {
4720 for (j=0; j<BOARD_WIDTH; ++j) {
4721 if (b1[i][j] != b2[i][j]) {
4722 if (++c > 4) /* Castling causes 4 diffs */
4730 /* Matrix describing castling maneuvers */
4731 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4732 static int castling_matrix[4][5] = {
4733 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4734 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4735 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4736 { 7, 7, 4, 5, 6 } /* 0-0, black */
4739 /* Checks whether castling occurred. If it did, *rrow and *rcol
4740 are set to the destination (row,col) of the rook that moved.
4742 Returns 1 if castling occurred, 0 if not.
4744 Note: Only handles a max of 1 castling move, so be sure
4745 to call too_many_diffs() first.
4747 static int check_castle_draw(newb, oldb, rrow, rcol)
4754 /* For each type of castling... */
4755 for (i=0; i<4; ++i) {
4756 r = castling_matrix[i];
4758 /* Check the 4 squares involved in the castling move */
4760 for (j=1; j<=4; ++j) {
4761 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4768 /* All 4 changed, so it must be a castling move */
4777 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4778 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4780 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4783 void DrawSeekBackground( int left, int top, int right, int bottom )
4785 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4788 void DrawSeekText(char *buf, int x, int y)
4790 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4793 void DrawSeekDot(int x, int y, int colorNr)
4795 int square = colorNr & 0x80;
4798 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4800 XFillRectangle(xDisplay, xBoardWindow, color,
4801 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4803 XFillArc(xDisplay, xBoardWindow, color,
4804 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4807 static int damage[2][BOARD_RANKS][BOARD_FILES];
4810 * event handler for redrawing the board
4812 void XDrawPosition(w, repaint, board)
4814 /*Boolean*/int repaint;
4818 static int lastFlipView = 0;
4819 static int lastBoardValid[2] = {0, 0};
4820 static Board lastBoard[2];
4823 int nr = twoBoards*partnerUp;
4825 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4827 if (board == NULL) {
4828 if (!lastBoardValid[nr]) return;
4829 board = lastBoard[nr];
4831 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4832 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4833 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4838 * It would be simpler to clear the window with XClearWindow()
4839 * but this causes a very distracting flicker.
4842 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4844 if ( lineGap && IsDrawArrowEnabled())
4845 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4846 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4848 /* If too much changes (begin observing new game, etc.), don't
4850 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4852 /* Special check for castling so we don't flash both the king
4853 and the rook (just flash the king). */
4855 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4856 /* Draw rook with NO flashing. King will be drawn flashing later */
4857 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4858 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4862 /* First pass -- Draw (newly) empty squares and repair damage.
4863 This prevents you from having a piece show up twice while it
4864 is flashing on its new square */
4865 for (i = 0; i < BOARD_HEIGHT; i++)
4866 for (j = 0; j < BOARD_WIDTH; j++)
4867 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4868 || damage[nr][i][j]) {
4869 DrawSquare(i, j, board[i][j], 0);
4870 damage[nr][i][j] = False;
4873 /* Second pass -- Draw piece(s) in new position and flash them */
4874 for (i = 0; i < BOARD_HEIGHT; i++)
4875 for (j = 0; j < BOARD_WIDTH; j++)
4876 if (board[i][j] != lastBoard[nr][i][j]) {
4877 DrawSquare(i, j, board[i][j], do_flash);
4881 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4882 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4883 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4885 for (i = 0; i < BOARD_HEIGHT; i++)
4886 for (j = 0; j < BOARD_WIDTH; j++) {
4887 DrawSquare(i, j, board[i][j], 0);
4888 damage[nr][i][j] = False;
4892 CopyBoard(lastBoard[nr], board);
4893 lastBoardValid[nr] = 1;
4894 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4895 lastFlipView = flipView;
4897 /* Draw highlights */
4898 if (pm1X >= 0 && pm1Y >= 0) {
4899 drawHighlight(pm1X, pm1Y, prelineGC);
4901 if (pm2X >= 0 && pm2Y >= 0) {
4902 drawHighlight(pm2X, pm2Y, prelineGC);
4904 if (hi1X >= 0 && hi1Y >= 0) {
4905 drawHighlight(hi1X, hi1Y, highlineGC);
4907 if (hi2X >= 0 && hi2Y >= 0) {
4908 drawHighlight(hi2X, hi2Y, highlineGC);
4910 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4912 /* If piece being dragged around board, must redraw that too */
4915 XSync(xDisplay, False);
4920 * event handler for redrawing the board
4922 void DrawPositionProc(w, event, prms, nprms)
4928 XDrawPosition(w, True, NULL);
4933 * event handler for parsing user moves
4935 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4936 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4937 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4938 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4939 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4940 // and at the end FinishMove() to perform the move after optional promotion popups.
4941 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4942 void HandleUserMove(w, event, prms, nprms)
4948 if (w != boardWidget || errorExitStatus != -1) return;
4949 if(nprms) shiftKey = !strcmp(prms[0], "1");
4952 if (event->type == ButtonPress) {
4953 XtPopdown(promotionShell);
4954 XtDestroyWidget(promotionShell);
4955 promotionUp = False;
4963 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4964 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4965 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4968 void AnimateUserMove (Widget w, XEvent * event,
4969 String * params, Cardinal * nParams)
4971 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4972 DragPieceMove(event->xmotion.x, event->xmotion.y);
4975 void HandlePV (Widget w, XEvent * event,
4976 String * params, Cardinal * nParams)
4977 { // [HGM] pv: walk PV
4978 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4981 static int savedIndex; /* gross that this is global */
4983 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4986 XawTextPosition index, dummy;
4989 XawTextGetSelectionPos(w, &index, &dummy);
4990 XtSetArg(arg, XtNstring, &val);
4991 XtGetValues(w, &arg, 1);
4992 ReplaceComment(savedIndex, val);
4993 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4994 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4997 void EditCommentPopUp(index, title, text)
5002 if (text == NULL) text = "";
5003 NewCommentPopup(title, text, index);
5006 void ICSInputBoxPopUp()
5011 extern Option boxOptions[];
5013 void ICSInputSendText()
5020 edit = boxOptions[0].handle;
5022 XtSetArg(args[j], XtNstring, &val); j++;
5023 XtGetValues(edit, args, j);
5025 SendMultiLineToICS(val);
5026 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5027 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5030 void ICSInputBoxPopDown()
5035 void CommentPopUp(title, text)
5038 savedIndex = currentMove; // [HGM] vari
5039 NewCommentPopup(title, text, currentMove);
5042 void CommentPopDown()
5047 static char *openName;
5052 (void) (*fileProc)(openFP, 0, openName);
5055 void FileNamePopUp(label, def, filter, proc, openMode)
5062 fileProc = proc; /* I can't see a way not */
5063 fileOpenMode = openMode; /* to use globals here */
5064 { // [HGM] use file-selector dialog stolen from Ghostview
5065 int index; // this is not supported yet
5066 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5067 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5068 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5069 ScheduleDelayedEvent(&DelayedLoad, 50);
5073 void FileNamePopDown()
5075 if (!filenameUp) return;
5076 XtPopdown(fileNameShell);
5077 XtDestroyWidget(fileNameShell);
5082 void FileNameCallback(w, client_data, call_data)
5084 XtPointer client_data, call_data;
5089 XtSetArg(args[0], XtNlabel, &name);
5090 XtGetValues(w, args, 1);
5092 if (strcmp(name, _("cancel")) == 0) {
5097 FileNameAction(w, NULL, NULL, NULL);
5100 void FileNameAction(w, event, prms, nprms)
5112 name = XawDialogGetValueString(w = XtParent(w));
5114 if ((name != NULL) && (*name != NULLCHAR)) {
5115 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5116 XtPopdown(w = XtParent(XtParent(w)));
5120 p = strrchr(buf, ' ');
5127 fullname = ExpandPathName(buf);
5129 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5132 f = fopen(fullname, fileOpenMode);
5134 DisplayError(_("Failed to open file"), errno);
5136 (void) (*fileProc)(f, index, buf);
5143 XtPopdown(w = XtParent(XtParent(w)));
5149 void PromotionPopUp()
5152 Widget dialog, layout;
5154 Dimension bw_width, pw_width;
5156 char *PromoChars = "wglcqrbnkac+=\0";
5159 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5160 XtGetValues(boardWidget, args, j);
5163 XtSetArg(args[j], XtNresizable, True); j++;
5164 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5166 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5167 shellWidget, args, j);
5169 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5170 layoutArgs, XtNumber(layoutArgs));
5173 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5174 XtSetArg(args[j], XtNborderWidth, 0); j++;
5175 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5178 if(gameInfo.variant != VariantShogi) {
5179 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5180 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5181 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5182 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5183 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5185 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5186 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5187 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5188 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5190 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5191 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5192 gameInfo.variant == VariantGiveaway) {
5193 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5195 if(gameInfo.variant == VariantCapablanca ||
5196 gameInfo.variant == VariantGothic ||
5197 gameInfo.variant == VariantCapaRandom) {
5198 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5199 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5201 } else // [HGM] shogi
5203 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5204 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5206 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5208 XtRealizeWidget(promotionShell);
5209 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5212 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5213 XtGetValues(promotionShell, args, j);
5215 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5216 lineGap + squareSize/3 +
5217 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5218 0 : 6*(squareSize + lineGap)), &x, &y);
5221 XtSetArg(args[j], XtNx, x); j++;
5222 XtSetArg(args[j], XtNy, y); j++;
5223 XtSetValues(promotionShell, args, j);
5225 XtPopup(promotionShell, XtGrabNone);
5230 void PromotionPopDown()
5232 if (!promotionUp) return;
5233 XtPopdown(promotionShell);
5234 XtDestroyWidget(promotionShell);
5235 promotionUp = False;
5238 void PromotionCallback(w, client_data, call_data)
5240 XtPointer client_data, call_data;
5242 int promoChar = * (const char *) client_data;
5246 if (fromX == -1) return;
5253 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5255 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5256 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5261 void ErrorCallback(w, client_data, call_data)
5263 XtPointer client_data, call_data;
5266 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5268 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5274 if (!errorUp) return;
5276 XtPopdown(errorShell);
5277 XtDestroyWidget(errorShell);
5278 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5281 void ErrorPopUp(title, label, modal)
5282 char *title, *label;
5286 Widget dialog, layout;
5290 Dimension bw_width, pw_width;
5291 Dimension pw_height;
5295 XtSetArg(args[i], XtNresizable, True); i++;
5296 XtSetArg(args[i], XtNtitle, title); i++;
5298 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5299 shellWidget, args, i);
5301 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5302 layoutArgs, XtNumber(layoutArgs));
5305 XtSetArg(args[i], XtNlabel, label); i++;
5306 XtSetArg(args[i], XtNborderWidth, 0); i++;
5307 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5310 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5312 XtRealizeWidget(errorShell);
5313 CatchDeleteWindow(errorShell, "ErrorPopDown");
5316 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5317 XtGetValues(boardWidget, args, i);
5319 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5320 XtSetArg(args[i], XtNheight, &pw_height); i++;
5321 XtGetValues(errorShell, args, i);
5324 /* This code seems to tickle an X bug if it is executed too soon
5325 after xboard starts up. The coordinates get transformed as if
5326 the main window was positioned at (0, 0).
5328 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5329 0 - pw_height + squareSize / 3, &x, &y);
5331 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5332 RootWindowOfScreen(XtScreen(boardWidget)),
5333 (bw_width - pw_width) / 2,
5334 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5338 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5341 XtSetArg(args[i], XtNx, x); i++;
5342 XtSetArg(args[i], XtNy, y); i++;
5343 XtSetValues(errorShell, args, i);
5346 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5349 /* Disable all user input other than deleting the window */
5350 static int frozen = 0;
5354 /* Grab by a widget that doesn't accept input */
5355 XtAddGrab(messageWidget, TRUE, FALSE);
5359 /* Undo a FreezeUI */
5362 if (!frozen) return;
5363 XtRemoveGrab(messageWidget);
5367 char *ModeToWidgetName(mode)
5371 case BeginningOfGame:
5372 if (appData.icsActive)
5373 return "menuMode.ICS Client";
5374 else if (appData.noChessProgram ||
5375 *appData.cmailGameName != NULLCHAR)
5376 return "menuMode.Edit Game";
5378 return "menuMode.Machine Black";
5379 case MachinePlaysBlack:
5380 return "menuMode.Machine Black";
5381 case MachinePlaysWhite:
5382 return "menuMode.Machine White";
5384 return "menuMode.Analysis Mode";
5386 return "menuMode.Analyze File";
5387 case TwoMachinesPlay:
5388 return "menuMode.Two Machines";
5390 return "menuMode.Edit Game";
5391 case PlayFromGameFile:
5392 return "menuFile.Load Game";
5394 return "menuMode.Edit Position";
5396 return "menuMode.Training";
5397 case IcsPlayingWhite:
5398 case IcsPlayingBlack:
5402 return "menuMode.ICS Client";
5409 void ModeHighlight()
5412 static int oldPausing = FALSE;
5413 static GameMode oldmode = (GameMode) -1;
5416 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5418 if (pausing != oldPausing) {
5419 oldPausing = pausing;
5421 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5423 XtSetArg(args[0], XtNleftBitmap, None);
5425 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5428 if (appData.showButtonBar) {
5429 /* Always toggle, don't set. Previous code messes up when
5430 invoked while the button is pressed, as releasing it
5431 toggles the state again. */
5434 XtSetArg(args[0], XtNbackground, &oldbg);
5435 XtSetArg(args[1], XtNforeground, &oldfg);
5436 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5438 XtSetArg(args[0], XtNbackground, oldfg);
5439 XtSetArg(args[1], XtNforeground, oldbg);
5441 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5445 wname = ModeToWidgetName(oldmode);
5446 if (wname != NULL) {
5447 XtSetArg(args[0], XtNleftBitmap, None);
5448 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5450 wname = ModeToWidgetName(gameMode);
5451 if (wname != NULL) {
5452 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5453 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5456 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5457 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5459 /* Maybe all the enables should be handled here, not just this one */
5460 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5461 gameMode == Training || gameMode == PlayFromGameFile);
5466 * Button/menu procedures
5468 void ResetProc(w, event, prms, nprms)
5477 int LoadGamePopUp(f, gameNumber, title)
5482 cmailMsgLoaded = FALSE;
5483 if (gameNumber == 0) {
5484 int error = GameListBuild(f);
5486 DisplayError(_("Cannot build game list"), error);
5487 } else if (!ListEmpty(&gameList) &&
5488 ((ListGame *) gameList.tailPred)->number > 1) {
5489 GameListPopUp(f, title);
5495 return LoadGame(f, gameNumber, title, FALSE);
5498 void LoadGameProc(w, event, prms, nprms)
5504 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5507 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5510 void LoadNextGameProc(w, event, prms, nprms)
5519 void LoadPrevGameProc(w, event, prms, nprms)
5528 void ReloadGameProc(w, event, prms, nprms)
5537 void LoadNextPositionProc(w, event, prms, nprms)
5546 void LoadPrevPositionProc(w, event, prms, nprms)
5555 void ReloadPositionProc(w, event, prms, nprms)
5564 void LoadPositionProc(w, event, prms, nprms)
5570 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5573 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5576 void SaveGameProc(w, event, prms, nprms)
5582 FileNamePopUp(_("Save game file name?"),
5583 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5584 appData.oldSaveStyle ? ".game" : ".pgn",
5588 void SavePositionProc(w, event, prms, nprms)
5594 FileNamePopUp(_("Save position file name?"),
5595 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5596 appData.oldSaveStyle ? ".pos" : ".fen",
5600 void ReloadCmailMsgProc(w, event, prms, nprms)
5606 ReloadCmailMsgEvent(FALSE);
5609 void MailMoveProc(w, event, prms, nprms)
5618 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5619 char *selected_fen_position=NULL;
5622 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5623 Atom *type_return, XtPointer *value_return,
5624 unsigned long *length_return, int *format_return)
5626 char *selection_tmp;
5628 if (!selected_fen_position) return False; /* should never happen */
5629 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5630 /* note: since no XtSelectionDoneProc was registered, Xt will
5631 * automatically call XtFree on the value returned. So have to
5632 * make a copy of it allocated with XtMalloc */
5633 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5634 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5636 *value_return=selection_tmp;
5637 *length_return=strlen(selection_tmp);
5638 *type_return=*target;
5639 *format_return = 8; /* bits per byte */
5641 } else if (*target == XA_TARGETS(xDisplay)) {
5642 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5643 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5644 targets_tmp[1] = XA_STRING;
5645 *value_return = targets_tmp;
5646 *type_return = XA_ATOM;
5649 // This code leads to a read of value_return out of bounds on 64-bit systems.
5650 // Other code which I have seen always sets *format_return to 32 independent of
5651 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5652 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5653 *format_return = 8 * sizeof(Atom);
5654 if (*format_return > 32) {
5655 *length_return *= *format_return / 32;
5656 *format_return = 32;
5659 *format_return = 32;
5667 /* note: when called from menu all parameters are NULL, so no clue what the
5668 * Widget which was clicked on was, or what the click event was
5670 void CopyPositionProc(w, event, prms, nprms)
5677 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5678 * have a notion of a position that is selected but not copied.
5679 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5681 if(gameMode == EditPosition) EditPositionDone(TRUE);
5682 if (selected_fen_position) free(selected_fen_position);
5683 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5684 if (!selected_fen_position) return;
5685 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5687 SendPositionSelection,
5688 NULL/* lose_ownership_proc */ ,
5689 NULL/* transfer_done_proc */);
5690 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5692 SendPositionSelection,
5693 NULL/* lose_ownership_proc */ ,
5694 NULL/* transfer_done_proc */);
5697 /* function called when the data to Paste is ready */
5699 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5700 Atom *type, XtPointer value, unsigned long *len, int *format)
5703 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5704 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5705 EditPositionPasteFEN(fenstr);
5709 /* called when Paste Position button is pressed,
5710 * all parameters will be NULL */
5711 void PastePositionProc(w, event, prms, nprms)
5717 XtGetSelectionValue(menuBarWidget,
5718 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5719 /* (XtSelectionCallbackProc) */ PastePositionCB,
5720 NULL, /* client_data passed to PastePositionCB */
5722 /* better to use the time field from the event that triggered the
5723 * call to this function, but that isn't trivial to get
5731 SendGameSelection(Widget w, Atom *selection, Atom *target,
5732 Atom *type_return, XtPointer *value_return,
5733 unsigned long *length_return, int *format_return)
5735 char *selection_tmp;
5737 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5738 FILE* f = fopen(gameCopyFilename, "r");
5741 if (f == NULL) return False;
5745 selection_tmp = XtMalloc(len + 1);
5746 count = fread(selection_tmp, 1, len, f);
5749 XtFree(selection_tmp);
5752 selection_tmp[len] = NULLCHAR;
5753 *value_return = selection_tmp;
5754 *length_return = len;
5755 *type_return = *target;
5756 *format_return = 8; /* bits per byte */
5758 } else if (*target == XA_TARGETS(xDisplay)) {
5759 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5760 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5761 targets_tmp[1] = XA_STRING;
5762 *value_return = targets_tmp;
5763 *type_return = XA_ATOM;
5766 // This code leads to a read of value_return out of bounds on 64-bit systems.
5767 // Other code which I have seen always sets *format_return to 32 independent of
5768 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5769 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5770 *format_return = 8 * sizeof(Atom);
5771 if (*format_return > 32) {
5772 *length_return *= *format_return / 32;
5773 *format_return = 32;
5776 *format_return = 32;
5784 void CopySomething()
5787 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5788 * have a notion of a game that is selected but not copied.
5789 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5791 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5794 NULL/* lose_ownership_proc */ ,
5795 NULL/* transfer_done_proc */);
5796 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5799 NULL/* lose_ownership_proc */ ,
5800 NULL/* transfer_done_proc */);
5803 /* note: when called from menu all parameters are NULL, so no clue what the
5804 * Widget which was clicked on was, or what the click event was
5806 void CopyGameProc(w, event, prms, nprms)
5814 ret = SaveGameToFile(gameCopyFilename, FALSE);
5820 void CopyGameListProc(w, event, prms, nprms)
5826 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5830 /* function called when the data to Paste is ready */
5832 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5833 Atom *type, XtPointer value, unsigned long *len, int *format)
5836 if (value == NULL || *len == 0) {
5837 return; /* nothing had been selected to copy */
5839 f = fopen(gamePasteFilename, "w");
5841 DisplayError(_("Can't open temp file"), errno);
5844 fwrite(value, 1, *len, f);
5847 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5850 /* called when Paste Game button is pressed,
5851 * all parameters will be NULL */
5852 void PasteGameProc(w, event, prms, nprms)
5858 XtGetSelectionValue(menuBarWidget,
5859 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5860 /* (XtSelectionCallbackProc) */ PasteGameCB,
5861 NULL, /* client_data passed to PasteGameCB */
5863 /* better to use the time field from the event that triggered the
5864 * call to this function, but that isn't trivial to get
5874 SaveGameProc(NULL, NULL, NULL, NULL);
5878 void QuitProc(w, event, prms, nprms)
5887 void PauseProc(w, event, prms, nprms)
5897 void MachineBlackProc(w, event, prms, nprms)
5903 MachineBlackEvent();
5906 void MachineWhiteProc(w, event, prms, nprms)
5912 MachineWhiteEvent();
5915 void AnalyzeModeProc(w, event, prms, nprms)
5923 if (!first.analysisSupport) {
5924 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5925 DisplayError(buf, 0);
5928 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5929 if (appData.icsActive) {
5930 if (gameMode != IcsObserving) {
5931 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5932 DisplayError(buf, 0);
5934 if (appData.icsEngineAnalyze) {
5935 if (appData.debugMode)
5936 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5942 /* if enable, use want disable icsEngineAnalyze */
5943 if (appData.icsEngineAnalyze) {
5948 appData.icsEngineAnalyze = TRUE;
5949 if (appData.debugMode)
5950 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5952 #ifndef OPTIONSDIALOG
5953 if (!appData.showThinking)
5954 ShowThinkingProc(w,event,prms,nprms);
5960 void AnalyzeFileProc(w, event, prms, nprms)
5966 if (!first.analysisSupport) {
5968 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5969 DisplayError(buf, 0);
5972 // Reset(FALSE, TRUE);
5973 #ifndef OPTIONSDIALOG
5974 if (!appData.showThinking)
5975 ShowThinkingProc(w,event,prms,nprms);
5978 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5979 AnalysisPeriodicEvent(1);
5982 void TwoMachinesProc(w, event, prms, nprms)
5991 void MatchProc(w, event, prms, nprms)
6000 void IcsClientProc(w, event, prms, nprms)
6009 void EditGameProc(w, event, prms, nprms)
6018 void EditPositionProc(w, event, prms, nprms)
6024 EditPositionEvent();
6027 void TrainingProc(w, event, prms, nprms)
6036 void EditCommentProc(w, event, prms, nprms)
6044 if (PopDown(1)) { // popdown succesful
6046 XtSetArg(args[j], XtNleftBitmap, None); j++;
6047 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6048 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6049 } else // was not up
6053 void IcsInputBoxProc(w, event, prms, nprms)
6059 if (!PopDown(4)) ICSInputBoxPopUp();
6062 void AcceptProc(w, event, prms, nprms)
6071 void DeclineProc(w, event, prms, nprms)
6080 void RematchProc(w, event, prms, nprms)
6089 void CallFlagProc(w, event, prms, nprms)
6098 void DrawProc(w, event, prms, nprms)
6107 void AbortProc(w, event, prms, nprms)
6116 void AdjournProc(w, event, prms, nprms)
6125 void ResignProc(w, event, prms, nprms)
6134 void AdjuWhiteProc(w, event, prms, nprms)
6140 UserAdjudicationEvent(+1);
6143 void AdjuBlackProc(w, event, prms, nprms)
6149 UserAdjudicationEvent(-1);
6152 void AdjuDrawProc(w, event, prms, nprms)
6158 UserAdjudicationEvent(0);
6161 void EnterKeyProc(w, event, prms, nprms)
6167 if (shellUp[4] == True)
6171 void UpKeyProc(w, event, prms, nprms)
6176 { // [HGM] input: let up-arrow recall previous line from history
6183 if (!shellUp[4]) return;
6184 edit = boxOptions[0].handle;
6186 XtSetArg(args[j], XtNstring, &val); j++;
6187 XtGetValues(edit, args, j);
6188 val = PrevInHistory(val);
6189 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6190 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6192 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6193 XawTextReplace(edit, 0, 0, &t);
6194 XawTextSetInsertionPoint(edit, 9999);
6198 void DownKeyProc(w, event, prms, nprms)
6203 { // [HGM] input: let down-arrow recall next line from history
6208 if (!shellUp[4]) return;
6209 edit = boxOptions[0].handle;
6210 val = NextInHistory();
6211 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6212 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6214 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6215 XawTextReplace(edit, 0, 0, &t);
6216 XawTextSetInsertionPoint(edit, 9999);
6220 void StopObservingProc(w, event, prms, nprms)
6226 StopObservingEvent();
6229 void StopExaminingProc(w, event, prms, nprms)
6235 StopExaminingEvent();
6238 void UploadProc(w, event, prms, nprms)
6248 void ForwardProc(w, event, prms, nprms)
6258 void BackwardProc(w, event, prms, nprms)
6267 void TempBackwardProc(w, event, prms, nprms)
6273 if (!TempBackwardActive) {
6274 TempBackwardActive = True;
6279 void TempForwardProc(w, event, prms, nprms)
6285 /* Check to see if triggered by a key release event for a repeating key.
6286 * If so the next queued event will be a key press of the same key at the same time */
6287 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6289 XPeekEvent(xDisplay, &next);
6290 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6291 next.xkey.keycode == event->xkey.keycode)
6295 TempBackwardActive = False;
6298 void ToStartProc(w, event, prms, nprms)
6307 void ToEndProc(w, event, prms, nprms)
6316 void RevertProc(w, event, prms, nprms)
6325 void AnnotateProc(w, event, prms, nprms)
6334 void TruncateGameProc(w, event, prms, nprms)
6340 TruncateGameEvent();
6342 void RetractMoveProc(w, event, prms, nprms)
6351 void MoveNowProc(w, event, prms, nprms)
6360 void FlipViewProc(w, event, prms, nprms)
6366 flipView = !flipView;
6367 DrawPosition(True, NULL);
6370 void PonderNextMoveProc(w, event, prms, nprms)
6378 PonderNextMoveEvent(!appData.ponderNextMove);
6379 #ifndef OPTIONSDIALOG
6380 if (appData.ponderNextMove) {
6381 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6383 XtSetArg(args[0], XtNleftBitmap, None);
6385 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6390 #ifndef OPTIONSDIALOG
6391 void AlwaysQueenProc(w, event, prms, nprms)
6399 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6401 if (appData.alwaysPromoteToQueen) {
6402 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6404 XtSetArg(args[0], XtNleftBitmap, None);
6406 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6410 void AnimateDraggingProc(w, event, prms, nprms)
6418 appData.animateDragging = !appData.animateDragging;
6420 if (appData.animateDragging) {
6421 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6424 XtSetArg(args[0], XtNleftBitmap, None);
6426 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6430 void AnimateMovingProc(w, event, prms, nprms)
6438 appData.animate = !appData.animate;
6440 if (appData.animate) {
6441 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6444 XtSetArg(args[0], XtNleftBitmap, None);
6446 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6450 void AutoflagProc(w, event, prms, nprms)
6458 appData.autoCallFlag = !appData.autoCallFlag;
6460 if (appData.autoCallFlag) {
6461 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6463 XtSetArg(args[0], XtNleftBitmap, None);
6465 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6469 void AutoflipProc(w, event, prms, nprms)
6477 appData.autoFlipView = !appData.autoFlipView;
6479 if (appData.autoFlipView) {
6480 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6482 XtSetArg(args[0], XtNleftBitmap, None);
6484 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6488 void BlindfoldProc(w, event, prms, nprms)
6496 appData.blindfold = !appData.blindfold;
6498 if (appData.blindfold) {
6499 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6501 XtSetArg(args[0], XtNleftBitmap, None);
6503 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6506 DrawPosition(True, NULL);
6509 void TestLegalityProc(w, event, prms, nprms)
6517 appData.testLegality = !appData.testLegality;
6519 if (appData.testLegality) {
6520 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6522 XtSetArg(args[0], XtNleftBitmap, None);
6524 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6529 void FlashMovesProc(w, event, prms, nprms)
6537 if (appData.flashCount == 0) {
6538 appData.flashCount = 3;
6540 appData.flashCount = -appData.flashCount;
6543 if (appData.flashCount > 0) {
6544 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6546 XtSetArg(args[0], XtNleftBitmap, None);
6548 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6553 void HighlightDraggingProc(w, event, prms, nprms)
6561 appData.highlightDragging = !appData.highlightDragging;
6563 if (appData.highlightDragging) {
6564 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6566 XtSetArg(args[0], XtNleftBitmap, None);
6568 XtSetValues(XtNameToWidget(menuBarWidget,
6569 "menuOptions.Highlight Dragging"), args, 1);
6573 void HighlightLastMoveProc(w, event, prms, nprms)
6581 appData.highlightLastMove = !appData.highlightLastMove;
6583 if (appData.highlightLastMove) {
6584 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6586 XtSetArg(args[0], XtNleftBitmap, None);
6588 XtSetValues(XtNameToWidget(menuBarWidget,
6589 "menuOptions.Highlight Last Move"), args, 1);
6592 void HighlightArrowProc(w, event, prms, nprms)
6600 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6602 if (appData.highlightMoveWithArrow) {
6603 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6605 XtSetArg(args[0], XtNleftBitmap, None);
6607 XtSetValues(XtNameToWidget(menuBarWidget,
6608 "menuOptions.Arrow"), args, 1);
6612 void IcsAlarmProc(w, event, prms, nprms)
6620 appData.icsAlarm = !appData.icsAlarm;
6622 if (appData.icsAlarm) {
6623 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6625 XtSetArg(args[0], XtNleftBitmap, None);
6627 XtSetValues(XtNameToWidget(menuBarWidget,
6628 "menuOptions.ICS Alarm"), args, 1);
6632 void MoveSoundProc(w, event, prms, nprms)
6640 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6642 if (appData.ringBellAfterMoves) {
6643 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6645 XtSetArg(args[0], XtNleftBitmap, None);
6647 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6651 void OneClickProc(w, event, prms, nprms)
6659 appData.oneClick = !appData.oneClick;
6661 if (appData.oneClick) {
6662 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6664 XtSetArg(args[0], XtNleftBitmap, None);
6666 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6670 void PeriodicUpdatesProc(w, event, prms, nprms)
6678 PeriodicUpdatesEvent(!appData.periodicUpdates);
6680 if (appData.periodicUpdates) {
6681 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6683 XtSetArg(args[0], XtNleftBitmap, None);
6685 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6689 void PopupExitMessageProc(w, event, prms, nprms)
6697 appData.popupExitMessage = !appData.popupExitMessage;
6699 if (appData.popupExitMessage) {
6700 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6702 XtSetArg(args[0], XtNleftBitmap, None);
6704 XtSetValues(XtNameToWidget(menuBarWidget,
6705 "menuOptions.Popup Exit Message"), args, 1);
6708 void PopupMoveErrorsProc(w, event, prms, nprms)
6716 appData.popupMoveErrors = !appData.popupMoveErrors;
6718 if (appData.popupMoveErrors) {
6719 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6721 XtSetArg(args[0], XtNleftBitmap, None);
6723 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6728 void PremoveProc(w, event, prms, nprms)
6736 appData.premove = !appData.premove;
6738 if (appData.premove) {
6739 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6741 XtSetArg(args[0], XtNleftBitmap, None);
6743 XtSetValues(XtNameToWidget(menuBarWidget,
6744 "menuOptions.Premove"), args, 1);
6748 void ShowCoordsProc(w, event, prms, nprms)
6756 appData.showCoords = !appData.showCoords;
6758 if (appData.showCoords) {
6759 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6761 XtSetArg(args[0], XtNleftBitmap, None);
6763 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6766 DrawPosition(True, NULL);
6769 void ShowThinkingProc(w, event, prms, nprms)
6775 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6776 ShowThinkingEvent();
6779 void HideThinkingProc(w, event, prms, nprms)
6787 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6788 ShowThinkingEvent();
6790 if (appData.hideThinkingFromHuman) {
6791 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6793 XtSetArg(args[0], XtNleftBitmap, None);
6795 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6800 void SaveOnExitProc(w, event, prms, nprms)
6808 saveSettingsOnExit = !saveSettingsOnExit;
6810 if (saveSettingsOnExit) {
6811 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6813 XtSetArg(args[0], XtNleftBitmap, None);
6815 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6819 void SaveSettingsProc(w, event, prms, nprms)
6825 SaveSettings(settingsFileName);
6828 void InfoProc(w, event, prms, nprms)
6835 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6840 void ManProc(w, event, prms, nprms)
6848 if (nprms && *nprms > 0)
6852 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6856 void HintProc(w, event, prms, nprms)
6865 void BookProc(w, event, prms, nprms)
6874 void AboutProc(w, event, prms, nprms)
6882 char *zippy = _(" (with Zippy code)");
6886 snprintf(buf, sizeof(buf),
6888 "Copyright 1991 Digital Equipment Corporation\n"
6889 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6890 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6891 "%s is free software and carries NO WARRANTY;"
6892 "see the file COPYING for more information."),
6893 programVersion, zippy, PACKAGE);
6894 ErrorPopUp(_("About XBoard"), buf, FALSE);
6897 void DebugProc(w, event, prms, nprms)
6903 appData.debugMode = !appData.debugMode;
6906 void AboutGameProc(w, event, prms, nprms)
6915 void NothingProc(w, event, prms, nprms)
6924 void DisplayMessage(message, extMessage)
6925 char *message, *extMessage;
6927 /* display a message in the message widget */
6936 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6941 message = extMessage;
6945 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6947 /* need to test if messageWidget already exists, since this function
6948 can also be called during the startup, if for example a Xresource
6949 is not set up correctly */
6952 XtSetArg(arg, XtNlabel, message);
6953 XtSetValues(messageWidget, &arg, 1);
6959 void DisplayTitle(text)
6964 char title[MSG_SIZ];
6967 if (text == NULL) text = "";
6969 if (appData.titleInWindow) {
6971 XtSetArg(args[i], XtNlabel, text); i++;
6972 XtSetValues(titleWidget, args, i);
6975 if (*text != NULLCHAR) {
6976 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6977 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6978 } else if (appData.icsActive) {
6979 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6980 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6981 } else if (appData.cmailGameName[0] != NULLCHAR) {
6982 snprintf(icon, sizeof(icon), "%s", "CMail");
6983 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6985 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6986 } else if (gameInfo.variant == VariantGothic) {
6987 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6988 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6991 } else if (gameInfo.variant == VariantFalcon) {
6992 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6993 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6995 } else if (appData.noChessProgram) {
6996 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6997 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6999 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
7000 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7003 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
7004 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
7005 XtSetValues(shellWidget, args, i);
7006 XSync(xDisplay, False);
7011 DisplayError(message, error)
7018 if (appData.debugMode || appData.matchMode) {
7019 fprintf(stderr, "%s: %s\n", programName, message);
7022 if (appData.debugMode || appData.matchMode) {
7023 fprintf(stderr, "%s: %s: %s\n",
7024 programName, message, strerror(error));
7026 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7029 ErrorPopUp(_("Error"), message, FALSE);
7033 void DisplayMoveError(message)
7038 DrawPosition(FALSE, NULL);
7039 if (appData.debugMode || appData.matchMode) {
7040 fprintf(stderr, "%s: %s\n", programName, message);
7042 if (appData.popupMoveErrors) {
7043 ErrorPopUp(_("Error"), message, FALSE);
7045 DisplayMessage(message, "");
7050 void DisplayFatalError(message, error, status)
7056 errorExitStatus = status;
7058 fprintf(stderr, "%s: %s\n", programName, message);
7060 fprintf(stderr, "%s: %s: %s\n",
7061 programName, message, strerror(error));
7062 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7065 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7066 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7072 void DisplayInformation(message)
7076 ErrorPopUp(_("Information"), message, TRUE);
7079 void DisplayNote(message)
7083 ErrorPopUp(_("Note"), message, FALSE);
7087 NullXErrorCheck(dpy, error_event)
7089 XErrorEvent *error_event;
7094 void DisplayIcsInteractionTitle(message)
7097 if (oldICSInteractionTitle == NULL) {
7098 /* Magic to find the old window title, adapted from vim */
7099 char *wina = getenv("WINDOWID");
7101 Window win = (Window) atoi(wina);
7102 Window root, parent, *children;
7103 unsigned int nchildren;
7104 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7106 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7107 if (!XQueryTree(xDisplay, win, &root, &parent,
7108 &children, &nchildren)) break;
7109 if (children) XFree((void *)children);
7110 if (parent == root || parent == 0) break;
7113 XSetErrorHandler(oldHandler);
7115 if (oldICSInteractionTitle == NULL) {
7116 oldICSInteractionTitle = "xterm";
7119 printf("\033]0;%s\007", message);
7123 char pendingReplyPrefix[MSG_SIZ];
7124 ProcRef pendingReplyPR;
7126 void AskQuestionProc(w, event, prms, nprms)
7133 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7137 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7140 void AskQuestionPopDown()
7142 if (!askQuestionUp) return;
7143 XtPopdown(askQuestionShell);
7144 XtDestroyWidget(askQuestionShell);
7145 askQuestionUp = False;
7148 void AskQuestionReplyAction(w, event, prms, nprms)
7158 reply = XawDialogGetValueString(w = XtParent(w));
7159 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7160 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7161 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7162 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7163 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7164 AskQuestionPopDown();
7166 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7169 void AskQuestionCallback(w, client_data, call_data)
7171 XtPointer client_data, call_data;
7176 XtSetArg(args[0], XtNlabel, &name);
7177 XtGetValues(w, args, 1);
7179 if (strcmp(name, _("cancel")) == 0) {
7180 AskQuestionPopDown();
7182 AskQuestionReplyAction(w, NULL, NULL, NULL);
7186 void AskQuestion(title, question, replyPrefix, pr)
7187 char *title, *question, *replyPrefix;
7191 Widget popup, layout, dialog, edit;
7197 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7198 pendingReplyPR = pr;
7201 XtSetArg(args[i], XtNresizable, True); i++;
7202 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7203 askQuestionShell = popup =
7204 XtCreatePopupShell(title, transientShellWidgetClass,
7205 shellWidget, args, i);
7208 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7209 layoutArgs, XtNumber(layoutArgs));
7212 XtSetArg(args[i], XtNlabel, question); i++;
7213 XtSetArg(args[i], XtNvalue, ""); i++;
7214 XtSetArg(args[i], XtNborderWidth, 0); i++;
7215 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7218 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7219 (XtPointer) dialog);
7220 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7221 (XtPointer) dialog);
7223 XtRealizeWidget(popup);
7224 CatchDeleteWindow(popup, "AskQuestionPopDown");
7226 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7227 &x, &y, &win_x, &win_y, &mask);
7229 XtSetArg(args[0], XtNx, x - 10);
7230 XtSetArg(args[1], XtNy, y - 30);
7231 XtSetValues(popup, args, 2);
7233 XtPopup(popup, XtGrabExclusive);
7234 askQuestionUp = True;
7236 edit = XtNameToWidget(dialog, "*value");
7237 XtSetKeyboardFocus(popup, edit);
7245 if (*name == NULLCHAR) {
7247 } else if (strcmp(name, "$") == 0) {
7248 putc(BELLCHAR, stderr);
7251 char *prefix = "", *sep = "";
7252 if(appData.soundProgram[0] == NULLCHAR) return;
7253 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7254 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7262 PlaySound(appData.soundMove);
7268 PlaySound(appData.soundIcsWin);
7274 PlaySound(appData.soundIcsLoss);
7280 PlaySound(appData.soundIcsDraw);
7284 PlayIcsUnfinishedSound()
7286 PlaySound(appData.soundIcsUnfinished);
7292 PlaySound(appData.soundIcsAlarm);
7298 PlaySound(appData.soundTell);
7304 system("stty echo");
7311 system("stty -echo");
7316 RunCommand(char *buf)
7322 Colorize(cc, continuation)
7327 int count, outCount, error;
7329 if (textColors[(int)cc].bg > 0) {
7330 if (textColors[(int)cc].fg > 0) {
7331 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7332 textColors[(int)cc].fg, textColors[(int)cc].bg);
7334 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7335 textColors[(int)cc].bg);
7338 if (textColors[(int)cc].fg > 0) {
7339 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7340 textColors[(int)cc].fg);
7342 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7345 count = strlen(buf);
7346 outCount = OutputToProcess(NoProc, buf, count, &error);
7347 if (outCount < count) {
7348 DisplayFatalError(_("Error writing to display"), error, 1);
7351 if (continuation) return;
7354 PlaySound(appData.soundShout);
7357 PlaySound(appData.soundSShout);
7360 PlaySound(appData.soundChannel1);
7363 PlaySound(appData.soundChannel);
7366 PlaySound(appData.soundKibitz);
7369 PlaySound(appData.soundTell);
7371 case ColorChallenge:
7372 PlaySound(appData.soundChallenge);
7375 PlaySound(appData.soundRequest);
7378 PlaySound(appData.soundSeek);
7389 return getpwuid(getuid())->pw_name;
7393 ExpandPathName(path)
7396 static char static_buf[4*MSG_SIZ];
7397 char *d, *s, buf[4*MSG_SIZ];
7403 while (*s && isspace(*s))
7412 if (*(s+1) == '/') {
7413 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7417 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7418 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7419 pwd = getpwnam(buf);
7422 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7426 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7427 strcat(d, strchr(s+1, '/'));
7431 safeStrCpy(d, s, 4*MSG_SIZ );
7438 static char host_name[MSG_SIZ];
7440 #if HAVE_GETHOSTNAME
7441 gethostname(host_name, MSG_SIZ);
7443 #else /* not HAVE_GETHOSTNAME */
7444 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7445 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7447 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7449 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7450 #endif /* not HAVE_GETHOSTNAME */
7453 XtIntervalId delayedEventTimerXID = 0;
7454 DelayedEventCallback delayedEventCallback = 0;
7459 delayedEventTimerXID = 0;
7460 delayedEventCallback();
7464 ScheduleDelayedEvent(cb, millisec)
7465 DelayedEventCallback cb; long millisec;
7467 if(delayedEventTimerXID && delayedEventCallback == cb)
7468 // [HGM] alive: replace, rather than add or flush identical event
7469 XtRemoveTimeOut(delayedEventTimerXID);
7470 delayedEventCallback = cb;
7471 delayedEventTimerXID =
7472 XtAppAddTimeOut(appContext, millisec,
7473 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7476 DelayedEventCallback
7479 if (delayedEventTimerXID) {
7480 return delayedEventCallback;
7487 CancelDelayedEvent()
7489 if (delayedEventTimerXID) {
7490 XtRemoveTimeOut(delayedEventTimerXID);
7491 delayedEventTimerXID = 0;
7495 XtIntervalId loadGameTimerXID = 0;
7497 int LoadGameTimerRunning()
7499 return loadGameTimerXID != 0;
7502 int StopLoadGameTimer()
7504 if (loadGameTimerXID != 0) {
7505 XtRemoveTimeOut(loadGameTimerXID);
7506 loadGameTimerXID = 0;
7514 LoadGameTimerCallback(arg, id)
7518 loadGameTimerXID = 0;
7523 StartLoadGameTimer(millisec)
7527 XtAppAddTimeOut(appContext, millisec,
7528 (XtTimerCallbackProc) LoadGameTimerCallback,
7532 XtIntervalId analysisClockXID = 0;
7535 AnalysisClockCallback(arg, id)
7539 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7540 || appData.icsEngineAnalyze) { // [DM]
7541 AnalysisPeriodicEvent(0);
7542 StartAnalysisClock();
7547 StartAnalysisClock()
7550 XtAppAddTimeOut(appContext, 2000,
7551 (XtTimerCallbackProc) AnalysisClockCallback,
7555 XtIntervalId clockTimerXID = 0;
7557 int ClockTimerRunning()
7559 return clockTimerXID != 0;
7562 int StopClockTimer()
7564 if (clockTimerXID != 0) {
7565 XtRemoveTimeOut(clockTimerXID);
7574 ClockTimerCallback(arg, id)
7583 StartClockTimer(millisec)
7587 XtAppAddTimeOut(appContext, millisec,
7588 (XtTimerCallbackProc) ClockTimerCallback,
7593 DisplayTimerLabel(w, color, timer, highlight)
7602 /* check for low time warning */
7603 Pixel foregroundOrWarningColor = timerForegroundPixel;
7606 appData.lowTimeWarning &&
7607 (timer / 1000) < appData.icsAlarmTime)
7608 foregroundOrWarningColor = lowTimeWarningColor;
7610 if (appData.clockMode) {
7611 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7612 XtSetArg(args[0], XtNlabel, buf);
7614 snprintf(buf, MSG_SIZ, "%s ", color);
7615 XtSetArg(args[0], XtNlabel, buf);
7620 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7621 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7623 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7624 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7627 XtSetValues(w, args, 3);
7631 DisplayWhiteClock(timeRemaining, highlight)
7637 if(appData.noGUI) return;
7638 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7639 if (highlight && iconPixmap == bIconPixmap) {
7640 iconPixmap = wIconPixmap;
7641 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7642 XtSetValues(shellWidget, args, 1);
7647 DisplayBlackClock(timeRemaining, highlight)
7653 if(appData.noGUI) return;
7654 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7655 if (highlight && iconPixmap == wIconPixmap) {
7656 iconPixmap = bIconPixmap;
7657 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7658 XtSetValues(shellWidget, args, 1);
7676 int StartChildProcess(cmdLine, dir, pr)
7683 int to_prog[2], from_prog[2];
7687 if (appData.debugMode) {
7688 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7691 /* We do NOT feed the cmdLine to the shell; we just
7692 parse it into blank-separated arguments in the
7693 most simple-minded way possible.
7696 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7699 while(*p == ' ') p++;
7701 if(*p == '"' || *p == '\'')
7702 p = strchr(++argv[i-1], *p);
7703 else p = strchr(p, ' ');
7704 if (p == NULL) break;
7709 SetUpChildIO(to_prog, from_prog);
7711 if ((pid = fork()) == 0) {
7713 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7714 close(to_prog[1]); // first close the unused pipe ends
7715 close(from_prog[0]);
7716 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7717 dup2(from_prog[1], 1);
7718 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7719 close(from_prog[1]); // and closing again loses one of the pipes!
7720 if(fileno(stderr) >= 2) // better safe than sorry...
7721 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7723 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7728 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7730 execvp(argv[0], argv);
7732 /* If we get here, exec failed */
7737 /* Parent process */
7739 close(from_prog[1]);
7741 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7744 cp->fdFrom = from_prog[0];
7745 cp->fdTo = to_prog[1];
7750 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7751 static RETSIGTYPE AlarmCallBack(int n)
7757 DestroyChildProcess(pr, signalType)
7761 ChildProc *cp = (ChildProc *) pr;
7763 if (cp->kind != CPReal) return;
7765 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7766 signal(SIGALRM, AlarmCallBack);
7768 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7769 kill(cp->pid, SIGKILL); // kill it forcefully
7770 wait((int *) 0); // and wait again
7774 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7776 /* Process is exiting either because of the kill or because of
7777 a quit command sent by the backend; either way, wait for it to die.
7786 InterruptChildProcess(pr)
7789 ChildProc *cp = (ChildProc *) pr;
7791 if (cp->kind != CPReal) return;
7792 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7795 int OpenTelnet(host, port, pr)
7800 char cmdLine[MSG_SIZ];
7802 if (port[0] == NULLCHAR) {
7803 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7805 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7807 return StartChildProcess(cmdLine, "", pr);
7810 int OpenTCP(host, port, pr)
7816 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7817 #else /* !OMIT_SOCKETS */
7818 struct addrinfo hints;
7819 struct addrinfo *ais, *ai;
7824 memset(&hints, 0, sizeof(hints));
7825 hints.ai_family = AF_UNSPEC;
7826 hints.ai_socktype = SOCK_STREAM;
7828 error = getaddrinfo(host, port, &hints, &ais);
7830 /* a getaddrinfo error is not an errno, so can't return it */
7831 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7832 host, port, gai_strerror(error));
7836 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7837 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7841 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7854 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7860 #endif /* !OMIT_SOCKETS */
7865 int OpenCommPort(name, pr)
7872 fd = open(name, 2, 0);
7873 if (fd < 0) return errno;
7875 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7885 int OpenLoopback(pr)
7891 SetUpChildIO(to, from);
7893 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7896 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7903 int OpenRcmd(host, user, cmd, pr)
7904 char *host, *user, *cmd;
7907 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7911 #define INPUT_SOURCE_BUF_SIZE 8192
7920 char buf[INPUT_SOURCE_BUF_SIZE];
7925 DoInputCallback(closure, source, xid)
7930 InputSource *is = (InputSource *) closure;
7935 if (is->lineByLine) {
7936 count = read(is->fd, is->unused,
7937 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7939 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7942 is->unused += count;
7944 while (p < is->unused) {
7945 q = memchr(p, '\n', is->unused - p);
7946 if (q == NULL) break;
7948 (is->func)(is, is->closure, p, q - p, 0);
7952 while (p < is->unused) {
7957 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7962 (is->func)(is, is->closure, is->buf, count, error);
7966 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7973 ChildProc *cp = (ChildProc *) pr;
7975 is = (InputSource *) calloc(1, sizeof(InputSource));
7976 is->lineByLine = lineByLine;
7980 is->fd = fileno(stdin);
7982 is->kind = cp->kind;
7983 is->fd = cp->fdFrom;
7986 is->unused = is->buf;
7989 is->xid = XtAppAddInput(appContext, is->fd,
7990 (XtPointer) (XtInputReadMask),
7991 (XtInputCallbackProc) DoInputCallback,
7993 is->closure = closure;
7994 return (InputSourceRef) is;
7998 RemoveInputSource(isr)
8001 InputSource *is = (InputSource *) isr;
8003 if (is->xid == 0) return;
8004 XtRemoveInput(is->xid);
8008 int OutputToProcess(pr, message, count, outError)
8014 static int line = 0;
8015 ChildProc *cp = (ChildProc *) pr;
8020 if (appData.noJoin || !appData.useInternalWrap)
8021 outCount = fwrite(message, 1, count, stdout);
8024 int width = get_term_width();
8025 int len = wrap(NULL, message, count, width, &line);
8026 char *msg = malloc(len);
8030 outCount = fwrite(message, 1, count, stdout);
8033 dbgchk = wrap(msg, message, count, width, &line);
8034 if (dbgchk != len && appData.debugMode)
8035 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8036 outCount = fwrite(msg, 1, dbgchk, stdout);
8042 outCount = write(cp->fdTo, message, count);
8052 /* Output message to process, with "ms" milliseconds of delay
8053 between each character. This is needed when sending the logon
8054 script to ICC, which for some reason doesn't like the
8055 instantaneous send. */
8056 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8063 ChildProc *cp = (ChildProc *) pr;
8068 r = write(cp->fdTo, message++, 1);
8081 /**** Animation code by Hugh Fisher, DCS, ANU.
8083 Known problem: if a window overlapping the board is
8084 moved away while a piece is being animated underneath,
8085 the newly exposed area won't be updated properly.
8086 I can live with this.
8088 Known problem: if you look carefully at the animation
8089 of pieces in mono mode, they are being drawn as solid
8090 shapes without interior detail while moving. Fixing
8091 this would be a major complication for minimal return.
8094 /* Masks for XPM pieces. Black and white pieces can have
8095 different shapes, but in the interest of retaining my
8096 sanity pieces must have the same outline on both light
8097 and dark squares, and all pieces must use the same
8098 background square colors/images. */
8100 static int xpmDone = 0;
8103 CreateAnimMasks (pieceDepth)
8110 unsigned long plane;
8113 /* Need a bitmap just to get a GC with right depth */
8114 buf = XCreatePixmap(xDisplay, xBoardWindow,
8116 values.foreground = 1;
8117 values.background = 0;
8118 /* Don't use XtGetGC, not read only */
8119 maskGC = XCreateGC(xDisplay, buf,
8120 GCForeground | GCBackground, &values);
8121 XFreePixmap(xDisplay, buf);
8123 buf = XCreatePixmap(xDisplay, xBoardWindow,
8124 squareSize, squareSize, pieceDepth);
8125 values.foreground = XBlackPixel(xDisplay, xScreen);
8126 values.background = XWhitePixel(xDisplay, xScreen);
8127 bufGC = XCreateGC(xDisplay, buf,
8128 GCForeground | GCBackground, &values);
8130 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8131 /* Begin with empty mask */
8132 if(!xpmDone) // [HGM] pieces: keep using existing
8133 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8134 squareSize, squareSize, 1);
8135 XSetFunction(xDisplay, maskGC, GXclear);
8136 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8137 0, 0, squareSize, squareSize);
8139 /* Take a copy of the piece */
8144 XSetFunction(xDisplay, bufGC, GXcopy);
8145 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8147 0, 0, squareSize, squareSize, 0, 0);
8149 /* XOR the background (light) over the piece */
8150 XSetFunction(xDisplay, bufGC, GXxor);
8152 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8153 0, 0, squareSize, squareSize, 0, 0);
8155 XSetForeground(xDisplay, bufGC, lightSquareColor);
8156 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8159 /* We now have an inverted piece image with the background
8160 erased. Construct mask by just selecting all the non-zero
8161 pixels - no need to reconstruct the original image. */
8162 XSetFunction(xDisplay, maskGC, GXor);
8164 /* Might be quicker to download an XImage and create bitmap
8165 data from it rather than this N copies per piece, but it
8166 only takes a fraction of a second and there is a much
8167 longer delay for loading the pieces. */
8168 for (n = 0; n < pieceDepth; n ++) {
8169 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8170 0, 0, squareSize, squareSize,
8176 XFreePixmap(xDisplay, buf);
8177 XFreeGC(xDisplay, bufGC);
8178 XFreeGC(xDisplay, maskGC);
8182 InitAnimState (anim, info)
8184 XWindowAttributes * info;
8189 /* Each buffer is square size, same depth as window */
8190 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8191 squareSize, squareSize, info->depth);
8192 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8193 squareSize, squareSize, info->depth);
8195 /* Create a plain GC for blitting */
8196 mask = GCForeground | GCBackground | GCFunction |
8197 GCPlaneMask | GCGraphicsExposures;
8198 values.foreground = XBlackPixel(xDisplay, xScreen);
8199 values.background = XWhitePixel(xDisplay, xScreen);
8200 values.function = GXcopy;
8201 values.plane_mask = AllPlanes;
8202 values.graphics_exposures = False;
8203 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8205 /* Piece will be copied from an existing context at
8206 the start of each new animation/drag. */
8207 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8209 /* Outline will be a read-only copy of an existing */
8210 anim->outlineGC = None;
8216 XWindowAttributes info;
8218 if (xpmDone && gameInfo.variant == oldVariant) return;
8219 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8220 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8222 InitAnimState(&game, &info);
8223 InitAnimState(&player, &info);
8225 /* For XPM pieces, we need bitmaps to use as masks. */
8227 CreateAnimMasks(info.depth), xpmDone = 1;
8232 static Boolean frameWaiting;
8234 static RETSIGTYPE FrameAlarm (sig)
8237 frameWaiting = False;
8238 /* In case System-V style signals. Needed?? */
8239 signal(SIGALRM, FrameAlarm);
8246 struct itimerval delay;
8248 XSync(xDisplay, False);
8251 frameWaiting = True;
8252 signal(SIGALRM, FrameAlarm);
8253 delay.it_interval.tv_sec =
8254 delay.it_value.tv_sec = time / 1000;
8255 delay.it_interval.tv_usec =
8256 delay.it_value.tv_usec = (time % 1000) * 1000;
8257 setitimer(ITIMER_REAL, &delay, NULL);
8258 while (frameWaiting) pause();
8259 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8260 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8261 setitimer(ITIMER_REAL, &delay, NULL);
8271 XSync(xDisplay, False);
8273 usleep(time * 1000);
8284 /* Convert board position to corner of screen rect and color */
8287 ScreenSquare(column, row, pt, color)
8288 int column; int row; XPoint * pt; int * color;
8291 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8292 pt->y = lineGap + row * (squareSize + lineGap);
8294 pt->x = lineGap + column * (squareSize + lineGap);
8295 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8297 *color = SquareColor(row, column);
8300 /* Convert window coords to square */
8303 BoardSquare(x, y, column, row)
8304 int x; int y; int * column; int * row;
8306 *column = EventToSquare(x, BOARD_WIDTH);
8307 if (flipView && *column >= 0)
8308 *column = BOARD_WIDTH - 1 - *column;
8309 *row = EventToSquare(y, BOARD_HEIGHT);
8310 if (!flipView && *row >= 0)
8311 *row = BOARD_HEIGHT - 1 - *row;
8316 #undef Max /* just in case */
8318 #define Max(a, b) ((a) > (b) ? (a) : (b))
8319 #define Min(a, b) ((a) < (b) ? (a) : (b))
8322 SetRect(rect, x, y, width, height)
8323 XRectangle * rect; int x; int y; int width; int height;
8327 rect->width = width;
8328 rect->height = height;
8331 /* Test if two frames overlap. If they do, return
8332 intersection rect within old and location of
8333 that rect within new. */
8336 Intersect(old, new, size, area, pt)
8337 XPoint * old; XPoint * new;
8338 int size; XRectangle * area; XPoint * pt;
8340 if (old->x > new->x + size || new->x > old->x + size ||
8341 old->y > new->y + size || new->y > old->y + size) {
8344 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8345 size - abs(old->x - new->x), size - abs(old->y - new->y));
8346 pt->x = Max(old->x - new->x, 0);
8347 pt->y = Max(old->y - new->y, 0);
8352 /* For two overlapping frames, return the rect(s)
8353 in the old that do not intersect with the new. */
8356 CalcUpdateRects(old, new, size, update, nUpdates)
8357 XPoint * old; XPoint * new; int size;
8358 XRectangle update[]; int * nUpdates;
8362 /* If old = new (shouldn't happen) then nothing to draw */
8363 if (old->x == new->x && old->y == new->y) {
8367 /* Work out what bits overlap. Since we know the rects
8368 are the same size we don't need a full intersect calc. */
8370 /* Top or bottom edge? */
8371 if (new->y > old->y) {
8372 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8374 } else if (old->y > new->y) {
8375 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8376 size, old->y - new->y);
8379 /* Left or right edge - don't overlap any update calculated above. */
8380 if (new->x > old->x) {
8381 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8382 new->x - old->x, size - abs(new->y - old->y));
8384 } else if (old->x > new->x) {
8385 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8386 old->x - new->x, size - abs(new->y - old->y));
8393 /* Generate a series of frame coords from start->mid->finish.
8394 The movement rate doubles until the half way point is
8395 reached, then halves back down to the final destination,
8396 which gives a nice slow in/out effect. The algorithmn
8397 may seem to generate too many intermediates for short
8398 moves, but remember that the purpose is to attract the
8399 viewers attention to the piece about to be moved and
8400 then to where it ends up. Too few frames would be less
8404 Tween(start, mid, finish, factor, frames, nFrames)
8405 XPoint * start; XPoint * mid;
8406 XPoint * finish; int factor;
8407 XPoint frames[]; int * nFrames;
8409 int fraction, n, count;
8413 /* Slow in, stepping 1/16th, then 1/8th, ... */
8415 for (n = 0; n < factor; n++)
8417 for (n = 0; n < factor; n++) {
8418 frames[count].x = start->x + (mid->x - start->x) / fraction;
8419 frames[count].y = start->y + (mid->y - start->y) / fraction;
8421 fraction = fraction / 2;
8425 frames[count] = *mid;
8428 /* Slow out, stepping 1/2, then 1/4, ... */
8430 for (n = 0; n < factor; n++) {
8431 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8432 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8434 fraction = fraction * 2;
8439 /* Draw a piece on the screen without disturbing what's there */
8442 SelectGCMask(piece, clip, outline, mask)
8443 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8447 /* Bitmap for piece being moved. */
8448 if (appData.monoMode) {
8449 *mask = *pieceToSolid(piece);
8450 } else if (useImages) {
8452 *mask = xpmMask[piece];
8454 *mask = ximMaskPm[piece];
8457 *mask = *pieceToSolid(piece);
8460 /* GC for piece being moved. Square color doesn't matter, but
8461 since it gets modified we make a copy of the original. */
8463 if (appData.monoMode)
8468 if (appData.monoMode)
8473 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8475 /* Outline only used in mono mode and is not modified */
8477 *outline = bwPieceGC;
8479 *outline = wbPieceGC;
8483 OverlayPiece(piece, clip, outline, dest)
8484 ChessSquare piece; GC clip; GC outline; Drawable dest;
8489 /* Draw solid rectangle which will be clipped to shape of piece */
8490 XFillRectangle(xDisplay, dest, clip,
8491 0, 0, squareSize, squareSize);
8492 if (appData.monoMode)
8493 /* Also draw outline in contrasting color for black
8494 on black / white on white cases */
8495 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8496 0, 0, squareSize, squareSize, 0, 0, 1);
8498 /* Copy the piece */
8503 if(appData.upsideDown && flipView) kind ^= 2;
8504 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8506 0, 0, squareSize, squareSize,
8511 /* Animate the movement of a single piece */
8514 BeginAnimation(anim, piece, startColor, start)
8522 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8523 /* The old buffer is initialised with the start square (empty) */
8524 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8525 anim->prevFrame = *start;
8527 /* The piece will be drawn using its own bitmap as a matte */
8528 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8529 XSetClipMask(xDisplay, anim->pieceGC, mask);
8533 AnimationFrame(anim, frame, piece)
8538 XRectangle updates[4];
8543 /* Save what we are about to draw into the new buffer */
8544 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8545 frame->x, frame->y, squareSize, squareSize,
8548 /* Erase bits of the previous frame */
8549 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8550 /* Where the new frame overlapped the previous,
8551 the contents in newBuf are wrong. */
8552 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8553 overlap.x, overlap.y,
8554 overlap.width, overlap.height,
8556 /* Repaint the areas in the old that don't overlap new */
8557 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8558 for (i = 0; i < count; i++)
8559 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8560 updates[i].x - anim->prevFrame.x,
8561 updates[i].y - anim->prevFrame.y,
8562 updates[i].width, updates[i].height,
8563 updates[i].x, updates[i].y);
8565 /* Easy when no overlap */
8566 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8567 0, 0, squareSize, squareSize,
8568 anim->prevFrame.x, anim->prevFrame.y);
8571 /* Save this frame for next time round */
8572 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8573 0, 0, squareSize, squareSize,
8575 anim->prevFrame = *frame;
8577 /* Draw piece over original screen contents, not current,
8578 and copy entire rect. Wipes out overlapping piece images. */
8579 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8580 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8581 0, 0, squareSize, squareSize,
8582 frame->x, frame->y);
8586 EndAnimation (anim, finish)
8590 XRectangle updates[4];
8595 /* The main code will redraw the final square, so we
8596 only need to erase the bits that don't overlap. */
8597 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8598 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8599 for (i = 0; i < count; i++)
8600 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8601 updates[i].x - anim->prevFrame.x,
8602 updates[i].y - anim->prevFrame.y,
8603 updates[i].width, updates[i].height,
8604 updates[i].x, updates[i].y);
8606 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8607 0, 0, squareSize, squareSize,
8608 anim->prevFrame.x, anim->prevFrame.y);
8613 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8615 ChessSquare piece; int startColor;
8616 XPoint * start; XPoint * finish;
8617 XPoint frames[]; int nFrames;
8621 BeginAnimation(anim, piece, startColor, start);
8622 for (n = 0; n < nFrames; n++) {
8623 AnimationFrame(anim, &(frames[n]), piece);
8624 FrameDelay(appData.animSpeed);
8626 EndAnimation(anim, finish);
8630 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8633 ChessSquare piece = board[fromY][toY];
8634 board[fromY][toY] = EmptySquare;
8635 DrawPosition(FALSE, board);
8637 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8638 y = lineGap + toY * (squareSize + lineGap);
8640 x = lineGap + toX * (squareSize + lineGap);
8641 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8643 for(i=1; i<4*kFactor; i++) {
8644 int r = squareSize * 9 * i/(20*kFactor - 5);
8645 XFillArc(xDisplay, xBoardWindow, highlineGC,
8646 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8647 FrameDelay(appData.animSpeed);
8649 board[fromY][toY] = piece;
8652 /* Main control logic for deciding what to animate and how */
8655 AnimateMove(board, fromX, fromY, toX, toY)
8664 XPoint start, finish, mid;
8665 XPoint frames[kFactor * 2 + 1];
8666 int nFrames, startColor, endColor;
8668 /* Are we animating? */
8669 if (!appData.animate || appData.blindfold)
8672 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8673 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8674 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8676 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8677 piece = board[fromY][fromX];
8678 if (piece >= EmptySquare) return;
8683 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8686 if (appData.debugMode) {
8687 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8688 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8689 piece, fromX, fromY, toX, toY); }
8691 ScreenSquare(fromX, fromY, &start, &startColor);
8692 ScreenSquare(toX, toY, &finish, &endColor);
8695 /* Knight: make straight movement then diagonal */
8696 if (abs(toY - fromY) < abs(toX - fromX)) {
8697 mid.x = start.x + (finish.x - start.x) / 2;
8701 mid.y = start.y + (finish.y - start.y) / 2;
8704 mid.x = start.x + (finish.x - start.x) / 2;
8705 mid.y = start.y + (finish.y - start.y) / 2;
8708 /* Don't use as many frames for very short moves */
8709 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8710 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8712 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8713 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8714 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8716 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8717 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8720 /* Be sure end square is redrawn */
8721 damage[0][toY][toX] = True;
8725 DragPieceBegin(x, y, instantly)
8726 int x; int y; Boolean instantly;
8728 int boardX, boardY, color;
8731 /* Are we animating? */
8732 if (!appData.animateDragging || appData.blindfold)
8735 /* Figure out which square we start in and the
8736 mouse position relative to top left corner. */
8737 BoardSquare(x, y, &boardX, &boardY);
8738 player.startBoardX = boardX;
8739 player.startBoardY = boardY;
8740 ScreenSquare(boardX, boardY, &corner, &color);
8741 player.startSquare = corner;
8742 player.startColor = color;
8743 /* As soon as we start dragging, the piece will jump slightly to
8744 be centered over the mouse pointer. */
8745 player.mouseDelta.x = squareSize/2;
8746 player.mouseDelta.y = squareSize/2;
8747 /* Initialise animation */
8748 player.dragPiece = PieceForSquare(boardX, boardY);
8750 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8751 player.dragActive = True;
8752 BeginAnimation(&player, player.dragPiece, color, &corner);
8753 /* Mark this square as needing to be redrawn. Note that
8754 we don't remove the piece though, since logically (ie
8755 as seen by opponent) the move hasn't been made yet. */
8756 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8757 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8758 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8759 corner.x, corner.y, squareSize, squareSize,
8760 0, 0); // [HGM] zh: unstack in stead of grab
8761 if(gatingPiece != EmptySquare) {
8762 /* Kludge alert: When gating we want the introduced
8763 piece to appear on the from square. To generate an
8764 image of it, we draw it on the board, copy the image,
8765 and draw the original piece again. */
8766 ChessSquare piece = boards[currentMove][boardY][boardX];
8767 DrawSquare(boardY, boardX, gatingPiece, 0);
8768 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8769 corner.x, corner.y, squareSize, squareSize, 0, 0);
8770 DrawSquare(boardY, boardX, piece, 0);
8772 damage[0][boardY][boardX] = True;
8774 player.dragActive = False;
8779 ChangeDragPiece(ChessSquare piece)
8782 player.dragPiece = piece;
8783 /* The piece will be drawn using its own bitmap as a matte */
8784 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8785 XSetClipMask(xDisplay, player.pieceGC, mask);
8794 /* Are we animating? */
8795 if (!appData.animateDragging || appData.blindfold)
8799 if (! player.dragActive)
8801 /* Move piece, maintaining same relative position
8802 of mouse within square */
8803 corner.x = x - player.mouseDelta.x;
8804 corner.y = y - player.mouseDelta.y;
8805 AnimationFrame(&player, &corner, player.dragPiece);
8807 if (appData.highlightDragging) {
8809 BoardSquare(x, y, &boardX, &boardY);
8810 SetHighlights(fromX, fromY, boardX, boardY);
8819 int boardX, boardY, color;
8822 /* Are we animating? */
8823 if (!appData.animateDragging || appData.blindfold)
8827 if (! player.dragActive)
8829 /* Last frame in sequence is square piece is
8830 placed on, which may not match mouse exactly. */
8831 BoardSquare(x, y, &boardX, &boardY);
8832 ScreenSquare(boardX, boardY, &corner, &color);
8833 EndAnimation(&player, &corner);
8835 /* Be sure end square is redrawn */
8836 damage[0][boardY][boardX] = True;
8838 /* This prevents weird things happening with fast successive
8839 clicks which on my Sun at least can cause motion events
8840 without corresponding press/release. */
8841 player.dragActive = False;
8844 /* Handle expose event while piece being dragged */
8849 if (!player.dragActive || appData.blindfold)
8852 /* What we're doing: logically, the move hasn't been made yet,
8853 so the piece is still in it's original square. But visually
8854 it's being dragged around the board. So we erase the square
8855 that the piece is on and draw it at the last known drag point. */
8856 BlankSquare(player.startSquare.x, player.startSquare.y,
8857 player.startColor, EmptySquare, xBoardWindow, 1);
8858 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8859 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8862 #include <sys/ioctl.h>
8863 int get_term_width()
8865 int fd, default_width;
8868 default_width = 79; // this is FICS default anyway...
8870 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8872 if (!ioctl(fd, TIOCGSIZE, &win))
8873 default_width = win.ts_cols;
8874 #elif defined(TIOCGWINSZ)
8876 if (!ioctl(fd, TIOCGWINSZ, &win))
8877 default_width = win.ws_col;
8879 return default_width;
8885 static int old_width = 0;
8886 int new_width = get_term_width();
8888 if (old_width != new_width)
8889 ics_printf("set width %d\n", new_width);
8890 old_width = new_width;
8893 void NotifyFrontendLogin()
8898 /* [AS] Arrow highlighting support */
8900 static double A_WIDTH = 5; /* Width of arrow body */
8902 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8903 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8905 static double Sqr( double x )
8910 static int Round( double x )
8912 return (int) (x + 0.5);
8915 void SquareToPos(int rank, int file, int *x, int *y)
8918 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8919 *y = lineGap + rank * (squareSize + lineGap);
8921 *x = lineGap + file * (squareSize + lineGap);
8922 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8926 /* Draw an arrow between two points using current settings */
8927 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8930 double dx, dy, j, k, x, y;
8933 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8935 arrow[0].x = s_x + A_WIDTH + 0.5;
8938 arrow[1].x = s_x + A_WIDTH + 0.5;
8939 arrow[1].y = d_y - h;
8941 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8942 arrow[2].y = d_y - h;
8947 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8948 arrow[5].y = d_y - h;
8950 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8951 arrow[4].y = d_y - h;
8953 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8956 else if( d_y == s_y ) {
8957 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8960 arrow[0].y = s_y + A_WIDTH + 0.5;
8962 arrow[1].x = d_x - w;
8963 arrow[1].y = s_y + A_WIDTH + 0.5;
8965 arrow[2].x = d_x - w;
8966 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8971 arrow[5].x = d_x - w;
8972 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8974 arrow[4].x = d_x - w;
8975 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8978 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8981 /* [AS] Needed a lot of paper for this! :-) */
8982 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8983 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8985 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8987 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8992 arrow[0].x = Round(x - j);
8993 arrow[0].y = Round(y + j*dx);
8995 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8996 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8999 x = (double) d_x - k;
9000 y = (double) d_y - k*dy;
9003 x = (double) d_x + k;
9004 y = (double) d_y + k*dy;
9007 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
9009 arrow[6].x = Round(x - j);
9010 arrow[6].y = Round(y + j*dx);
9012 arrow[2].x = Round(arrow[6].x + 2*j);
9013 arrow[2].y = Round(arrow[6].y - 2*j*dx);
9015 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
9016 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
9021 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9022 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9025 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9026 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
9027 // Polygon( hdc, arrow, 7 );
9030 /* [AS] Draw an arrow between two squares */
9031 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9033 int s_x, s_y, d_x, d_y, hor, vert, i;
9035 if( s_col == d_col && s_row == d_row ) {
9039 /* Get source and destination points */
9040 SquareToPos( s_row, s_col, &s_x, &s_y);
9041 SquareToPos( d_row, d_col, &d_x, &d_y);
9044 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9046 else if( d_y < s_y ) {
9047 d_y += squareSize / 2 + squareSize / 4;
9050 d_y += squareSize / 2;
9054 d_x += squareSize / 2 - squareSize / 4;
9056 else if( d_x < s_x ) {
9057 d_x += squareSize / 2 + squareSize / 4;
9060 d_x += squareSize / 2;
9063 s_x += squareSize / 2;
9064 s_y += squareSize / 2;
9067 A_WIDTH = squareSize / 14.; //[HGM] make float
9069 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9071 hor = 64*s_col + 32; vert = 64*s_row + 32;
9072 for(i=0; i<= 64; i++) {
9073 damage[0][vert+6>>6][hor+6>>6] = True;
9074 damage[0][vert-6>>6][hor+6>>6] = True;
9075 damage[0][vert+6>>6][hor-6>>6] = True;
9076 damage[0][vert-6>>6][hor-6>>6] = True;
9077 hor += d_col - s_col; vert += d_row - s_row;
9081 Boolean IsDrawArrowEnabled()
9083 return appData.highlightMoveWithArrow && squareSize >= 32;
9086 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9088 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9089 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9092 void UpdateLogos(int displ)
9094 return; // no logos in XBoard yet