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++;
3977 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3978 XtSetArg(args[j], XtNborderWidth, 0); j++;
3979 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3981 CreateMenuBarPopup(menuBar, menuName, mb);
3983 XtSetArg(args[j], XtNwidth, &w); j++;
3984 XtGetValues(mb->subMenu, args, j);
3985 wtot += mb->textWidth = widths[nr++] = w;
3988 while(wtot > boardWidth - 40) {
3990 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3994 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3996 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3997 XtSetValues(ma[i].subMenu, args, j);
4002 Widget CreateButtonBar(mi)
4006 Widget button, buttonBar;
4010 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4012 XtSetArg(args[j], XtNhSpace, 0); j++;
4014 XtSetArg(args[j], XtNborderWidth, 0); j++;
4015 XtSetArg(args[j], XtNvSpace, 0); j++;
4016 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4017 formWidget, args, j);
4019 while (mi->string != NULL) {
4022 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4023 XtSetArg(args[j], XtNborderWidth, 0); j++;
4025 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4026 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4027 buttonBar, args, j);
4028 XtAddCallback(button, XtNcallback,
4029 (XtCallbackProc) MenuBarSelect,
4030 (caddr_t) mi->proc);
4037 CreatePieceMenu(name, color)
4044 ChessSquare selection;
4046 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4047 boardWidget, args, 0);
4049 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4050 String item = pieceMenuStrings[color][i];
4052 if (strcmp(item, "----") == 0) {
4053 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4056 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4057 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4059 selection = pieceMenuTranslation[color][i];
4060 XtAddCallback(entry, XtNcallback,
4061 (XtCallbackProc) PieceMenuSelect,
4062 (caddr_t) selection);
4063 if (selection == WhitePawn || selection == BlackPawn) {
4064 XtSetArg(args[0], XtNpopupOnEntry, entry);
4065 XtSetValues(menu, args, 1);
4078 ChessSquare selection;
4080 whitePieceMenu = CreatePieceMenu("menuW", 0);
4081 blackPieceMenu = CreatePieceMenu("menuB", 1);
4083 XtRegisterGrabAction(PieceMenuPopup, True,
4084 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4085 GrabModeAsync, GrabModeAsync);
4087 XtSetArg(args[0], XtNlabel, _("Drop"));
4088 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4089 boardWidget, args, 1);
4090 for (i = 0; i < DROP_MENU_SIZE; i++) {
4091 String item = dropMenuStrings[i];
4093 if (strcmp(item, "----") == 0) {
4094 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4097 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4098 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4100 selection = dropMenuTranslation[i];
4101 XtAddCallback(entry, XtNcallback,
4102 (XtCallbackProc) DropMenuSelect,
4103 (caddr_t) selection);
4108 void SetupDropMenu()
4116 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4117 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4118 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4119 dmEnables[i].piece);
4120 XtSetSensitive(entry, p != NULL || !appData.testLegality
4121 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4122 && !appData.icsActive));
4124 while (p && *p++ == dmEnables[i].piece) count++;
4125 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4127 XtSetArg(args[j], XtNlabel, label); j++;
4128 XtSetValues(entry, args, j);
4132 void PieceMenuPopup(w, event, params, num_params)
4136 Cardinal *num_params;
4138 String whichMenu; int menuNr = -2;
4139 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4140 if (event->type == ButtonRelease)
4141 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4142 else if (event->type == ButtonPress)
4143 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4145 case 0: whichMenu = params[0]; break;
4146 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4148 case -1: if (errorUp) ErrorPopDown();
4151 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4154 static void PieceMenuSelect(w, piece, junk)
4159 if (pmFromX < 0 || pmFromY < 0) return;
4160 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4163 static void DropMenuSelect(w, piece, junk)
4168 if (pmFromX < 0 || pmFromY < 0) return;
4169 DropMenuEvent(piece, pmFromX, pmFromY);
4172 void WhiteClock(w, event, prms, nprms)
4178 shiftKey = prms[0][0] & 1;
4182 void BlackClock(w, event, prms, nprms)
4188 shiftKey = prms[0][0] & 1;
4194 * If the user selects on a border boundary, return -1; if off the board,
4195 * return -2. Otherwise map the event coordinate to the square.
4197 int EventToSquare(x, limit)
4205 if ((x % (squareSize + lineGap)) >= squareSize)
4207 x /= (squareSize + lineGap);
4213 static void do_flash_delay(msec)
4219 static void drawHighlight(file, rank, gc)
4225 if (lineGap == 0) return;
4228 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4229 (squareSize + lineGap);
4230 y = lineGap/2 + rank * (squareSize + lineGap);
4232 x = lineGap/2 + file * (squareSize + lineGap);
4233 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4234 (squareSize + lineGap);
4237 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4238 squareSize+lineGap, squareSize+lineGap);
4241 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4242 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4245 SetHighlights(fromX, fromY, toX, toY)
4246 int fromX, fromY, toX, toY;
4248 if (hi1X != fromX || hi1Y != fromY) {
4249 if (hi1X >= 0 && hi1Y >= 0) {
4250 drawHighlight(hi1X, hi1Y, lineGC);
4252 } // [HGM] first erase both, then draw new!
4253 if (hi2X != toX || hi2Y != toY) {
4254 if (hi2X >= 0 && hi2Y >= 0) {
4255 drawHighlight(hi2X, hi2Y, lineGC);
4258 if (hi1X != fromX || hi1Y != fromY) {
4259 if (fromX >= 0 && fromY >= 0) {
4260 drawHighlight(fromX, fromY, highlineGC);
4263 if (hi2X != toX || hi2Y != toY) {
4264 if (toX >= 0 && toY >= 0) {
4265 drawHighlight(toX, toY, highlineGC);
4277 SetHighlights(-1, -1, -1, -1);
4282 SetPremoveHighlights(fromX, fromY, toX, toY)
4283 int fromX, fromY, toX, toY;
4285 if (pm1X != fromX || pm1Y != fromY) {
4286 if (pm1X >= 0 && pm1Y >= 0) {
4287 drawHighlight(pm1X, pm1Y, lineGC);
4289 if (fromX >= 0 && fromY >= 0) {
4290 drawHighlight(fromX, fromY, prelineGC);
4293 if (pm2X != toX || pm2Y != toY) {
4294 if (pm2X >= 0 && pm2Y >= 0) {
4295 drawHighlight(pm2X, pm2Y, lineGC);
4297 if (toX >= 0 && toY >= 0) {
4298 drawHighlight(toX, toY, prelineGC);
4308 ClearPremoveHighlights()
4310 SetPremoveHighlights(-1, -1, -1, -1);
4313 static int CutOutSquare(x, y, x0, y0, kind)
4314 int x, y, *x0, *y0, kind;
4316 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4317 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4319 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4320 if(textureW[kind] < W*squareSize)
4321 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4323 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4324 if(textureH[kind] < H*squareSize)
4325 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4327 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4331 static void BlankSquare(x, y, color, piece, dest, fac)
4332 int x, y, color, fac;
4335 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4337 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4338 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4339 squareSize, squareSize, x*fac, y*fac);
4341 if (useImages && useImageSqs) {
4345 pm = xpmLightSquare;
4350 case 2: /* neutral */
4355 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4356 squareSize, squareSize, x*fac, y*fac);
4366 case 2: /* neutral */
4371 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4376 I split out the routines to draw a piece so that I could
4377 make a generic flash routine.
4379 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4381 int square_color, x, y;
4384 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4385 switch (square_color) {
4387 case 2: /* neutral */
4389 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4390 ? *pieceToOutline(piece)
4391 : *pieceToSolid(piece),
4392 dest, bwPieceGC, 0, 0,
4393 squareSize, squareSize, x, y);
4396 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4397 ? *pieceToSolid(piece)
4398 : *pieceToOutline(piece),
4399 dest, wbPieceGC, 0, 0,
4400 squareSize, squareSize, x, y);
4405 static void monoDrawPiece(piece, square_color, x, y, dest)
4407 int square_color, x, y;
4410 switch (square_color) {
4412 case 2: /* neutral */
4414 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4415 ? *pieceToOutline(piece)
4416 : *pieceToSolid(piece),
4417 dest, bwPieceGC, 0, 0,
4418 squareSize, squareSize, x, y, 1);
4421 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4422 ? *pieceToSolid(piece)
4423 : *pieceToOutline(piece),
4424 dest, wbPieceGC, 0, 0,
4425 squareSize, squareSize, x, y, 1);
4430 static void colorDrawPiece(piece, square_color, x, y, dest)
4432 int square_color, x, y;
4435 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4436 switch (square_color) {
4438 XCopyPlane(xDisplay, *pieceToSolid(piece),
4439 dest, (int) piece < (int) BlackPawn
4440 ? wlPieceGC : blPieceGC, 0, 0,
4441 squareSize, squareSize, x, y, 1);
4444 XCopyPlane(xDisplay, *pieceToSolid(piece),
4445 dest, (int) piece < (int) BlackPawn
4446 ? wdPieceGC : bdPieceGC, 0, 0,
4447 squareSize, squareSize, x, y, 1);
4449 case 2: /* neutral */
4451 XCopyPlane(xDisplay, *pieceToSolid(piece),
4452 dest, (int) piece < (int) BlackPawn
4453 ? wjPieceGC : bjPieceGC, 0, 0,
4454 squareSize, squareSize, x, y, 1);
4459 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4461 int square_color, x, y;
4464 int kind, p = piece;
4466 switch (square_color) {
4468 case 2: /* neutral */
4470 if ((int)piece < (int) BlackPawn) {
4478 if ((int)piece < (int) BlackPawn) {
4486 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4487 if(useTexture & square_color+1) {
4488 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4489 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4490 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4491 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4492 XSetClipMask(xDisplay, wlPieceGC, None);
4493 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4495 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4496 dest, wlPieceGC, 0, 0,
4497 squareSize, squareSize, x, y);
4500 typedef void (*DrawFunc)();
4502 DrawFunc ChooseDrawFunc()
4504 if (appData.monoMode) {
4505 if (DefaultDepth(xDisplay, xScreen) == 1) {
4506 return monoDrawPiece_1bit;
4508 return monoDrawPiece;
4512 return colorDrawPieceImage;
4514 return colorDrawPiece;
4518 /* [HR] determine square color depending on chess variant. */
4519 static int SquareColor(row, column)
4524 if (gameInfo.variant == VariantXiangqi) {
4525 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4527 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4529 } else if (row <= 4) {
4535 square_color = ((column + row) % 2) == 1;
4538 /* [hgm] holdings: next line makes all holdings squares light */
4539 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4541 return square_color;
4544 void DrawSquare(row, column, piece, do_flash)
4545 int row, column, do_flash;
4548 int square_color, x, y, direction, font_ascent, font_descent;
4551 XCharStruct overall;
4555 /* Calculate delay in milliseconds (2-delays per complete flash) */
4556 flash_delay = 500 / appData.flashRate;
4559 x = lineGap + ((BOARD_WIDTH-1)-column) *
4560 (squareSize + lineGap);
4561 y = lineGap + row * (squareSize + lineGap);
4563 x = lineGap + column * (squareSize + lineGap);
4564 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4565 (squareSize + lineGap);
4568 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4570 square_color = SquareColor(row, column);
4572 if ( // [HGM] holdings: blank out area between board and holdings
4573 column == BOARD_LEFT-1 || column == BOARD_RGHT
4574 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4575 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4576 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4578 // [HGM] print piece counts next to holdings
4579 string[1] = NULLCHAR;
4580 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4581 string[0] = '0' + piece;
4582 XTextExtents(countFontStruct, string, 1, &direction,
4583 &font_ascent, &font_descent, &overall);
4584 if (appData.monoMode) {
4585 XDrawImageString(xDisplay, xBoardWindow, countGC,
4586 x + squareSize - overall.width - 2,
4587 y + font_ascent + 1, string, 1);
4589 XDrawString(xDisplay, xBoardWindow, countGC,
4590 x + squareSize - overall.width - 2,
4591 y + font_ascent + 1, string, 1);
4594 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4595 string[0] = '0' + piece;
4596 XTextExtents(countFontStruct, string, 1, &direction,
4597 &font_ascent, &font_descent, &overall);
4598 if (appData.monoMode) {
4599 XDrawImageString(xDisplay, xBoardWindow, countGC,
4600 x + 2, y + font_ascent + 1, string, 1);
4602 XDrawString(xDisplay, xBoardWindow, countGC,
4603 x + 2, y + font_ascent + 1, string, 1);
4607 if (piece == EmptySquare || appData.blindfold) {
4608 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4610 drawfunc = ChooseDrawFunc();
4612 if (do_flash && appData.flashCount > 0) {
4613 for (i=0; i<appData.flashCount; ++i) {
4614 drawfunc(piece, square_color, x, y, xBoardWindow);
4615 XSync(xDisplay, False);
4616 do_flash_delay(flash_delay);
4618 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4619 XSync(xDisplay, False);
4620 do_flash_delay(flash_delay);
4623 drawfunc(piece, square_color, x, y, xBoardWindow);
4627 string[1] = NULLCHAR;
4628 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4629 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4630 string[0] = 'a' + column - BOARD_LEFT;
4631 XTextExtents(coordFontStruct, string, 1, &direction,
4632 &font_ascent, &font_descent, &overall);
4633 if (appData.monoMode) {
4634 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4635 x + squareSize - overall.width - 2,
4636 y + squareSize - font_descent - 1, string, 1);
4638 XDrawString(xDisplay, xBoardWindow, coordGC,
4639 x + squareSize - overall.width - 2,
4640 y + squareSize - font_descent - 1, string, 1);
4643 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4644 string[0] = ONE + row;
4645 XTextExtents(coordFontStruct, string, 1, &direction,
4646 &font_ascent, &font_descent, &overall);
4647 if (appData.monoMode) {
4648 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4649 x + 2, y + font_ascent + 1, string, 1);
4651 XDrawString(xDisplay, xBoardWindow, coordGC,
4652 x + 2, y + font_ascent + 1, string, 1);
4655 if(!partnerUp && marker[row][column]) {
4656 if(appData.monoMode) {
4657 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4658 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4659 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4660 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4662 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4663 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4668 /* Why is this needed on some versions of X? */
4669 void EventProc(widget, unused, event)
4674 if (!XtIsRealized(widget))
4677 switch (event->type) {
4679 if (event->xexpose.count > 0) return; /* no clipping is done */
4680 XDrawPosition(widget, True, NULL);
4681 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4682 flipView = !flipView; partnerUp = !partnerUp;
4683 XDrawPosition(widget, True, NULL);
4684 flipView = !flipView; partnerUp = !partnerUp;
4688 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4695 void DrawPosition(fullRedraw, board)
4696 /*Boolean*/int fullRedraw;
4699 XDrawPosition(boardWidget, fullRedraw, board);
4702 /* Returns 1 if there are "too many" differences between b1 and b2
4703 (i.e. more than 1 move was made) */
4704 static int too_many_diffs(b1, b2)
4710 for (i=0; i<BOARD_HEIGHT; ++i) {
4711 for (j=0; j<BOARD_WIDTH; ++j) {
4712 if (b1[i][j] != b2[i][j]) {
4713 if (++c > 4) /* Castling causes 4 diffs */
4721 /* Matrix describing castling maneuvers */
4722 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4723 static int castling_matrix[4][5] = {
4724 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4725 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4726 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4727 { 7, 7, 4, 5, 6 } /* 0-0, black */
4730 /* Checks whether castling occurred. If it did, *rrow and *rcol
4731 are set to the destination (row,col) of the rook that moved.
4733 Returns 1 if castling occurred, 0 if not.
4735 Note: Only handles a max of 1 castling move, so be sure
4736 to call too_many_diffs() first.
4738 static int check_castle_draw(newb, oldb, rrow, rcol)
4745 /* For each type of castling... */
4746 for (i=0; i<4; ++i) {
4747 r = castling_matrix[i];
4749 /* Check the 4 squares involved in the castling move */
4751 for (j=1; j<=4; ++j) {
4752 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4759 /* All 4 changed, so it must be a castling move */
4768 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4769 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4771 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4774 void DrawSeekBackground( int left, int top, int right, int bottom )
4776 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4779 void DrawSeekText(char *buf, int x, int y)
4781 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4784 void DrawSeekDot(int x, int y, int colorNr)
4786 int square = colorNr & 0x80;
4789 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4791 XFillRectangle(xDisplay, xBoardWindow, color,
4792 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4794 XFillArc(xDisplay, xBoardWindow, color,
4795 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4798 static int damage[2][BOARD_RANKS][BOARD_FILES];
4801 * event handler for redrawing the board
4803 void XDrawPosition(w, repaint, board)
4805 /*Boolean*/int repaint;
4809 static int lastFlipView = 0;
4810 static int lastBoardValid[2] = {0, 0};
4811 static Board lastBoard[2];
4814 int nr = twoBoards*partnerUp;
4816 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4818 if (board == NULL) {
4819 if (!lastBoardValid[nr]) return;
4820 board = lastBoard[nr];
4822 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4823 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4824 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4829 * It would be simpler to clear the window with XClearWindow()
4830 * but this causes a very distracting flicker.
4833 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4835 if ( lineGap && IsDrawArrowEnabled())
4836 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4837 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4839 /* If too much changes (begin observing new game, etc.), don't
4841 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4843 /* Special check for castling so we don't flash both the king
4844 and the rook (just flash the king). */
4846 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4847 /* Draw rook with NO flashing. King will be drawn flashing later */
4848 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4849 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4853 /* First pass -- Draw (newly) empty squares and repair damage.
4854 This prevents you from having a piece show up twice while it
4855 is flashing on its new square */
4856 for (i = 0; i < BOARD_HEIGHT; i++)
4857 for (j = 0; j < BOARD_WIDTH; j++)
4858 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4859 || damage[nr][i][j]) {
4860 DrawSquare(i, j, board[i][j], 0);
4861 damage[nr][i][j] = False;
4864 /* Second pass -- Draw piece(s) in new position and flash them */
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]) {
4868 DrawSquare(i, j, board[i][j], do_flash);
4872 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4873 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4874 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4876 for (i = 0; i < BOARD_HEIGHT; i++)
4877 for (j = 0; j < BOARD_WIDTH; j++) {
4878 DrawSquare(i, j, board[i][j], 0);
4879 damage[nr][i][j] = False;
4883 CopyBoard(lastBoard[nr], board);
4884 lastBoardValid[nr] = 1;
4885 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4886 lastFlipView = flipView;
4888 /* Draw highlights */
4889 if (pm1X >= 0 && pm1Y >= 0) {
4890 drawHighlight(pm1X, pm1Y, prelineGC);
4892 if (pm2X >= 0 && pm2Y >= 0) {
4893 drawHighlight(pm2X, pm2Y, prelineGC);
4895 if (hi1X >= 0 && hi1Y >= 0) {
4896 drawHighlight(hi1X, hi1Y, highlineGC);
4898 if (hi2X >= 0 && hi2Y >= 0) {
4899 drawHighlight(hi2X, hi2Y, highlineGC);
4901 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4903 /* If piece being dragged around board, must redraw that too */
4906 XSync(xDisplay, False);
4911 * event handler for redrawing the board
4913 void DrawPositionProc(w, event, prms, nprms)
4919 XDrawPosition(w, True, NULL);
4924 * event handler for parsing user moves
4926 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4927 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4928 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4929 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4930 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4931 // and at the end FinishMove() to perform the move after optional promotion popups.
4932 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4933 void HandleUserMove(w, event, prms, nprms)
4939 if (w != boardWidget || errorExitStatus != -1) return;
4940 if(nprms) shiftKey = !strcmp(prms[0], "1");
4943 if (event->type == ButtonPress) {
4944 XtPopdown(promotionShell);
4945 XtDestroyWidget(promotionShell);
4946 promotionUp = False;
4954 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4955 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4956 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4959 void AnimateUserMove (Widget w, XEvent * event,
4960 String * params, Cardinal * nParams)
4962 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4963 DragPieceMove(event->xmotion.x, event->xmotion.y);
4966 void HandlePV (Widget w, XEvent * event,
4967 String * params, Cardinal * nParams)
4968 { // [HGM] pv: walk PV
4969 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4972 static int savedIndex; /* gross that this is global */
4974 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4977 XawTextPosition index, dummy;
4980 XawTextGetSelectionPos(w, &index, &dummy);
4981 XtSetArg(arg, XtNstring, &val);
4982 XtGetValues(w, &arg, 1);
4983 ReplaceComment(savedIndex, val);
4984 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4985 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4988 void EditCommentPopUp(index, title, text)
4993 if (text == NULL) text = "";
4994 NewCommentPopup(title, text, index);
4997 void ICSInputBoxPopUp()
5002 extern Option boxOptions[];
5004 void ICSInputSendText()
5011 edit = boxOptions[0].handle;
5013 XtSetArg(args[j], XtNstring, &val); j++;
5014 XtGetValues(edit, args, j);
5016 SendMultiLineToICS(val);
5017 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5018 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5021 void ICSInputBoxPopDown()
5026 void CommentPopUp(title, text)
5029 savedIndex = currentMove; // [HGM] vari
5030 NewCommentPopup(title, text, currentMove);
5033 void CommentPopDown()
5038 static char *openName;
5043 (void) (*fileProc)(openFP, 0, openName);
5046 void FileNamePopUp(label, def, filter, proc, openMode)
5053 fileProc = proc; /* I can't see a way not */
5054 fileOpenMode = openMode; /* to use globals here */
5055 { // [HGM] use file-selector dialog stolen from Ghostview
5056 int index; // this is not supported yet
5057 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5058 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5059 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5060 ScheduleDelayedEvent(&DelayedLoad, 50);
5064 void FileNamePopDown()
5066 if (!filenameUp) return;
5067 XtPopdown(fileNameShell);
5068 XtDestroyWidget(fileNameShell);
5073 void FileNameCallback(w, client_data, call_data)
5075 XtPointer client_data, call_data;
5080 XtSetArg(args[0], XtNlabel, &name);
5081 XtGetValues(w, args, 1);
5083 if (strcmp(name, _("cancel")) == 0) {
5088 FileNameAction(w, NULL, NULL, NULL);
5091 void FileNameAction(w, event, prms, nprms)
5103 name = XawDialogGetValueString(w = XtParent(w));
5105 if ((name != NULL) && (*name != NULLCHAR)) {
5106 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5107 XtPopdown(w = XtParent(XtParent(w)));
5111 p = strrchr(buf, ' ');
5118 fullname = ExpandPathName(buf);
5120 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5123 f = fopen(fullname, fileOpenMode);
5125 DisplayError(_("Failed to open file"), errno);
5127 (void) (*fileProc)(f, index, buf);
5134 XtPopdown(w = XtParent(XtParent(w)));
5140 void PromotionPopUp()
5143 Widget dialog, layout;
5145 Dimension bw_width, pw_width;
5147 char *PromoChars = "wglcqrbnkac+=\0";
5150 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5151 XtGetValues(boardWidget, args, j);
5154 XtSetArg(args[j], XtNresizable, True); j++;
5155 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5157 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5158 shellWidget, args, j);
5160 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5161 layoutArgs, XtNumber(layoutArgs));
5164 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5165 XtSetArg(args[j], XtNborderWidth, 0); j++;
5166 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5169 if(gameInfo.variant != VariantShogi) {
5170 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5171 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5172 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5173 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5174 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5176 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5177 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5178 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5179 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5181 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5182 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5183 gameInfo.variant == VariantGiveaway) {
5184 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5186 if(gameInfo.variant == VariantCapablanca ||
5187 gameInfo.variant == VariantGothic ||
5188 gameInfo.variant == VariantCapaRandom) {
5189 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5190 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5192 } else // [HGM] shogi
5194 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5195 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5197 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5199 XtRealizeWidget(promotionShell);
5200 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5203 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5204 XtGetValues(promotionShell, args, j);
5206 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5207 lineGap + squareSize/3 +
5208 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5209 0 : 6*(squareSize + lineGap)), &x, &y);
5212 XtSetArg(args[j], XtNx, x); j++;
5213 XtSetArg(args[j], XtNy, y); j++;
5214 XtSetValues(promotionShell, args, j);
5216 XtPopup(promotionShell, XtGrabNone);
5221 void PromotionPopDown()
5223 if (!promotionUp) return;
5224 XtPopdown(promotionShell);
5225 XtDestroyWidget(promotionShell);
5226 promotionUp = False;
5229 void PromotionCallback(w, client_data, call_data)
5231 XtPointer client_data, call_data;
5233 int promoChar = * (const char *) client_data;
5237 if (fromX == -1) return;
5244 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5246 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5247 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5252 void ErrorCallback(w, client_data, call_data)
5254 XtPointer client_data, call_data;
5257 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5259 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5265 if (!errorUp) return;
5267 XtPopdown(errorShell);
5268 XtDestroyWidget(errorShell);
5269 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5272 void ErrorPopUp(title, label, modal)
5273 char *title, *label;
5277 Widget dialog, layout;
5281 Dimension bw_width, pw_width;
5282 Dimension pw_height;
5286 XtSetArg(args[i], XtNresizable, True); i++;
5287 XtSetArg(args[i], XtNtitle, title); i++;
5289 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5290 shellWidget, args, i);
5292 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5293 layoutArgs, XtNumber(layoutArgs));
5296 XtSetArg(args[i], XtNlabel, label); i++;
5297 XtSetArg(args[i], XtNborderWidth, 0); i++;
5298 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5301 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5303 XtRealizeWidget(errorShell);
5304 CatchDeleteWindow(errorShell, "ErrorPopDown");
5307 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5308 XtGetValues(boardWidget, args, i);
5310 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5311 XtSetArg(args[i], XtNheight, &pw_height); i++;
5312 XtGetValues(errorShell, args, i);
5315 /* This code seems to tickle an X bug if it is executed too soon
5316 after xboard starts up. The coordinates get transformed as if
5317 the main window was positioned at (0, 0).
5319 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5320 0 - pw_height + squareSize / 3, &x, &y);
5322 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5323 RootWindowOfScreen(XtScreen(boardWidget)),
5324 (bw_width - pw_width) / 2,
5325 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5329 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5332 XtSetArg(args[i], XtNx, x); i++;
5333 XtSetArg(args[i], XtNy, y); i++;
5334 XtSetValues(errorShell, args, i);
5337 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5340 /* Disable all user input other than deleting the window */
5341 static int frozen = 0;
5345 /* Grab by a widget that doesn't accept input */
5346 XtAddGrab(messageWidget, TRUE, FALSE);
5350 /* Undo a FreezeUI */
5353 if (!frozen) return;
5354 XtRemoveGrab(messageWidget);
5358 char *ModeToWidgetName(mode)
5362 case BeginningOfGame:
5363 if (appData.icsActive)
5364 return "menuMode.ICS Client";
5365 else if (appData.noChessProgram ||
5366 *appData.cmailGameName != NULLCHAR)
5367 return "menuMode.Edit Game";
5369 return "menuMode.Machine Black";
5370 case MachinePlaysBlack:
5371 return "menuMode.Machine Black";
5372 case MachinePlaysWhite:
5373 return "menuMode.Machine White";
5375 return "menuMode.Analysis Mode";
5377 return "menuMode.Analyze File";
5378 case TwoMachinesPlay:
5379 return "menuMode.Two Machines";
5381 return "menuMode.Edit Game";
5382 case PlayFromGameFile:
5383 return "menuFile.Load Game";
5385 return "menuMode.Edit Position";
5387 return "menuMode.Training";
5388 case IcsPlayingWhite:
5389 case IcsPlayingBlack:
5393 return "menuMode.ICS Client";
5400 void ModeHighlight()
5403 static int oldPausing = FALSE;
5404 static GameMode oldmode = (GameMode) -1;
5407 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5409 if (pausing != oldPausing) {
5410 oldPausing = pausing;
5412 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5414 XtSetArg(args[0], XtNleftBitmap, None);
5416 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5419 if (appData.showButtonBar) {
5420 /* Always toggle, don't set. Previous code messes up when
5421 invoked while the button is pressed, as releasing it
5422 toggles the state again. */
5425 XtSetArg(args[0], XtNbackground, &oldbg);
5426 XtSetArg(args[1], XtNforeground, &oldfg);
5427 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5429 XtSetArg(args[0], XtNbackground, oldfg);
5430 XtSetArg(args[1], XtNforeground, oldbg);
5432 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5436 wname = ModeToWidgetName(oldmode);
5437 if (wname != NULL) {
5438 XtSetArg(args[0], XtNleftBitmap, None);
5439 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5441 wname = ModeToWidgetName(gameMode);
5442 if (wname != NULL) {
5443 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5444 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5447 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5448 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5450 /* Maybe all the enables should be handled here, not just this one */
5451 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5452 gameMode == Training || gameMode == PlayFromGameFile);
5457 * Button/menu procedures
5459 void ResetProc(w, event, prms, nprms)
5468 int LoadGamePopUp(f, gameNumber, title)
5473 cmailMsgLoaded = FALSE;
5474 if (gameNumber == 0) {
5475 int error = GameListBuild(f);
5477 DisplayError(_("Cannot build game list"), error);
5478 } else if (!ListEmpty(&gameList) &&
5479 ((ListGame *) gameList.tailPred)->number > 1) {
5480 GameListPopUp(f, title);
5486 return LoadGame(f, gameNumber, title, FALSE);
5489 void LoadGameProc(w, event, prms, nprms)
5495 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5498 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5501 void LoadNextGameProc(w, event, prms, nprms)
5510 void LoadPrevGameProc(w, event, prms, nprms)
5519 void ReloadGameProc(w, event, prms, nprms)
5528 void LoadNextPositionProc(w, event, prms, nprms)
5537 void LoadPrevPositionProc(w, event, prms, nprms)
5546 void ReloadPositionProc(w, event, prms, nprms)
5555 void LoadPositionProc(w, event, prms, nprms)
5561 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5564 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5567 void SaveGameProc(w, event, prms, nprms)
5573 FileNamePopUp(_("Save game file name?"),
5574 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5575 appData.oldSaveStyle ? ".game" : ".pgn",
5579 void SavePositionProc(w, event, prms, nprms)
5585 FileNamePopUp(_("Save position file name?"),
5586 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5587 appData.oldSaveStyle ? ".pos" : ".fen",
5591 void ReloadCmailMsgProc(w, event, prms, nprms)
5597 ReloadCmailMsgEvent(FALSE);
5600 void MailMoveProc(w, event, prms, nprms)
5609 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5610 char *selected_fen_position=NULL;
5613 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5614 Atom *type_return, XtPointer *value_return,
5615 unsigned long *length_return, int *format_return)
5617 char *selection_tmp;
5619 if (!selected_fen_position) return False; /* should never happen */
5620 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5621 /* note: since no XtSelectionDoneProc was registered, Xt will
5622 * automatically call XtFree on the value returned. So have to
5623 * make a copy of it allocated with XtMalloc */
5624 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5625 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5627 *value_return=selection_tmp;
5628 *length_return=strlen(selection_tmp);
5629 *type_return=*target;
5630 *format_return = 8; /* bits per byte */
5632 } else if (*target == XA_TARGETS(xDisplay)) {
5633 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5634 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5635 targets_tmp[1] = XA_STRING;
5636 *value_return = targets_tmp;
5637 *type_return = XA_ATOM;
5640 // This code leads to a read of value_return out of bounds on 64-bit systems.
5641 // Other code which I have seen always sets *format_return to 32 independent of
5642 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5643 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5644 *format_return = 8 * sizeof(Atom);
5645 if (*format_return > 32) {
5646 *length_return *= *format_return / 32;
5647 *format_return = 32;
5650 *format_return = 32;
5658 /* note: when called from menu all parameters are NULL, so no clue what the
5659 * Widget which was clicked on was, or what the click event was
5661 void CopyPositionProc(w, event, prms, nprms)
5668 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5669 * have a notion of a position that is selected but not copied.
5670 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5672 if(gameMode == EditPosition) EditPositionDone(TRUE);
5673 if (selected_fen_position) free(selected_fen_position);
5674 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5675 if (!selected_fen_position) return;
5676 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5678 SendPositionSelection,
5679 NULL/* lose_ownership_proc */ ,
5680 NULL/* transfer_done_proc */);
5681 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5683 SendPositionSelection,
5684 NULL/* lose_ownership_proc */ ,
5685 NULL/* transfer_done_proc */);
5688 /* function called when the data to Paste is ready */
5690 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5691 Atom *type, XtPointer value, unsigned long *len, int *format)
5694 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5695 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5696 EditPositionPasteFEN(fenstr);
5700 /* called when Paste Position button is pressed,
5701 * all parameters will be NULL */
5702 void PastePositionProc(w, event, prms, nprms)
5708 XtGetSelectionValue(menuBarWidget,
5709 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5710 /* (XtSelectionCallbackProc) */ PastePositionCB,
5711 NULL, /* client_data passed to PastePositionCB */
5713 /* better to use the time field from the event that triggered the
5714 * call to this function, but that isn't trivial to get
5722 SendGameSelection(Widget w, Atom *selection, Atom *target,
5723 Atom *type_return, XtPointer *value_return,
5724 unsigned long *length_return, int *format_return)
5726 char *selection_tmp;
5728 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5729 FILE* f = fopen(gameCopyFilename, "r");
5732 if (f == NULL) return False;
5736 selection_tmp = XtMalloc(len + 1);
5737 count = fread(selection_tmp, 1, len, f);
5740 XtFree(selection_tmp);
5743 selection_tmp[len] = NULLCHAR;
5744 *value_return = selection_tmp;
5745 *length_return = len;
5746 *type_return = *target;
5747 *format_return = 8; /* bits per byte */
5749 } else if (*target == XA_TARGETS(xDisplay)) {
5750 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5751 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5752 targets_tmp[1] = XA_STRING;
5753 *value_return = targets_tmp;
5754 *type_return = XA_ATOM;
5757 // This code leads to a read of value_return out of bounds on 64-bit systems.
5758 // Other code which I have seen always sets *format_return to 32 independent of
5759 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5760 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5761 *format_return = 8 * sizeof(Atom);
5762 if (*format_return > 32) {
5763 *length_return *= *format_return / 32;
5764 *format_return = 32;
5767 *format_return = 32;
5775 void CopySomething()
5778 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5779 * have a notion of a game that is selected but not copied.
5780 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5782 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5785 NULL/* lose_ownership_proc */ ,
5786 NULL/* transfer_done_proc */);
5787 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5790 NULL/* lose_ownership_proc */ ,
5791 NULL/* transfer_done_proc */);
5794 /* note: when called from menu all parameters are NULL, so no clue what the
5795 * Widget which was clicked on was, or what the click event was
5797 void CopyGameProc(w, event, prms, nprms)
5805 ret = SaveGameToFile(gameCopyFilename, FALSE);
5811 void CopyGameListProc(w, event, prms, nprms)
5817 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5821 /* function called when the data to Paste is ready */
5823 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5824 Atom *type, XtPointer value, unsigned long *len, int *format)
5827 if (value == NULL || *len == 0) {
5828 return; /* nothing had been selected to copy */
5830 f = fopen(gamePasteFilename, "w");
5832 DisplayError(_("Can't open temp file"), errno);
5835 fwrite(value, 1, *len, f);
5838 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5841 /* called when Paste Game button is pressed,
5842 * all parameters will be NULL */
5843 void PasteGameProc(w, event, prms, nprms)
5849 XtGetSelectionValue(menuBarWidget,
5850 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5851 /* (XtSelectionCallbackProc) */ PasteGameCB,
5852 NULL, /* client_data passed to PasteGameCB */
5854 /* better to use the time field from the event that triggered the
5855 * call to this function, but that isn't trivial to get
5865 SaveGameProc(NULL, NULL, NULL, NULL);
5869 void QuitProc(w, event, prms, nprms)
5878 void PauseProc(w, event, prms, nprms)
5888 void MachineBlackProc(w, event, prms, nprms)
5894 MachineBlackEvent();
5897 void MachineWhiteProc(w, event, prms, nprms)
5903 MachineWhiteEvent();
5906 void AnalyzeModeProc(w, event, prms, nprms)
5914 if (!first.analysisSupport) {
5915 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5916 DisplayError(buf, 0);
5919 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5920 if (appData.icsActive) {
5921 if (gameMode != IcsObserving) {
5922 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5923 DisplayError(buf, 0);
5925 if (appData.icsEngineAnalyze) {
5926 if (appData.debugMode)
5927 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5933 /* if enable, use want disable icsEngineAnalyze */
5934 if (appData.icsEngineAnalyze) {
5939 appData.icsEngineAnalyze = TRUE;
5940 if (appData.debugMode)
5941 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5943 #ifndef OPTIONSDIALOG
5944 if (!appData.showThinking)
5945 ShowThinkingProc(w,event,prms,nprms);
5951 void AnalyzeFileProc(w, event, prms, nprms)
5957 if (!first.analysisSupport) {
5959 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5960 DisplayError(buf, 0);
5963 // Reset(FALSE, TRUE);
5964 #ifndef OPTIONSDIALOG
5965 if (!appData.showThinking)
5966 ShowThinkingProc(w,event,prms,nprms);
5969 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5970 AnalysisPeriodicEvent(1);
5973 void TwoMachinesProc(w, event, prms, nprms)
5982 void MatchProc(w, event, prms, nprms)
5991 void IcsClientProc(w, event, prms, nprms)
6000 void EditGameProc(w, event, prms, nprms)
6009 void EditPositionProc(w, event, prms, nprms)
6015 EditPositionEvent();
6018 void TrainingProc(w, event, prms, nprms)
6027 void EditCommentProc(w, event, prms, nprms)
6035 if (PopDown(1)) { // popdown succesful
6037 XtSetArg(args[j], XtNleftBitmap, None); j++;
6038 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6039 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6040 } else // was not up
6044 void IcsInputBoxProc(w, event, prms, nprms)
6050 if (!PopDown(4)) ICSInputBoxPopUp();
6053 void AcceptProc(w, event, prms, nprms)
6062 void DeclineProc(w, event, prms, nprms)
6071 void RematchProc(w, event, prms, nprms)
6080 void CallFlagProc(w, event, prms, nprms)
6089 void DrawProc(w, event, prms, nprms)
6098 void AbortProc(w, event, prms, nprms)
6107 void AdjournProc(w, event, prms, nprms)
6116 void ResignProc(w, event, prms, nprms)
6125 void AdjuWhiteProc(w, event, prms, nprms)
6131 UserAdjudicationEvent(+1);
6134 void AdjuBlackProc(w, event, prms, nprms)
6140 UserAdjudicationEvent(-1);
6143 void AdjuDrawProc(w, event, prms, nprms)
6149 UserAdjudicationEvent(0);
6152 void EnterKeyProc(w, event, prms, nprms)
6158 if (shellUp[4] == True)
6162 void UpKeyProc(w, event, prms, nprms)
6167 { // [HGM] input: let up-arrow recall previous line from history
6174 if (!shellUp[4]) return;
6175 edit = boxOptions[0].handle;
6177 XtSetArg(args[j], XtNstring, &val); j++;
6178 XtGetValues(edit, args, j);
6179 val = PrevInHistory(val);
6180 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6181 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6183 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6184 XawTextReplace(edit, 0, 0, &t);
6185 XawTextSetInsertionPoint(edit, 9999);
6189 void DownKeyProc(w, event, prms, nprms)
6194 { // [HGM] input: let down-arrow recall next line from history
6199 if (!shellUp[4]) return;
6200 edit = boxOptions[0].handle;
6201 val = NextInHistory();
6202 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6203 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6205 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6206 XawTextReplace(edit, 0, 0, &t);
6207 XawTextSetInsertionPoint(edit, 9999);
6211 void StopObservingProc(w, event, prms, nprms)
6217 StopObservingEvent();
6220 void StopExaminingProc(w, event, prms, nprms)
6226 StopExaminingEvent();
6229 void UploadProc(w, event, prms, nprms)
6239 void ForwardProc(w, event, prms, nprms)
6249 void BackwardProc(w, event, prms, nprms)
6258 void TempBackwardProc(w, event, prms, nprms)
6264 if (!TempBackwardActive) {
6265 TempBackwardActive = True;
6270 void TempForwardProc(w, event, prms, nprms)
6276 /* Check to see if triggered by a key release event for a repeating key.
6277 * If so the next queued event will be a key press of the same key at the same time */
6278 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6280 XPeekEvent(xDisplay, &next);
6281 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6282 next.xkey.keycode == event->xkey.keycode)
6286 TempBackwardActive = False;
6289 void ToStartProc(w, event, prms, nprms)
6298 void ToEndProc(w, event, prms, nprms)
6307 void RevertProc(w, event, prms, nprms)
6316 void AnnotateProc(w, event, prms, nprms)
6325 void TruncateGameProc(w, event, prms, nprms)
6331 TruncateGameEvent();
6333 void RetractMoveProc(w, event, prms, nprms)
6342 void MoveNowProc(w, event, prms, nprms)
6351 void FlipViewProc(w, event, prms, nprms)
6357 flipView = !flipView;
6358 DrawPosition(True, NULL);
6361 void PonderNextMoveProc(w, event, prms, nprms)
6369 PonderNextMoveEvent(!appData.ponderNextMove);
6370 #ifndef OPTIONSDIALOG
6371 if (appData.ponderNextMove) {
6372 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6374 XtSetArg(args[0], XtNleftBitmap, None);
6376 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6381 #ifndef OPTIONSDIALOG
6382 void AlwaysQueenProc(w, event, prms, nprms)
6390 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6392 if (appData.alwaysPromoteToQueen) {
6393 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6395 XtSetArg(args[0], XtNleftBitmap, None);
6397 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6401 void AnimateDraggingProc(w, event, prms, nprms)
6409 appData.animateDragging = !appData.animateDragging;
6411 if (appData.animateDragging) {
6412 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6415 XtSetArg(args[0], XtNleftBitmap, None);
6417 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6421 void AnimateMovingProc(w, event, prms, nprms)
6429 appData.animate = !appData.animate;
6431 if (appData.animate) {
6432 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6435 XtSetArg(args[0], XtNleftBitmap, None);
6437 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6441 void AutoflagProc(w, event, prms, nprms)
6449 appData.autoCallFlag = !appData.autoCallFlag;
6451 if (appData.autoCallFlag) {
6452 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6454 XtSetArg(args[0], XtNleftBitmap, None);
6456 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6460 void AutoflipProc(w, event, prms, nprms)
6468 appData.autoFlipView = !appData.autoFlipView;
6470 if (appData.autoFlipView) {
6471 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6473 XtSetArg(args[0], XtNleftBitmap, None);
6475 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6479 void BlindfoldProc(w, event, prms, nprms)
6487 appData.blindfold = !appData.blindfold;
6489 if (appData.blindfold) {
6490 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6492 XtSetArg(args[0], XtNleftBitmap, None);
6494 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6497 DrawPosition(True, NULL);
6500 void TestLegalityProc(w, event, prms, nprms)
6508 appData.testLegality = !appData.testLegality;
6510 if (appData.testLegality) {
6511 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6513 XtSetArg(args[0], XtNleftBitmap, None);
6515 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6520 void FlashMovesProc(w, event, prms, nprms)
6528 if (appData.flashCount == 0) {
6529 appData.flashCount = 3;
6531 appData.flashCount = -appData.flashCount;
6534 if (appData.flashCount > 0) {
6535 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6537 XtSetArg(args[0], XtNleftBitmap, None);
6539 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6544 void HighlightDraggingProc(w, event, prms, nprms)
6552 appData.highlightDragging = !appData.highlightDragging;
6554 if (appData.highlightDragging) {
6555 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6557 XtSetArg(args[0], XtNleftBitmap, None);
6559 XtSetValues(XtNameToWidget(menuBarWidget,
6560 "menuOptions.Highlight Dragging"), args, 1);
6564 void HighlightLastMoveProc(w, event, prms, nprms)
6572 appData.highlightLastMove = !appData.highlightLastMove;
6574 if (appData.highlightLastMove) {
6575 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6577 XtSetArg(args[0], XtNleftBitmap, None);
6579 XtSetValues(XtNameToWidget(menuBarWidget,
6580 "menuOptions.Highlight Last Move"), args, 1);
6583 void HighlightArrowProc(w, event, prms, nprms)
6591 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6593 if (appData.highlightMoveWithArrow) {
6594 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6596 XtSetArg(args[0], XtNleftBitmap, None);
6598 XtSetValues(XtNameToWidget(menuBarWidget,
6599 "menuOptions.Arrow"), args, 1);
6603 void IcsAlarmProc(w, event, prms, nprms)
6611 appData.icsAlarm = !appData.icsAlarm;
6613 if (appData.icsAlarm) {
6614 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6616 XtSetArg(args[0], XtNleftBitmap, None);
6618 XtSetValues(XtNameToWidget(menuBarWidget,
6619 "menuOptions.ICS Alarm"), args, 1);
6623 void MoveSoundProc(w, event, prms, nprms)
6631 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6633 if (appData.ringBellAfterMoves) {
6634 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6636 XtSetArg(args[0], XtNleftBitmap, None);
6638 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6642 void OneClickProc(w, event, prms, nprms)
6650 appData.oneClick = !appData.oneClick;
6652 if (appData.oneClick) {
6653 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6655 XtSetArg(args[0], XtNleftBitmap, None);
6657 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6661 void PeriodicUpdatesProc(w, event, prms, nprms)
6669 PeriodicUpdatesEvent(!appData.periodicUpdates);
6671 if (appData.periodicUpdates) {
6672 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6674 XtSetArg(args[0], XtNleftBitmap, None);
6676 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6680 void PopupExitMessageProc(w, event, prms, nprms)
6688 appData.popupExitMessage = !appData.popupExitMessage;
6690 if (appData.popupExitMessage) {
6691 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6693 XtSetArg(args[0], XtNleftBitmap, None);
6695 XtSetValues(XtNameToWidget(menuBarWidget,
6696 "menuOptions.Popup Exit Message"), args, 1);
6699 void PopupMoveErrorsProc(w, event, prms, nprms)
6707 appData.popupMoveErrors = !appData.popupMoveErrors;
6709 if (appData.popupMoveErrors) {
6710 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6712 XtSetArg(args[0], XtNleftBitmap, None);
6714 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6719 void PremoveProc(w, event, prms, nprms)
6727 appData.premove = !appData.premove;
6729 if (appData.premove) {
6730 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6732 XtSetArg(args[0], XtNleftBitmap, None);
6734 XtSetValues(XtNameToWidget(menuBarWidget,
6735 "menuOptions.Premove"), args, 1);
6739 void ShowCoordsProc(w, event, prms, nprms)
6747 appData.showCoords = !appData.showCoords;
6749 if (appData.showCoords) {
6750 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6752 XtSetArg(args[0], XtNleftBitmap, None);
6754 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6757 DrawPosition(True, NULL);
6760 void ShowThinkingProc(w, event, prms, nprms)
6766 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6767 ShowThinkingEvent();
6770 void HideThinkingProc(w, event, prms, nprms)
6778 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6779 ShowThinkingEvent();
6781 if (appData.hideThinkingFromHuman) {
6782 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6784 XtSetArg(args[0], XtNleftBitmap, None);
6786 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6791 void SaveOnExitProc(w, event, prms, nprms)
6799 saveSettingsOnExit = !saveSettingsOnExit;
6801 if (saveSettingsOnExit) {
6802 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6804 XtSetArg(args[0], XtNleftBitmap, None);
6806 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6810 void SaveSettingsProc(w, event, prms, nprms)
6816 SaveSettings(settingsFileName);
6819 void InfoProc(w, event, prms, nprms)
6826 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6831 void ManProc(w, event, prms, nprms)
6839 if (nprms && *nprms > 0)
6843 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6847 void HintProc(w, event, prms, nprms)
6856 void BookProc(w, event, prms, nprms)
6865 void AboutProc(w, event, prms, nprms)
6873 char *zippy = _(" (with Zippy code)");
6877 snprintf(buf, sizeof(buf),
6879 "Copyright 1991 Digital Equipment Corporation\n"
6880 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6881 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6882 "%s is free software and carries NO WARRANTY;"
6883 "see the file COPYING for more information."),
6884 programVersion, zippy, PACKAGE);
6885 ErrorPopUp(_("About XBoard"), buf, FALSE);
6888 void DebugProc(w, event, prms, nprms)
6894 appData.debugMode = !appData.debugMode;
6897 void AboutGameProc(w, event, prms, nprms)
6906 void NothingProc(w, event, prms, nprms)
6915 void DisplayMessage(message, extMessage)
6916 char *message, *extMessage;
6918 /* display a message in the message widget */
6927 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6932 message = extMessage;
6936 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6938 /* need to test if messageWidget already exists, since this function
6939 can also be called during the startup, if for example a Xresource
6940 is not set up correctly */
6943 XtSetArg(arg, XtNlabel, message);
6944 XtSetValues(messageWidget, &arg, 1);
6950 void DisplayTitle(text)
6955 char title[MSG_SIZ];
6958 if (text == NULL) text = "";
6960 if (appData.titleInWindow) {
6962 XtSetArg(args[i], XtNlabel, text); i++;
6963 XtSetValues(titleWidget, args, i);
6966 if (*text != NULLCHAR) {
6967 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6968 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6969 } else if (appData.icsActive) {
6970 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6971 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6972 } else if (appData.cmailGameName[0] != NULLCHAR) {
6973 snprintf(icon, sizeof(icon), "%s", "CMail");
6974 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6976 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6977 } else if (gameInfo.variant == VariantGothic) {
6978 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6979 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6982 } else if (gameInfo.variant == VariantFalcon) {
6983 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6984 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6986 } else if (appData.noChessProgram) {
6987 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6988 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6990 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6991 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6994 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6995 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6996 XtSetValues(shellWidget, args, i);
6997 XSync(xDisplay, False);
7002 DisplayError(message, error)
7009 if (appData.debugMode || appData.matchMode) {
7010 fprintf(stderr, "%s: %s\n", programName, message);
7013 if (appData.debugMode || appData.matchMode) {
7014 fprintf(stderr, "%s: %s: %s\n",
7015 programName, message, strerror(error));
7017 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7020 ErrorPopUp(_("Error"), message, FALSE);
7024 void DisplayMoveError(message)
7029 DrawPosition(FALSE, NULL);
7030 if (appData.debugMode || appData.matchMode) {
7031 fprintf(stderr, "%s: %s\n", programName, message);
7033 if (appData.popupMoveErrors) {
7034 ErrorPopUp(_("Error"), message, FALSE);
7036 DisplayMessage(message, "");
7041 void DisplayFatalError(message, error, status)
7047 errorExitStatus = status;
7049 fprintf(stderr, "%s: %s\n", programName, message);
7051 fprintf(stderr, "%s: %s: %s\n",
7052 programName, message, strerror(error));
7053 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7056 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7057 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7063 void DisplayInformation(message)
7067 ErrorPopUp(_("Information"), message, TRUE);
7070 void DisplayNote(message)
7074 ErrorPopUp(_("Note"), message, FALSE);
7078 NullXErrorCheck(dpy, error_event)
7080 XErrorEvent *error_event;
7085 void DisplayIcsInteractionTitle(message)
7088 if (oldICSInteractionTitle == NULL) {
7089 /* Magic to find the old window title, adapted from vim */
7090 char *wina = getenv("WINDOWID");
7092 Window win = (Window) atoi(wina);
7093 Window root, parent, *children;
7094 unsigned int nchildren;
7095 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7097 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7098 if (!XQueryTree(xDisplay, win, &root, &parent,
7099 &children, &nchildren)) break;
7100 if (children) XFree((void *)children);
7101 if (parent == root || parent == 0) break;
7104 XSetErrorHandler(oldHandler);
7106 if (oldICSInteractionTitle == NULL) {
7107 oldICSInteractionTitle = "xterm";
7110 printf("\033]0;%s\007", message);
7114 char pendingReplyPrefix[MSG_SIZ];
7115 ProcRef pendingReplyPR;
7117 void AskQuestionProc(w, event, prms, nprms)
7124 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7128 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7131 void AskQuestionPopDown()
7133 if (!askQuestionUp) return;
7134 XtPopdown(askQuestionShell);
7135 XtDestroyWidget(askQuestionShell);
7136 askQuestionUp = False;
7139 void AskQuestionReplyAction(w, event, prms, nprms)
7149 reply = XawDialogGetValueString(w = XtParent(w));
7150 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7151 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7152 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7153 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7154 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7155 AskQuestionPopDown();
7157 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7160 void AskQuestionCallback(w, client_data, call_data)
7162 XtPointer client_data, call_data;
7167 XtSetArg(args[0], XtNlabel, &name);
7168 XtGetValues(w, args, 1);
7170 if (strcmp(name, _("cancel")) == 0) {
7171 AskQuestionPopDown();
7173 AskQuestionReplyAction(w, NULL, NULL, NULL);
7177 void AskQuestion(title, question, replyPrefix, pr)
7178 char *title, *question, *replyPrefix;
7182 Widget popup, layout, dialog, edit;
7188 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7189 pendingReplyPR = pr;
7192 XtSetArg(args[i], XtNresizable, True); i++;
7193 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7194 askQuestionShell = popup =
7195 XtCreatePopupShell(title, transientShellWidgetClass,
7196 shellWidget, args, i);
7199 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7200 layoutArgs, XtNumber(layoutArgs));
7203 XtSetArg(args[i], XtNlabel, question); i++;
7204 XtSetArg(args[i], XtNvalue, ""); i++;
7205 XtSetArg(args[i], XtNborderWidth, 0); i++;
7206 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7209 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7210 (XtPointer) dialog);
7211 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7212 (XtPointer) dialog);
7214 XtRealizeWidget(popup);
7215 CatchDeleteWindow(popup, "AskQuestionPopDown");
7217 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7218 &x, &y, &win_x, &win_y, &mask);
7220 XtSetArg(args[0], XtNx, x - 10);
7221 XtSetArg(args[1], XtNy, y - 30);
7222 XtSetValues(popup, args, 2);
7224 XtPopup(popup, XtGrabExclusive);
7225 askQuestionUp = True;
7227 edit = XtNameToWidget(dialog, "*value");
7228 XtSetKeyboardFocus(popup, edit);
7236 if (*name == NULLCHAR) {
7238 } else if (strcmp(name, "$") == 0) {
7239 putc(BELLCHAR, stderr);
7242 char *prefix = "", *sep = "";
7243 if(appData.soundProgram[0] == NULLCHAR) return;
7244 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7245 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7253 PlaySound(appData.soundMove);
7259 PlaySound(appData.soundIcsWin);
7265 PlaySound(appData.soundIcsLoss);
7271 PlaySound(appData.soundIcsDraw);
7275 PlayIcsUnfinishedSound()
7277 PlaySound(appData.soundIcsUnfinished);
7283 PlaySound(appData.soundIcsAlarm);
7289 PlaySound(appData.soundTell);
7295 system("stty echo");
7302 system("stty -echo");
7307 RunCommand(char *buf)
7313 Colorize(cc, continuation)
7318 int count, outCount, error;
7320 if (textColors[(int)cc].bg > 0) {
7321 if (textColors[(int)cc].fg > 0) {
7322 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7323 textColors[(int)cc].fg, textColors[(int)cc].bg);
7325 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7326 textColors[(int)cc].bg);
7329 if (textColors[(int)cc].fg > 0) {
7330 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7331 textColors[(int)cc].fg);
7333 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7336 count = strlen(buf);
7337 outCount = OutputToProcess(NoProc, buf, count, &error);
7338 if (outCount < count) {
7339 DisplayFatalError(_("Error writing to display"), error, 1);
7342 if (continuation) return;
7345 PlaySound(appData.soundShout);
7348 PlaySound(appData.soundSShout);
7351 PlaySound(appData.soundChannel1);
7354 PlaySound(appData.soundChannel);
7357 PlaySound(appData.soundKibitz);
7360 PlaySound(appData.soundTell);
7362 case ColorChallenge:
7363 PlaySound(appData.soundChallenge);
7366 PlaySound(appData.soundRequest);
7369 PlaySound(appData.soundSeek);
7380 return getpwuid(getuid())->pw_name;
7384 ExpandPathName(path)
7387 static char static_buf[4*MSG_SIZ];
7388 char *d, *s, buf[4*MSG_SIZ];
7394 while (*s && isspace(*s))
7403 if (*(s+1) == '/') {
7404 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7408 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7409 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7410 pwd = getpwnam(buf);
7413 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7417 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7418 strcat(d, strchr(s+1, '/'));
7422 safeStrCpy(d, s, 4*MSG_SIZ );
7429 static char host_name[MSG_SIZ];
7431 #if HAVE_GETHOSTNAME
7432 gethostname(host_name, MSG_SIZ);
7434 #else /* not HAVE_GETHOSTNAME */
7435 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7436 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7438 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7440 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7441 #endif /* not HAVE_GETHOSTNAME */
7444 XtIntervalId delayedEventTimerXID = 0;
7445 DelayedEventCallback delayedEventCallback = 0;
7450 delayedEventTimerXID = 0;
7451 delayedEventCallback();
7455 ScheduleDelayedEvent(cb, millisec)
7456 DelayedEventCallback cb; long millisec;
7458 if(delayedEventTimerXID && delayedEventCallback == cb)
7459 // [HGM] alive: replace, rather than add or flush identical event
7460 XtRemoveTimeOut(delayedEventTimerXID);
7461 delayedEventCallback = cb;
7462 delayedEventTimerXID =
7463 XtAppAddTimeOut(appContext, millisec,
7464 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7467 DelayedEventCallback
7470 if (delayedEventTimerXID) {
7471 return delayedEventCallback;
7478 CancelDelayedEvent()
7480 if (delayedEventTimerXID) {
7481 XtRemoveTimeOut(delayedEventTimerXID);
7482 delayedEventTimerXID = 0;
7486 XtIntervalId loadGameTimerXID = 0;
7488 int LoadGameTimerRunning()
7490 return loadGameTimerXID != 0;
7493 int StopLoadGameTimer()
7495 if (loadGameTimerXID != 0) {
7496 XtRemoveTimeOut(loadGameTimerXID);
7497 loadGameTimerXID = 0;
7505 LoadGameTimerCallback(arg, id)
7509 loadGameTimerXID = 0;
7514 StartLoadGameTimer(millisec)
7518 XtAppAddTimeOut(appContext, millisec,
7519 (XtTimerCallbackProc) LoadGameTimerCallback,
7523 XtIntervalId analysisClockXID = 0;
7526 AnalysisClockCallback(arg, id)
7530 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7531 || appData.icsEngineAnalyze) { // [DM]
7532 AnalysisPeriodicEvent(0);
7533 StartAnalysisClock();
7538 StartAnalysisClock()
7541 XtAppAddTimeOut(appContext, 2000,
7542 (XtTimerCallbackProc) AnalysisClockCallback,
7546 XtIntervalId clockTimerXID = 0;
7548 int ClockTimerRunning()
7550 return clockTimerXID != 0;
7553 int StopClockTimer()
7555 if (clockTimerXID != 0) {
7556 XtRemoveTimeOut(clockTimerXID);
7565 ClockTimerCallback(arg, id)
7574 StartClockTimer(millisec)
7578 XtAppAddTimeOut(appContext, millisec,
7579 (XtTimerCallbackProc) ClockTimerCallback,
7584 DisplayTimerLabel(w, color, timer, highlight)
7593 /* check for low time warning */
7594 Pixel foregroundOrWarningColor = timerForegroundPixel;
7597 appData.lowTimeWarning &&
7598 (timer / 1000) < appData.icsAlarmTime)
7599 foregroundOrWarningColor = lowTimeWarningColor;
7601 if (appData.clockMode) {
7602 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7603 XtSetArg(args[0], XtNlabel, buf);
7605 snprintf(buf, MSG_SIZ, "%s ", color);
7606 XtSetArg(args[0], XtNlabel, buf);
7611 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7612 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7614 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7615 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7618 XtSetValues(w, args, 3);
7622 DisplayWhiteClock(timeRemaining, highlight)
7628 if(appData.noGUI) return;
7629 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7630 if (highlight && iconPixmap == bIconPixmap) {
7631 iconPixmap = wIconPixmap;
7632 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7633 XtSetValues(shellWidget, args, 1);
7638 DisplayBlackClock(timeRemaining, highlight)
7644 if(appData.noGUI) return;
7645 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7646 if (highlight && iconPixmap == wIconPixmap) {
7647 iconPixmap = bIconPixmap;
7648 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7649 XtSetValues(shellWidget, args, 1);
7667 int StartChildProcess(cmdLine, dir, pr)
7674 int to_prog[2], from_prog[2];
7678 if (appData.debugMode) {
7679 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7682 /* We do NOT feed the cmdLine to the shell; we just
7683 parse it into blank-separated arguments in the
7684 most simple-minded way possible.
7687 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7690 while(*p == ' ') p++;
7692 if(*p == '"' || *p == '\'')
7693 p = strchr(++argv[i-1], *p);
7694 else p = strchr(p, ' ');
7695 if (p == NULL) break;
7700 SetUpChildIO(to_prog, from_prog);
7702 if ((pid = fork()) == 0) {
7704 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7705 close(to_prog[1]); // first close the unused pipe ends
7706 close(from_prog[0]);
7707 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7708 dup2(from_prog[1], 1);
7709 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7710 close(from_prog[1]); // and closing again loses one of the pipes!
7711 if(fileno(stderr) >= 2) // better safe than sorry...
7712 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7714 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7719 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7721 execvp(argv[0], argv);
7723 /* If we get here, exec failed */
7728 /* Parent process */
7730 close(from_prog[1]);
7732 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7735 cp->fdFrom = from_prog[0];
7736 cp->fdTo = to_prog[1];
7741 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7742 static RETSIGTYPE AlarmCallBack(int n)
7748 DestroyChildProcess(pr, signalType)
7752 ChildProc *cp = (ChildProc *) pr;
7754 if (cp->kind != CPReal) return;
7756 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7757 signal(SIGALRM, AlarmCallBack);
7759 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7760 kill(cp->pid, SIGKILL); // kill it forcefully
7761 wait((int *) 0); // and wait again
7765 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7767 /* Process is exiting either because of the kill or because of
7768 a quit command sent by the backend; either way, wait for it to die.
7777 InterruptChildProcess(pr)
7780 ChildProc *cp = (ChildProc *) pr;
7782 if (cp->kind != CPReal) return;
7783 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7786 int OpenTelnet(host, port, pr)
7791 char cmdLine[MSG_SIZ];
7793 if (port[0] == NULLCHAR) {
7794 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7796 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7798 return StartChildProcess(cmdLine, "", pr);
7801 int OpenTCP(host, port, pr)
7807 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7808 #else /* !OMIT_SOCKETS */
7809 struct addrinfo hints;
7810 struct addrinfo *ais, *ai;
7815 memset(&hints, 0, sizeof(hints));
7816 hints.ai_family = AF_UNSPEC;
7817 hints.ai_socktype = SOCK_STREAM;
7819 error = getaddrinfo(host, port, &hints, &ais);
7821 /* a getaddrinfo error is not an errno, so can't return it */
7822 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7823 host, port, gai_strerror(error));
7827 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7828 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7832 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7845 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7851 #endif /* !OMIT_SOCKETS */
7856 int OpenCommPort(name, pr)
7863 fd = open(name, 2, 0);
7864 if (fd < 0) return errno;
7866 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7876 int OpenLoopback(pr)
7882 SetUpChildIO(to, from);
7884 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7887 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7894 int OpenRcmd(host, user, cmd, pr)
7895 char *host, *user, *cmd;
7898 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7902 #define INPUT_SOURCE_BUF_SIZE 8192
7911 char buf[INPUT_SOURCE_BUF_SIZE];
7916 DoInputCallback(closure, source, xid)
7921 InputSource *is = (InputSource *) closure;
7926 if (is->lineByLine) {
7927 count = read(is->fd, is->unused,
7928 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7930 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7933 is->unused += count;
7935 while (p < is->unused) {
7936 q = memchr(p, '\n', is->unused - p);
7937 if (q == NULL) break;
7939 (is->func)(is, is->closure, p, q - p, 0);
7943 while (p < is->unused) {
7948 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7953 (is->func)(is, is->closure, is->buf, count, error);
7957 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7964 ChildProc *cp = (ChildProc *) pr;
7966 is = (InputSource *) calloc(1, sizeof(InputSource));
7967 is->lineByLine = lineByLine;
7971 is->fd = fileno(stdin);
7973 is->kind = cp->kind;
7974 is->fd = cp->fdFrom;
7977 is->unused = is->buf;
7980 is->xid = XtAppAddInput(appContext, is->fd,
7981 (XtPointer) (XtInputReadMask),
7982 (XtInputCallbackProc) DoInputCallback,
7984 is->closure = closure;
7985 return (InputSourceRef) is;
7989 RemoveInputSource(isr)
7992 InputSource *is = (InputSource *) isr;
7994 if (is->xid == 0) return;
7995 XtRemoveInput(is->xid);
7999 int OutputToProcess(pr, message, count, outError)
8005 static int line = 0;
8006 ChildProc *cp = (ChildProc *) pr;
8011 if (appData.noJoin || !appData.useInternalWrap)
8012 outCount = fwrite(message, 1, count, stdout);
8015 int width = get_term_width();
8016 int len = wrap(NULL, message, count, width, &line);
8017 char *msg = malloc(len);
8021 outCount = fwrite(message, 1, count, stdout);
8024 dbgchk = wrap(msg, message, count, width, &line);
8025 if (dbgchk != len && appData.debugMode)
8026 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8027 outCount = fwrite(msg, 1, dbgchk, stdout);
8033 outCount = write(cp->fdTo, message, count);
8043 /* Output message to process, with "ms" milliseconds of delay
8044 between each character. This is needed when sending the logon
8045 script to ICC, which for some reason doesn't like the
8046 instantaneous send. */
8047 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8054 ChildProc *cp = (ChildProc *) pr;
8059 r = write(cp->fdTo, message++, 1);
8072 /**** Animation code by Hugh Fisher, DCS, ANU.
8074 Known problem: if a window overlapping the board is
8075 moved away while a piece is being animated underneath,
8076 the newly exposed area won't be updated properly.
8077 I can live with this.
8079 Known problem: if you look carefully at the animation
8080 of pieces in mono mode, they are being drawn as solid
8081 shapes without interior detail while moving. Fixing
8082 this would be a major complication for minimal return.
8085 /* Masks for XPM pieces. Black and white pieces can have
8086 different shapes, but in the interest of retaining my
8087 sanity pieces must have the same outline on both light
8088 and dark squares, and all pieces must use the same
8089 background square colors/images. */
8091 static int xpmDone = 0;
8094 CreateAnimMasks (pieceDepth)
8101 unsigned long plane;
8104 /* Need a bitmap just to get a GC with right depth */
8105 buf = XCreatePixmap(xDisplay, xBoardWindow,
8107 values.foreground = 1;
8108 values.background = 0;
8109 /* Don't use XtGetGC, not read only */
8110 maskGC = XCreateGC(xDisplay, buf,
8111 GCForeground | GCBackground, &values);
8112 XFreePixmap(xDisplay, buf);
8114 buf = XCreatePixmap(xDisplay, xBoardWindow,
8115 squareSize, squareSize, pieceDepth);
8116 values.foreground = XBlackPixel(xDisplay, xScreen);
8117 values.background = XWhitePixel(xDisplay, xScreen);
8118 bufGC = XCreateGC(xDisplay, buf,
8119 GCForeground | GCBackground, &values);
8121 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8122 /* Begin with empty mask */
8123 if(!xpmDone) // [HGM] pieces: keep using existing
8124 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8125 squareSize, squareSize, 1);
8126 XSetFunction(xDisplay, maskGC, GXclear);
8127 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8128 0, 0, squareSize, squareSize);
8130 /* Take a copy of the piece */
8135 XSetFunction(xDisplay, bufGC, GXcopy);
8136 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8138 0, 0, squareSize, squareSize, 0, 0);
8140 /* XOR the background (light) over the piece */
8141 XSetFunction(xDisplay, bufGC, GXxor);
8143 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8144 0, 0, squareSize, squareSize, 0, 0);
8146 XSetForeground(xDisplay, bufGC, lightSquareColor);
8147 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8150 /* We now have an inverted piece image with the background
8151 erased. Construct mask by just selecting all the non-zero
8152 pixels - no need to reconstruct the original image. */
8153 XSetFunction(xDisplay, maskGC, GXor);
8155 /* Might be quicker to download an XImage and create bitmap
8156 data from it rather than this N copies per piece, but it
8157 only takes a fraction of a second and there is a much
8158 longer delay for loading the pieces. */
8159 for (n = 0; n < pieceDepth; n ++) {
8160 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8161 0, 0, squareSize, squareSize,
8167 XFreePixmap(xDisplay, buf);
8168 XFreeGC(xDisplay, bufGC);
8169 XFreeGC(xDisplay, maskGC);
8173 InitAnimState (anim, info)
8175 XWindowAttributes * info;
8180 /* Each buffer is square size, same depth as window */
8181 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8182 squareSize, squareSize, info->depth);
8183 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8184 squareSize, squareSize, info->depth);
8186 /* Create a plain GC for blitting */
8187 mask = GCForeground | GCBackground | GCFunction |
8188 GCPlaneMask | GCGraphicsExposures;
8189 values.foreground = XBlackPixel(xDisplay, xScreen);
8190 values.background = XWhitePixel(xDisplay, xScreen);
8191 values.function = GXcopy;
8192 values.plane_mask = AllPlanes;
8193 values.graphics_exposures = False;
8194 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8196 /* Piece will be copied from an existing context at
8197 the start of each new animation/drag. */
8198 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8200 /* Outline will be a read-only copy of an existing */
8201 anim->outlineGC = None;
8207 XWindowAttributes info;
8209 if (xpmDone && gameInfo.variant == oldVariant) return;
8210 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8211 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8213 InitAnimState(&game, &info);
8214 InitAnimState(&player, &info);
8216 /* For XPM pieces, we need bitmaps to use as masks. */
8218 CreateAnimMasks(info.depth), xpmDone = 1;
8223 static Boolean frameWaiting;
8225 static RETSIGTYPE FrameAlarm (sig)
8228 frameWaiting = False;
8229 /* In case System-V style signals. Needed?? */
8230 signal(SIGALRM, FrameAlarm);
8237 struct itimerval delay;
8239 XSync(xDisplay, False);
8242 frameWaiting = True;
8243 signal(SIGALRM, FrameAlarm);
8244 delay.it_interval.tv_sec =
8245 delay.it_value.tv_sec = time / 1000;
8246 delay.it_interval.tv_usec =
8247 delay.it_value.tv_usec = (time % 1000) * 1000;
8248 setitimer(ITIMER_REAL, &delay, NULL);
8249 while (frameWaiting) pause();
8250 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8251 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8252 setitimer(ITIMER_REAL, &delay, NULL);
8262 XSync(xDisplay, False);
8264 usleep(time * 1000);
8275 /* Convert board position to corner of screen rect and color */
8278 ScreenSquare(column, row, pt, color)
8279 int column; int row; XPoint * pt; int * color;
8282 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8283 pt->y = lineGap + row * (squareSize + lineGap);
8285 pt->x = lineGap + column * (squareSize + lineGap);
8286 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8288 *color = SquareColor(row, column);
8291 /* Convert window coords to square */
8294 BoardSquare(x, y, column, row)
8295 int x; int y; int * column; int * row;
8297 *column = EventToSquare(x, BOARD_WIDTH);
8298 if (flipView && *column >= 0)
8299 *column = BOARD_WIDTH - 1 - *column;
8300 *row = EventToSquare(y, BOARD_HEIGHT);
8301 if (!flipView && *row >= 0)
8302 *row = BOARD_HEIGHT - 1 - *row;
8307 #undef Max /* just in case */
8309 #define Max(a, b) ((a) > (b) ? (a) : (b))
8310 #define Min(a, b) ((a) < (b) ? (a) : (b))
8313 SetRect(rect, x, y, width, height)
8314 XRectangle * rect; int x; int y; int width; int height;
8318 rect->width = width;
8319 rect->height = height;
8322 /* Test if two frames overlap. If they do, return
8323 intersection rect within old and location of
8324 that rect within new. */
8327 Intersect(old, new, size, area, pt)
8328 XPoint * old; XPoint * new;
8329 int size; XRectangle * area; XPoint * pt;
8331 if (old->x > new->x + size || new->x > old->x + size ||
8332 old->y > new->y + size || new->y > old->y + size) {
8335 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8336 size - abs(old->x - new->x), size - abs(old->y - new->y));
8337 pt->x = Max(old->x - new->x, 0);
8338 pt->y = Max(old->y - new->y, 0);
8343 /* For two overlapping frames, return the rect(s)
8344 in the old that do not intersect with the new. */
8347 CalcUpdateRects(old, new, size, update, nUpdates)
8348 XPoint * old; XPoint * new; int size;
8349 XRectangle update[]; int * nUpdates;
8353 /* If old = new (shouldn't happen) then nothing to draw */
8354 if (old->x == new->x && old->y == new->y) {
8358 /* Work out what bits overlap. Since we know the rects
8359 are the same size we don't need a full intersect calc. */
8361 /* Top or bottom edge? */
8362 if (new->y > old->y) {
8363 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8365 } else if (old->y > new->y) {
8366 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8367 size, old->y - new->y);
8370 /* Left or right edge - don't overlap any update calculated above. */
8371 if (new->x > old->x) {
8372 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8373 new->x - old->x, size - abs(new->y - old->y));
8375 } else if (old->x > new->x) {
8376 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8377 old->x - new->x, size - abs(new->y - old->y));
8384 /* Generate a series of frame coords from start->mid->finish.
8385 The movement rate doubles until the half way point is
8386 reached, then halves back down to the final destination,
8387 which gives a nice slow in/out effect. The algorithmn
8388 may seem to generate too many intermediates for short
8389 moves, but remember that the purpose is to attract the
8390 viewers attention to the piece about to be moved and
8391 then to where it ends up. Too few frames would be less
8395 Tween(start, mid, finish, factor, frames, nFrames)
8396 XPoint * start; XPoint * mid;
8397 XPoint * finish; int factor;
8398 XPoint frames[]; int * nFrames;
8400 int fraction, n, count;
8404 /* Slow in, stepping 1/16th, then 1/8th, ... */
8406 for (n = 0; n < factor; n++)
8408 for (n = 0; n < factor; n++) {
8409 frames[count].x = start->x + (mid->x - start->x) / fraction;
8410 frames[count].y = start->y + (mid->y - start->y) / fraction;
8412 fraction = fraction / 2;
8416 frames[count] = *mid;
8419 /* Slow out, stepping 1/2, then 1/4, ... */
8421 for (n = 0; n < factor; n++) {
8422 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8423 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8425 fraction = fraction * 2;
8430 /* Draw a piece on the screen without disturbing what's there */
8433 SelectGCMask(piece, clip, outline, mask)
8434 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8438 /* Bitmap for piece being moved. */
8439 if (appData.monoMode) {
8440 *mask = *pieceToSolid(piece);
8441 } else if (useImages) {
8443 *mask = xpmMask[piece];
8445 *mask = ximMaskPm[piece];
8448 *mask = *pieceToSolid(piece);
8451 /* GC for piece being moved. Square color doesn't matter, but
8452 since it gets modified we make a copy of the original. */
8454 if (appData.monoMode)
8459 if (appData.monoMode)
8464 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8466 /* Outline only used in mono mode and is not modified */
8468 *outline = bwPieceGC;
8470 *outline = wbPieceGC;
8474 OverlayPiece(piece, clip, outline, dest)
8475 ChessSquare piece; GC clip; GC outline; Drawable dest;
8480 /* Draw solid rectangle which will be clipped to shape of piece */
8481 XFillRectangle(xDisplay, dest, clip,
8482 0, 0, squareSize, squareSize);
8483 if (appData.monoMode)
8484 /* Also draw outline in contrasting color for black
8485 on black / white on white cases */
8486 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8487 0, 0, squareSize, squareSize, 0, 0, 1);
8489 /* Copy the piece */
8494 if(appData.upsideDown && flipView) kind ^= 2;
8495 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8497 0, 0, squareSize, squareSize,
8502 /* Animate the movement of a single piece */
8505 BeginAnimation(anim, piece, startColor, start)
8513 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8514 /* The old buffer is initialised with the start square (empty) */
8515 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8516 anim->prevFrame = *start;
8518 /* The piece will be drawn using its own bitmap as a matte */
8519 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8520 XSetClipMask(xDisplay, anim->pieceGC, mask);
8524 AnimationFrame(anim, frame, piece)
8529 XRectangle updates[4];
8534 /* Save what we are about to draw into the new buffer */
8535 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8536 frame->x, frame->y, squareSize, squareSize,
8539 /* Erase bits of the previous frame */
8540 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8541 /* Where the new frame overlapped the previous,
8542 the contents in newBuf are wrong. */
8543 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8544 overlap.x, overlap.y,
8545 overlap.width, overlap.height,
8547 /* Repaint the areas in the old that don't overlap new */
8548 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8549 for (i = 0; i < count; i++)
8550 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8551 updates[i].x - anim->prevFrame.x,
8552 updates[i].y - anim->prevFrame.y,
8553 updates[i].width, updates[i].height,
8554 updates[i].x, updates[i].y);
8556 /* Easy when no overlap */
8557 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8558 0, 0, squareSize, squareSize,
8559 anim->prevFrame.x, anim->prevFrame.y);
8562 /* Save this frame for next time round */
8563 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8564 0, 0, squareSize, squareSize,
8566 anim->prevFrame = *frame;
8568 /* Draw piece over original screen contents, not current,
8569 and copy entire rect. Wipes out overlapping piece images. */
8570 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8571 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8572 0, 0, squareSize, squareSize,
8573 frame->x, frame->y);
8577 EndAnimation (anim, finish)
8581 XRectangle updates[4];
8586 /* The main code will redraw the final square, so we
8587 only need to erase the bits that don't overlap. */
8588 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8589 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8590 for (i = 0; i < count; i++)
8591 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8592 updates[i].x - anim->prevFrame.x,
8593 updates[i].y - anim->prevFrame.y,
8594 updates[i].width, updates[i].height,
8595 updates[i].x, updates[i].y);
8597 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8598 0, 0, squareSize, squareSize,
8599 anim->prevFrame.x, anim->prevFrame.y);
8604 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8606 ChessSquare piece; int startColor;
8607 XPoint * start; XPoint * finish;
8608 XPoint frames[]; int nFrames;
8612 BeginAnimation(anim, piece, startColor, start);
8613 for (n = 0; n < nFrames; n++) {
8614 AnimationFrame(anim, &(frames[n]), piece);
8615 FrameDelay(appData.animSpeed);
8617 EndAnimation(anim, finish);
8621 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8624 ChessSquare piece = board[fromY][toY];
8625 board[fromY][toY] = EmptySquare;
8626 DrawPosition(FALSE, board);
8628 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8629 y = lineGap + toY * (squareSize + lineGap);
8631 x = lineGap + toX * (squareSize + lineGap);
8632 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8634 for(i=1; i<4*kFactor; i++) {
8635 int r = squareSize * 9 * i/(20*kFactor - 5);
8636 XFillArc(xDisplay, xBoardWindow, highlineGC,
8637 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8638 FrameDelay(appData.animSpeed);
8640 board[fromY][toY] = piece;
8643 /* Main control logic for deciding what to animate and how */
8646 AnimateMove(board, fromX, fromY, toX, toY)
8655 XPoint start, finish, mid;
8656 XPoint frames[kFactor * 2 + 1];
8657 int nFrames, startColor, endColor;
8659 /* Are we animating? */
8660 if (!appData.animate || appData.blindfold)
8663 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8664 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8665 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8667 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8668 piece = board[fromY][fromX];
8669 if (piece >= EmptySquare) return;
8674 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8677 if (appData.debugMode) {
8678 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8679 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8680 piece, fromX, fromY, toX, toY); }
8682 ScreenSquare(fromX, fromY, &start, &startColor);
8683 ScreenSquare(toX, toY, &finish, &endColor);
8686 /* Knight: make straight movement then diagonal */
8687 if (abs(toY - fromY) < abs(toX - fromX)) {
8688 mid.x = start.x + (finish.x - start.x) / 2;
8692 mid.y = start.y + (finish.y - start.y) / 2;
8695 mid.x = start.x + (finish.x - start.x) / 2;
8696 mid.y = start.y + (finish.y - start.y) / 2;
8699 /* Don't use as many frames for very short moves */
8700 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8701 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8703 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8704 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8705 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8707 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8708 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8711 /* Be sure end square is redrawn */
8712 damage[0][toY][toX] = True;
8716 DragPieceBegin(x, y, instantly)
8717 int x; int y; Boolean instantly;
8719 int boardX, boardY, color;
8722 /* Are we animating? */
8723 if (!appData.animateDragging || appData.blindfold)
8726 /* Figure out which square we start in and the
8727 mouse position relative to top left corner. */
8728 BoardSquare(x, y, &boardX, &boardY);
8729 player.startBoardX = boardX;
8730 player.startBoardY = boardY;
8731 ScreenSquare(boardX, boardY, &corner, &color);
8732 player.startSquare = corner;
8733 player.startColor = color;
8734 /* As soon as we start dragging, the piece will jump slightly to
8735 be centered over the mouse pointer. */
8736 player.mouseDelta.x = squareSize/2;
8737 player.mouseDelta.y = squareSize/2;
8738 /* Initialise animation */
8739 player.dragPiece = PieceForSquare(boardX, boardY);
8741 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8742 player.dragActive = True;
8743 BeginAnimation(&player, player.dragPiece, color, &corner);
8744 /* Mark this square as needing to be redrawn. Note that
8745 we don't remove the piece though, since logically (ie
8746 as seen by opponent) the move hasn't been made yet. */
8747 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8748 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8749 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8750 corner.x, corner.y, squareSize, squareSize,
8751 0, 0); // [HGM] zh: unstack in stead of grab
8752 if(gatingPiece != EmptySquare) {
8753 /* Kludge alert: When gating we want the introduced
8754 piece to appear on the from square. To generate an
8755 image of it, we draw it on the board, copy the image,
8756 and draw the original piece again. */
8757 ChessSquare piece = boards[currentMove][boardY][boardX];
8758 DrawSquare(boardY, boardX, gatingPiece, 0);
8759 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8760 corner.x, corner.y, squareSize, squareSize, 0, 0);
8761 DrawSquare(boardY, boardX, piece, 0);
8763 damage[0][boardY][boardX] = True;
8765 player.dragActive = False;
8770 ChangeDragPiece(ChessSquare piece)
8773 player.dragPiece = piece;
8774 /* The piece will be drawn using its own bitmap as a matte */
8775 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8776 XSetClipMask(xDisplay, player.pieceGC, mask);
8785 /* Are we animating? */
8786 if (!appData.animateDragging || appData.blindfold)
8790 if (! player.dragActive)
8792 /* Move piece, maintaining same relative position
8793 of mouse within square */
8794 corner.x = x - player.mouseDelta.x;
8795 corner.y = y - player.mouseDelta.y;
8796 AnimationFrame(&player, &corner, player.dragPiece);
8798 if (appData.highlightDragging) {
8800 BoardSquare(x, y, &boardX, &boardY);
8801 SetHighlights(fromX, fromY, boardX, boardY);
8810 int boardX, boardY, color;
8813 /* Are we animating? */
8814 if (!appData.animateDragging || appData.blindfold)
8818 if (! player.dragActive)
8820 /* Last frame in sequence is square piece is
8821 placed on, which may not match mouse exactly. */
8822 BoardSquare(x, y, &boardX, &boardY);
8823 ScreenSquare(boardX, boardY, &corner, &color);
8824 EndAnimation(&player, &corner);
8826 /* Be sure end square is redrawn */
8827 damage[0][boardY][boardX] = True;
8829 /* This prevents weird things happening with fast successive
8830 clicks which on my Sun at least can cause motion events
8831 without corresponding press/release. */
8832 player.dragActive = False;
8835 /* Handle expose event while piece being dragged */
8840 if (!player.dragActive || appData.blindfold)
8843 /* What we're doing: logically, the move hasn't been made yet,
8844 so the piece is still in it's original square. But visually
8845 it's being dragged around the board. So we erase the square
8846 that the piece is on and draw it at the last known drag point. */
8847 BlankSquare(player.startSquare.x, player.startSquare.y,
8848 player.startColor, EmptySquare, xBoardWindow, 1);
8849 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8850 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8853 #include <sys/ioctl.h>
8854 int get_term_width()
8856 int fd, default_width;
8859 default_width = 79; // this is FICS default anyway...
8861 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8863 if (!ioctl(fd, TIOCGSIZE, &win))
8864 default_width = win.ts_cols;
8865 #elif defined(TIOCGWINSZ)
8867 if (!ioctl(fd, TIOCGWINSZ, &win))
8868 default_width = win.ws_col;
8870 return default_width;
8876 static int old_width = 0;
8877 int new_width = get_term_width();
8879 if (old_width != new_width)
8880 ics_printf("set width %d\n", new_width);
8881 old_width = new_width;
8884 void NotifyFrontendLogin()
8889 /* [AS] Arrow highlighting support */
8891 static double A_WIDTH = 5; /* Width of arrow body */
8893 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8894 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8896 static double Sqr( double x )
8901 static int Round( double x )
8903 return (int) (x + 0.5);
8906 void SquareToPos(int rank, int file, int *x, int *y)
8909 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8910 *y = lineGap + rank * (squareSize + lineGap);
8912 *x = lineGap + file * (squareSize + lineGap);
8913 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8917 /* Draw an arrow between two points using current settings */
8918 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8921 double dx, dy, j, k, x, y;
8924 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8926 arrow[0].x = s_x + A_WIDTH + 0.5;
8929 arrow[1].x = s_x + A_WIDTH + 0.5;
8930 arrow[1].y = d_y - h;
8932 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8933 arrow[2].y = d_y - h;
8938 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8939 arrow[5].y = d_y - h;
8941 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8942 arrow[4].y = d_y - h;
8944 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8947 else if( d_y == s_y ) {
8948 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8951 arrow[0].y = s_y + A_WIDTH + 0.5;
8953 arrow[1].x = d_x - w;
8954 arrow[1].y = s_y + A_WIDTH + 0.5;
8956 arrow[2].x = d_x - w;
8957 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8962 arrow[5].x = d_x - w;
8963 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8965 arrow[4].x = d_x - w;
8966 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8969 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8972 /* [AS] Needed a lot of paper for this! :-) */
8973 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8974 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8976 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8978 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8983 arrow[0].x = Round(x - j);
8984 arrow[0].y = Round(y + j*dx);
8986 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8987 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8990 x = (double) d_x - k;
8991 y = (double) d_y - k*dy;
8994 x = (double) d_x + k;
8995 y = (double) d_y + k*dy;
8998 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
9000 arrow[6].x = Round(x - j);
9001 arrow[6].y = Round(y + j*dx);
9003 arrow[2].x = Round(arrow[6].x + 2*j);
9004 arrow[2].y = Round(arrow[6].y - 2*j*dx);
9006 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
9007 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
9012 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9013 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9016 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9017 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
9018 // Polygon( hdc, arrow, 7 );
9021 /* [AS] Draw an arrow between two squares */
9022 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9024 int s_x, s_y, d_x, d_y, hor, vert, i;
9026 if( s_col == d_col && s_row == d_row ) {
9030 /* Get source and destination points */
9031 SquareToPos( s_row, s_col, &s_x, &s_y);
9032 SquareToPos( d_row, d_col, &d_x, &d_y);
9035 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9037 else if( d_y < s_y ) {
9038 d_y += squareSize / 2 + squareSize / 4;
9041 d_y += squareSize / 2;
9045 d_x += squareSize / 2 - squareSize / 4;
9047 else if( d_x < s_x ) {
9048 d_x += squareSize / 2 + squareSize / 4;
9051 d_x += squareSize / 2;
9054 s_x += squareSize / 2;
9055 s_y += squareSize / 2;
9058 A_WIDTH = squareSize / 14.; //[HGM] make float
9060 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9062 hor = 64*s_col + 32; vert = 64*s_row + 32;
9063 for(i=0; i<= 64; i++) {
9064 damage[0][vert+6>>6][hor+6>>6] = True;
9065 damage[0][vert-6>>6][hor+6>>6] = True;
9066 damage[0][vert+6>>6][hor-6>>6] = True;
9067 damage[0][vert-6>>6][hor-6>>6] = True;
9068 hor += d_col - s_col; vert += d_row - s_row;
9072 Boolean IsDrawArrowEnabled()
9074 return appData.highlightMoveWithArrow && squareSize >= 32;
9077 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9079 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9080 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9083 void UpdateLogos(int displ)
9085 return; // no logos in XBoard yet