2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
243 int main P((int argc, char **argv));
244 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
245 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
246 RETSIGTYPE CmailSigHandler P((int sig));
247 RETSIGTYPE IntSigHandler P((int sig));
248 RETSIGTYPE TermSizeSigHandler P((int sig));
249 void CreateGCs P((int redo));
250 void CreateAnyPieces P((void));
251 void CreateXIMPieces P((void));
252 void CreateXPMPieces P((void));
253 void CreateXPMBoard P((char *s, int n));
254 void CreatePieces P((void));
255 void CreatePieceMenus P((void));
256 Widget CreateMenuBar P((Menu *mb));
257 Widget CreateButtonBar P ((MenuItem *mi));
259 char *InsertPxlSize P((char *pattern, int targetPxlSize));
260 XFontSet CreateFontSet P((char *base_fnt_lst));
262 char *FindFont P((char *pattern, int targetPxlSize));
264 void PieceMenuPopup P((Widget w, XEvent *event,
265 String *params, Cardinal *num_params));
266 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
267 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
268 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
269 u_int wreq, u_int hreq));
270 void CreateGrid P((void));
271 int EventToSquare P((int x, int limit));
272 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
273 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
274 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
275 void HandleUserMove P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AnimateUserMove P((Widget w, XEvent * event,
278 String * params, Cardinal * nParams));
279 void HandlePV P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void SelectPV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void StopPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void WhiteClock P((Widget w, XEvent *event,
286 String *prms, Cardinal *nprms));
287 void BlackClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void DrawPositionProc P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
293 void CommentClick P((Widget w, XEvent * event,
294 String * params, Cardinal * nParams));
295 void CommentPopUp P((char *title, char *label));
296 void CommentPopDown P((void));
297 void ICSInputBoxPopUp P((void));
298 void ICSInputBoxPopDown P((void));
299 void FileNamePopUp P((char *label, char *def, char *filter,
300 FileProc proc, char *openMode));
301 void FileNamePopDown P((void));
302 void FileNameCallback P((Widget w, XtPointer client_data,
303 XtPointer call_data));
304 void FileNameAction P((Widget w, XEvent *event,
305 String *prms, Cardinal *nprms));
306 void AskQuestionReplyAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionProc P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionPopDown P((void));
311 void PromotionPopDown P((void));
312 void PromotionCallback P((Widget w, XtPointer client_data,
313 XtPointer call_data));
314 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
315 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
316 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
317 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
319 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
321 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
323 void LoadPositionProc P((Widget w, XEvent *event,
324 String *prms, Cardinal *nprms));
325 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
327 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
329 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
331 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
333 void PastePositionProc P((Widget w, XEvent *event, String *prms,
335 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
337 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void SavePositionProc P((Widget w, XEvent *event,
340 String *prms, Cardinal *nprms));
341 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
344 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
348 void MachineWhiteProc P((Widget w, XEvent *event,
349 String *prms, Cardinal *nprms));
350 void AnalyzeModeProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeFileProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
356 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void IcsClientProc P((Widget w, XEvent *event, String *prms,
360 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void EditPositionProc P((Widget w, XEvent *event,
362 String *prms, Cardinal *nprms));
363 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void EditCommentProc P((Widget w, XEvent *event,
365 String *prms, Cardinal *nprms));
366 void IcsInputBoxProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void StopObservingProc P((Widget w, XEvent *event, String *prms,
385 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
387 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
388 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
389 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 Boolean TempBackwardActive = False;
393 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
395 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
396 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
399 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
401 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
402 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
404 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
406 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
408 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
409 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
413 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
414 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
416 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
418 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
420 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
421 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
422 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
425 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
427 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
429 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
431 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
434 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
436 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
438 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
440 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void DisplayMove P((int moveNumber));
451 void DisplayTitle P((char *title));
452 void ICSInitScript P((void));
453 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
454 void ErrorPopUp P((char *title, char *text, int modal));
455 void ErrorPopDown P((void));
456 static char *ExpandPathName P((char *path));
457 static void CreateAnimVars P((void));
458 static void DragPieceMove P((int x, int y));
459 static void DrawDragPiece P((void));
460 char *ModeToWidgetName P((GameMode mode));
461 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
477 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
478 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
479 void GameListOptionsPopDown P(());
480 void GenericPopDown P(());
481 void update_ics_width P(());
482 int get_term_width P(());
483 int CopyMemoProc P(());
484 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
485 Boolean IsDrawArrowEnabled P(());
488 * XBoard depends on Xt R4 or higher
490 int xtVersion = XtSpecificationRelease;
495 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
496 jailSquareColor, highlightSquareColor, premoveHighlightColor;
497 Pixel lowTimeWarningColor;
498 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
499 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
500 wjPieceGC, bjPieceGC, prelineGC, countGC;
501 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
502 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
503 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
504 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
505 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
506 ICSInputShell, fileNameShell, askQuestionShell;
507 Widget historyShell, evalGraphShell, gameListShell;
508 int hOffset; // [HGM] dual
509 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
510 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
511 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
513 XFontSet fontSet, clockFontSet;
516 XFontStruct *clockFontStruct;
518 Font coordFontID, countFontID;
519 XFontStruct *coordFontStruct, *countFontStruct;
520 XtAppContext appContext;
522 char *oldICSInteractionTitle;
526 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
528 Position commentX = -1, commentY = -1;
529 Dimension commentW, commentH;
530 typedef unsigned int BoardSize;
532 Boolean chessProgram;
534 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
535 int squareSize, smallLayout = 0, tinyLayout = 0,
536 marginW, marginH, // [HGM] for run-time resizing
537 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
538 ICSInputBoxUp = False, askQuestionUp = False,
539 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
540 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
541 Pixel timerForegroundPixel, timerBackgroundPixel;
542 Pixel buttonForegroundPixel, buttonBackgroundPixel;
543 char *chessDir, *programName, *programVersion,
544 *gameCopyFilename, *gamePasteFilename;
545 Boolean alwaysOnTop = False;
546 Boolean saveSettingsOnExit;
547 char *settingsFileName;
548 char *icsTextMenuString;
550 char *firstChessProgramNames;
551 char *secondChessProgramNames;
553 WindowPlacement wpMain;
554 WindowPlacement wpConsole;
555 WindowPlacement wpComment;
556 WindowPlacement wpMoveHistory;
557 WindowPlacement wpEvalGraph;
558 WindowPlacement wpEngineOutput;
559 WindowPlacement wpGameList;
560 WindowPlacement wpTags;
562 extern Widget shells[];
563 extern Boolean shellUp[];
567 Pixmap pieceBitmap[2][(int)BlackPawn];
568 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
569 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
570 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
571 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
572 Pixmap xpmBoardBitmap[2];
573 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
574 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
575 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
576 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
577 XImage *ximLightSquare, *ximDarkSquare;
580 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
581 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
583 #define White(piece) ((int)(piece) < (int)BlackPawn)
585 /* Variables for doing smooth animation. This whole thing
586 would be much easier if the board was double-buffered,
587 but that would require a fairly major rewrite. */
592 GC blitGC, pieceGC, outlineGC;
593 XPoint startSquare, prevFrame, mouseDelta;
597 int startBoardX, startBoardY;
600 /* There can be two pieces being animated at once: a player
601 can begin dragging a piece before the remote opponent has moved. */
603 static AnimState game, player;
605 /* Bitmaps for use as masks when drawing XPM pieces.
606 Need one for each black and white piece. */
607 static Pixmap xpmMask[BlackKing + 1];
609 /* This magic number is the number of intermediate frames used
610 in each half of the animation. For short moves it's reduced
611 by 1. The total number of frames will be factor * 2 + 1. */
614 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
616 MenuItem fileMenu[] = {
617 {N_("New Game Ctrl+N"), "New Game", ResetProc},
618 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
619 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
620 {"----", NULL, NothingProc},
621 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
622 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
623 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
624 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
625 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
626 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
627 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
628 {"----", NULL, NothingProc},
629 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
630 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
631 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
632 {"----", NULL, NothingProc},
633 {N_("Mail Move"), "Mail Move", MailMoveProc},
634 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
635 {"----", NULL, NothingProc},
636 {N_("Quit Ctr+Q"), "Exit", QuitProc},
640 MenuItem editMenu[] = {
641 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
642 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
643 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
644 {"----", NULL, NothingProc},
645 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
646 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
647 {"----", NULL, NothingProc},
648 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
649 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
650 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
651 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
652 {N_("Edit Book"), "Edit Book", EditBookProc},
653 {"----", NULL, NothingProc},
654 {N_("Revert Home"), "Revert", RevertProc},
655 {N_("Annotate"), "Annotate", AnnotateProc},
656 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
657 {"----", NULL, NothingProc},
658 {N_("Backward Alt+Left"), "Backward", BackwardProc},
659 {N_("Forward Alt+Right"), "Forward", ForwardProc},
660 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
661 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
665 MenuItem viewMenu[] = {
666 {N_("Flip View F2"), "Flip View", FlipViewProc},
667 {"----", NULL, NothingProc},
668 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
669 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
670 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
671 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
672 {N_("ICS text menu"), "ICStex", IcsTextProc},
673 {"----", NULL, NothingProc},
674 {N_("Tags"), "Show Tags", EditTagsProc},
675 {N_("Comments"), "Show Comments", EditCommentProc},
676 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
677 {"----", NULL, NothingProc},
678 {N_("Board..."), "Board Options", BoardOptionsProc},
679 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
683 MenuItem modeMenu[] = {
684 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
685 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
686 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
687 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
688 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
689 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
690 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
691 {N_("Training"), "Training", TrainingProc},
692 {N_("ICS Client"), "ICS Client", IcsClientProc},
693 {"----", NULL, NothingProc},
694 {N_("Machine Match"), "Machine Match", MatchProc},
695 {N_("Pause Pause"), "Pause", PauseProc},
699 MenuItem actionMenu[] = {
700 {N_("Accept F3"), "Accept", AcceptProc},
701 {N_("Decline F4"), "Decline", DeclineProc},
702 {N_("Rematch F12"), "Rematch", RematchProc},
703 {"----", NULL, NothingProc},
704 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
705 {N_("Draw F6"), "Draw", DrawProc},
706 {N_("Adjourn F7"), "Adjourn", AdjournProc},
707 {N_("Abort F8"),"Abort", AbortProc},
708 {N_("Resign F9"), "Resign", ResignProc},
709 {"----", NULL, NothingProc},
710 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
711 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
712 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
713 {"----", NULL, NothingProc},
714 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
715 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
716 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
720 MenuItem engineMenu[] = {
721 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
722 {"----", NULL, NothingProc},
723 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
724 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
725 {"----", NULL, NothingProc},
726 {N_("Hint"), "Hint", HintProc},
727 {N_("Book"), "Book", BookProc},
728 {"----", NULL, NothingProc},
729 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
730 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
734 MenuItem optionsMenu[] = {
735 #define OPTIONSDIALOG
737 {N_("General ..."), "General", OptionsProc},
739 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
740 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
741 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
742 {N_("ICS ..."), "ICS", IcsOptionsProc},
743 {N_("Match ..."), "Match", MatchOptionsProc},
744 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
745 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
746 // {N_(" ..."), "", OptionsProc},
747 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
748 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
749 {"----", NULL, NothingProc},
750 #ifndef OPTIONSDIALOG
751 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
752 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
753 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
754 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
755 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
756 {N_("Blindfold"), "Blindfold", BlindfoldProc},
757 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
759 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
761 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
762 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
763 {N_("Move Sound"), "Move Sound", MoveSoundProc},
764 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
765 {N_("One-Click Moving"), "OneClick", OneClickProc},
766 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
767 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
768 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
769 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
770 // {N_("Premove"), "Premove", PremoveProc},
771 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
772 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
773 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
774 {"----", NULL, NothingProc},
776 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
777 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
781 MenuItem helpMenu[] = {
782 {N_("Info XBoard"), "Info XBoard", InfoProc},
783 {N_("Man XBoard F1"), "Man XBoard", ManProc},
784 {"----", NULL, NothingProc},
785 {N_("About XBoard"), "About XBoard", AboutProc},
790 {N_("File"), "File", fileMenu},
791 {N_("Edit"), "Edit", editMenu},
792 {N_("View"), "View", viewMenu},
793 {N_("Mode"), "Mode", modeMenu},
794 {N_("Action"), "Action", actionMenu},
795 {N_("Engine"), "Engine", engineMenu},
796 {N_("Options"), "Options", optionsMenu},
797 {N_("Help"), "Help", helpMenu},
801 #define PAUSE_BUTTON "P"
802 MenuItem buttonBar[] = {
803 {"<<", "<<", ToStartProc},
804 {"<", "<", BackwardProc},
805 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
806 {">", ">", ForwardProc},
807 {">>", ">>", ToEndProc},
811 #define PIECE_MENU_SIZE 18
812 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
813 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
814 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
815 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
816 N_("Empty square"), N_("Clear board") },
817 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
818 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
819 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
820 N_("Empty square"), N_("Clear board") }
822 /* must be in same order as pieceMenuStrings! */
823 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
824 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
825 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
826 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
827 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
828 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
829 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
830 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
831 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
834 #define DROP_MENU_SIZE 6
835 String dropMenuStrings[DROP_MENU_SIZE] = {
836 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
838 /* must be in same order as dropMenuStrings! */
839 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
840 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
841 WhiteRook, WhiteQueen
849 DropMenuEnables dmEnables[] = {
867 { XtNborderWidth, 0 },
868 { XtNdefaultDistance, 0 },
872 { XtNborderWidth, 0 },
873 { XtNresizable, (XtArgVal) True },
877 { XtNborderWidth, 0 },
883 { XtNjustify, (XtArgVal) XtJustifyRight },
884 { XtNlabel, (XtArgVal) "..." },
885 { XtNresizable, (XtArgVal) True },
886 { XtNresize, (XtArgVal) False }
889 Arg messageArgs[] = {
890 { XtNjustify, (XtArgVal) XtJustifyLeft },
891 { XtNlabel, (XtArgVal) "..." },
892 { XtNresizable, (XtArgVal) True },
893 { XtNresize, (XtArgVal) False }
897 { XtNborderWidth, 0 },
898 { XtNjustify, (XtArgVal) XtJustifyLeft }
901 XtResource clientResources[] = {
902 { "flashCount", "flashCount", XtRInt, sizeof(int),
903 XtOffset(AppDataPtr, flashCount), XtRImmediate,
904 (XtPointer) FLASH_COUNT },
907 XrmOptionDescRec shellOptions[] = {
908 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
909 { "-flash", "flashCount", XrmoptionNoArg, "3" },
910 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
913 XtActionsRec boardActions[] = {
914 { "DrawPosition", DrawPositionProc },
915 { "HandleUserMove", HandleUserMove },
916 { "AnimateUserMove", AnimateUserMove },
917 { "HandlePV", HandlePV },
918 { "SelectPV", SelectPV },
919 { "StopPV", StopPV },
920 { "FileNameAction", FileNameAction },
921 { "AskQuestionProc", AskQuestionProc },
922 { "AskQuestionReplyAction", AskQuestionReplyAction },
923 { "PieceMenuPopup", PieceMenuPopup },
924 { "WhiteClock", WhiteClock },
925 { "BlackClock", BlackClock },
926 { "ResetProc", ResetProc },
927 { "NewVariantProc", NewVariantProc },
928 { "LoadGameProc", LoadGameProc },
929 { "LoadNextGameProc", LoadNextGameProc },
930 { "LoadPrevGameProc", LoadPrevGameProc },
931 { "LoadSelectedProc", LoadSelectedProc },
932 { "SetFilterProc", SetFilterProc },
933 { "ReloadGameProc", ReloadGameProc },
934 { "LoadPositionProc", LoadPositionProc },
935 { "LoadNextPositionProc", LoadNextPositionProc },
936 { "LoadPrevPositionProc", LoadPrevPositionProc },
937 { "ReloadPositionProc", ReloadPositionProc },
938 { "CopyPositionProc", CopyPositionProc },
939 { "PastePositionProc", PastePositionProc },
940 { "CopyGameProc", CopyGameProc },
941 { "CopyGameListProc", CopyGameListProc },
942 { "PasteGameProc", PasteGameProc },
943 { "SaveGameProc", SaveGameProc },
944 { "SavePositionProc", SavePositionProc },
945 { "MailMoveProc", MailMoveProc },
946 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
947 { "QuitProc", QuitProc },
948 { "MachineWhiteProc", MachineWhiteProc },
949 { "MachineBlackProc", MachineBlackProc },
950 { "AnalysisModeProc", AnalyzeModeProc },
951 { "AnalyzeFileProc", AnalyzeFileProc },
952 { "TwoMachinesProc", TwoMachinesProc },
953 { "IcsClientProc", IcsClientProc },
954 { "EditGameProc", EditGameProc },
955 { "EditPositionProc", EditPositionProc },
956 { "TrainingProc", EditPositionProc },
957 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
958 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
959 { "ShowGameListProc", ShowGameListProc },
960 { "ShowMoveListProc", HistoryShowProc},
961 { "EditTagsProc", EditCommentProc },
962 { "EditBookProc", EditBookProc },
963 { "EditCommentProc", EditCommentProc },
964 { "IcsInputBoxProc", IcsInputBoxProc },
965 { "PauseProc", PauseProc },
966 { "AcceptProc", AcceptProc },
967 { "DeclineProc", DeclineProc },
968 { "RematchProc", RematchProc },
969 { "CallFlagProc", CallFlagProc },
970 { "DrawProc", DrawProc },
971 { "AdjournProc", AdjournProc },
972 { "AbortProc", AbortProc },
973 { "ResignProc", ResignProc },
974 { "AdjuWhiteProc", AdjuWhiteProc },
975 { "AdjuBlackProc", AdjuBlackProc },
976 { "AdjuDrawProc", AdjuDrawProc },
977 { "TypeInProc", TypeInProc },
978 { "EnterKeyProc", EnterKeyProc },
979 { "UpKeyProc", UpKeyProc },
980 { "DownKeyProc", DownKeyProc },
981 { "StopObservingProc", StopObservingProc },
982 { "StopExaminingProc", StopExaminingProc },
983 { "UploadProc", UploadProc },
984 { "BackwardProc", BackwardProc },
985 { "ForwardProc", ForwardProc },
986 { "TempBackwardProc", TempBackwardProc },
987 { "TempForwardProc", TempForwardProc },
988 { "ToStartProc", ToStartProc },
989 { "ToEndProc", ToEndProc },
990 { "RevertProc", RevertProc },
991 { "AnnotateProc", AnnotateProc },
992 { "TruncateGameProc", TruncateGameProc },
993 { "MoveNowProc", MoveNowProc },
994 { "RetractMoveProc", RetractMoveProc },
995 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
996 { "UciMenuProc", (XtActionProc) UciMenuProc },
997 { "TimeControlProc", (XtActionProc) TimeControlProc },
998 { "FlipViewProc", FlipViewProc },
999 { "PonderNextMoveProc", PonderNextMoveProc },
1000 #ifndef OPTIONSDIALOG
1001 { "AlwaysQueenProc", AlwaysQueenProc },
1002 { "AnimateDraggingProc", AnimateDraggingProc },
1003 { "AnimateMovingProc", AnimateMovingProc },
1004 { "AutoflagProc", AutoflagProc },
1005 { "AutoflipProc", AutoflipProc },
1006 { "BlindfoldProc", BlindfoldProc },
1007 { "FlashMovesProc", FlashMovesProc },
1009 { "HighlightDraggingProc", HighlightDraggingProc },
1011 { "HighlightLastMoveProc", HighlightLastMoveProc },
1012 // { "IcsAlarmProc", IcsAlarmProc },
1013 { "MoveSoundProc", MoveSoundProc },
1014 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1015 { "PopupExitMessageProc", PopupExitMessageProc },
1016 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1017 // { "PremoveProc", PremoveProc },
1018 { "ShowCoordsProc", ShowCoordsProc },
1019 { "ShowThinkingProc", ShowThinkingProc },
1020 { "HideThinkingProc", HideThinkingProc },
1021 { "TestLegalityProc", TestLegalityProc },
1023 { "SaveSettingsProc", SaveSettingsProc },
1024 { "SaveOnExitProc", SaveOnExitProc },
1025 { "InfoProc", InfoProc },
1026 { "ManProc", ManProc },
1027 { "HintProc", HintProc },
1028 { "BookProc", BookProc },
1029 { "AboutGameProc", AboutGameProc },
1030 { "AboutProc", AboutProc },
1031 { "DebugProc", DebugProc },
1032 { "NothingProc", NothingProc },
1033 { "CommentClick", (XtActionProc) CommentClick },
1034 { "CommentPopDown", (XtActionProc) CommentPopDown },
1035 { "TagsPopDown", (XtActionProc) TagsPopDown },
1036 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1037 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1038 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1039 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1040 { "GameListPopDown", (XtActionProc) GameListPopDown },
1041 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1042 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1043 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1044 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1045 { "GenericPopDown", (XtActionProc) GenericPopDown },
1046 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1047 { "SelectMove", (XtActionProc) SelectMove },
1050 char globalTranslations[] =
1051 ":<Key>F9: ResignProc() \n \
1052 :Ctrl<Key>n: ResetProc() \n \
1053 :Meta<Key>V: NewVariantProc() \n \
1054 :Ctrl<Key>o: LoadGameProc() \n \
1055 :Meta<Key>Next: LoadNextGameProc() \n \
1056 :Meta<Key>Prior: LoadPrevGameProc() \n \
1057 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1058 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1059 :Ctrl<Key>s: SaveGameProc() \n \
1060 :Ctrl<Key>c: CopyGameProc() \n \
1061 :Ctrl<Key>v: PasteGameProc() \n \
1062 :Ctrl<Key>O: LoadPositionProc() \n \
1063 :Shift<Key>Next: LoadNextPositionProc() \n \
1064 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1065 :Ctrl<Key>S: SavePositionProc() \n \
1066 :Ctrl<Key>C: CopyPositionProc() \n \
1067 :Ctrl<Key>V: PastePositionProc() \n \
1068 :Ctrl<Key>q: QuitProc() \n \
1069 :Ctrl<Key>w: MachineWhiteProc() \n \
1070 :Ctrl<Key>b: MachineBlackProc() \n \
1071 :Ctrl<Key>t: TwoMachinesProc() \n \
1072 :Ctrl<Key>a: AnalysisModeProc() \n \
1073 :Ctrl<Key>g: AnalyzeFileProc() \n \
1074 :Ctrl<Key>e: EditGameProc() \n \
1075 :Ctrl<Key>E: EditPositionProc() \n \
1076 :Meta<Key>O: EngineOutputProc() \n \
1077 :Meta<Key>E: EvalGraphProc() \n \
1078 :Meta<Key>G: ShowGameListProc() \n \
1079 :Meta<Key>H: ShowMoveListProc() \n \
1080 :<Key>Pause: PauseProc() \n \
1081 :<Key>F3: AcceptProc() \n \
1082 :<Key>F4: DeclineProc() \n \
1083 :<Key>F12: RematchProc() \n \
1084 :<Key>F5: CallFlagProc() \n \
1085 :<Key>F6: DrawProc() \n \
1086 :<Key>F7: AdjournProc() \n \
1087 :<Key>F8: AbortProc() \n \
1088 :<Key>F10: StopObservingProc() \n \
1089 :<Key>F11: StopExaminingProc() \n \
1090 :Meta Ctrl<Key>F12: DebugProc() \n \
1091 :Meta<Key>End: ToEndProc() \n \
1092 :Meta<Key>Right: ForwardProc() \n \
1093 :Meta<Key>Home: ToStartProc() \n \
1094 :Meta<Key>Left: BackwardProc() \n \
1095 :<Key>Left: BackwardProc() \n \
1096 :<Key>Right: ForwardProc() \n \
1097 :<Key>Home: RevertProc() \n \
1098 :<Key>End: TruncateGameProc() \n \
1099 :Ctrl<Key>m: MoveNowProc() \n \
1100 :Ctrl<Key>x: RetractMoveProc() \n \
1101 :Meta<Key>J: EngineMenuProc() \n \
1102 :Meta<Key>U: UciMenuProc() \n \
1103 :Meta<Key>T: TimeControlProc() \n \
1104 :Ctrl<Key>P: PonderNextMoveProc() \n "
1105 #ifndef OPTIONSDIALOG
1107 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1108 :Ctrl<Key>F: AutoflagProc() \n \
1109 :Ctrl<Key>A: AnimateMovingProc() \n \
1110 :Ctrl<Key>L: TestLegalityProc() \n \
1111 :Ctrl<Key>H: HideThinkingProc() \n "
1114 :<Key>F1: ManProc() \n \
1115 :<Key>F2: FlipViewProc() \n \
1116 :Ctrl<KeyDown>.: TempBackwardProc() \n \
1117 :Ctrl<KeyUp>.: TempForwardProc() \n \
1118 :Ctrl<Key>1: AskQuestionProc(\"Direct command\",\
1119 \"Send to chess program:\",,1) \n \
1120 :Ctrl<Key>2: AskQuestionProc(\"Direct command\",\
1121 \"Send to second chess program:\",,2) \n";
1123 char boardTranslations[] =
1124 "<Btn1Down>: HandleUserMove(0) \n \
1125 Shift<Btn1Up>: HandleUserMove(1) \n \
1126 <Btn1Up>: HandleUserMove(0) \n \
1127 <Btn1Motion>: AnimateUserMove() \n \
1128 <Btn3Motion>: HandlePV() \n \
1129 <Btn3Up>: PieceMenuPopup(menuB) \n \
1130 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1131 PieceMenuPopup(menuB) \n \
1132 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1133 PieceMenuPopup(menuW) \n \
1134 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1135 PieceMenuPopup(menuW) \n \
1136 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1137 PieceMenuPopup(menuB) \n";
1139 char whiteTranslations[] =
1140 "Shift<BtnDown>: WhiteClock(1)\n \
1141 <BtnDown>: WhiteClock(0)\n";
1142 char blackTranslations[] =
1143 "Shift<BtnDown>: BlackClock(1)\n \
1144 <BtnDown>: BlackClock(0)\n";
1146 char ICSInputTranslations[] =
1147 "<Key>Up: UpKeyProc() \n "
1148 "<Key>Down: DownKeyProc() \n "
1149 "<Key>Return: EnterKeyProc() \n";
1151 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1152 // as the widget is destroyed before the up-click can call extend-end
1153 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1155 String xboardResources[] = {
1156 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1157 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1158 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1163 /* Max possible square size */
1164 #define MAXSQSIZE 256
1166 static int xpm_avail[MAXSQSIZE];
1168 #ifdef HAVE_DIR_STRUCT
1170 /* Extract piece size from filename */
1172 xpm_getsize(name, len, ext)
1183 if ((p=strchr(name, '.')) == NULL ||
1184 StrCaseCmp(p+1, ext) != 0)
1190 while (*p && isdigit(*p))
1197 /* Setup xpm_avail */
1199 xpm_getavail(dirname, ext)
1207 for (i=0; i<MAXSQSIZE; ++i)
1210 if (appData.debugMode)
1211 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1213 dir = opendir(dirname);
1216 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1217 programName, dirname);
1221 while ((ent=readdir(dir)) != NULL) {
1222 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1223 if (i > 0 && i < MAXSQSIZE)
1233 xpm_print_avail(fp, ext)
1239 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1240 for (i=1; i<MAXSQSIZE; ++i) {
1246 /* Return XPM piecesize closest to size */
1248 xpm_closest_to(dirname, size, ext)
1254 int sm_diff = MAXSQSIZE;
1258 xpm_getavail(dirname, ext);
1260 if (appData.debugMode)
1261 xpm_print_avail(stderr, ext);
1263 for (i=1; i<MAXSQSIZE; ++i) {
1266 diff = (diff<0) ? -diff : diff;
1267 if (diff < sm_diff) {
1275 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1281 #else /* !HAVE_DIR_STRUCT */
1282 /* If we are on a system without a DIR struct, we can't
1283 read the directory, so we can't collect a list of
1284 filenames, etc., so we can't do any size-fitting. */
1286 xpm_closest_to(dirname, size, ext)
1291 fprintf(stderr, _("\
1292 Warning: No DIR structure found on this system --\n\
1293 Unable to autosize for XPM/XIM pieces.\n\
1294 Please report this error to %s.\n\
1295 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1298 #endif /* HAVE_DIR_STRUCT */
1300 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1301 "magenta", "cyan", "white" };
1305 TextColors textColors[(int)NColorClasses];
1307 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1309 parse_color(str, which)
1313 char *p, buf[100], *d;
1316 if (strlen(str) > 99) /* watch bounds on buf */
1321 for (i=0; i<which; ++i) {
1328 /* Could be looking at something like:
1330 .. in which case we want to stop on a comma also */
1331 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1335 return -1; /* Use default for empty field */
1338 if (which == 2 || isdigit(*p))
1341 while (*p && isalpha(*p))
1346 for (i=0; i<8; ++i) {
1347 if (!StrCaseCmp(buf, cnames[i]))
1348 return which? (i+40) : (i+30);
1350 if (!StrCaseCmp(buf, "default")) return -1;
1352 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1357 parse_cpair(cc, str)
1361 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1362 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1367 /* bg and attr are optional */
1368 textColors[(int)cc].bg = parse_color(str, 1);
1369 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1370 textColors[(int)cc].attr = 0;
1376 /* Arrange to catch delete-window events */
1377 Atom wm_delete_window;
1379 CatchDeleteWindow(Widget w, String procname)
1382 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1383 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1384 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1391 XtSetArg(args[0], XtNiconic, False);
1392 XtSetValues(shellWidget, args, 1);
1394 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1397 //---------------------------------------------------------------------------------------------------------
1398 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1401 #define CW_USEDEFAULT (1<<31)
1402 #define ICS_TEXT_MENU_SIZE 90
1403 #define DEBUG_FILE "xboard.debug"
1404 #define SetCurrentDirectory chdir
1405 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1409 // these two must some day move to frontend.h, when they are implemented
1410 Boolean GameListIsUp();
1412 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1415 // front-end part of option handling
1417 // [HGM] This platform-dependent table provides the location for storing the color info
1418 extern char *crWhite, * crBlack;
1422 &appData.whitePieceColor,
1423 &appData.blackPieceColor,
1424 &appData.lightSquareColor,
1425 &appData.darkSquareColor,
1426 &appData.highlightSquareColor,
1427 &appData.premoveHighlightColor,
1428 &appData.lowTimeWarningColor,
1439 // [HGM] font: keep a font for each square size, even non-stndard ones
1440 #define NUM_SIZES 18
1441 #define MAX_SIZE 130
1442 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1443 char *fontTable[NUM_FONTS][MAX_SIZE];
1446 ParseFont(char *name, int number)
1447 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1449 if(sscanf(name, "size%d:", &size)) {
1450 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1451 // defer processing it until we know if it matches our board size
1452 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1453 fontTable[number][size] = strdup(strchr(name, ':')+1);
1454 fontValid[number][size] = True;
1459 case 0: // CLOCK_FONT
1460 appData.clockFont = strdup(name);
1462 case 1: // MESSAGE_FONT
1463 appData.font = strdup(name);
1465 case 2: // COORD_FONT
1466 appData.coordFont = strdup(name);
1471 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1476 { // only 2 fonts currently
1477 appData.clockFont = CLOCK_FONT_NAME;
1478 appData.coordFont = COORD_FONT_NAME;
1479 appData.font = DEFAULT_FONT_NAME;
1484 { // no-op, until we identify the code for this already in XBoard and move it here
1488 ParseColor(int n, char *name)
1489 { // in XBoard, just copy the color-name string
1490 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1494 ParseTextAttribs(ColorClass cc, char *s)
1496 (&appData.colorShout)[cc] = strdup(s);
1500 ParseBoardSize(void *addr, char *name)
1502 appData.boardSize = strdup(name);
1507 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1511 SetCommPortDefaults()
1512 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1515 // [HGM] args: these three cases taken out to stay in front-end
1517 SaveFontArg(FILE *f, ArgDescriptor *ad)
1520 int i, n = (int)(intptr_t)ad->argLoc;
1522 case 0: // CLOCK_FONT
1523 name = appData.clockFont;
1525 case 1: // MESSAGE_FONT
1526 name = appData.font;
1528 case 2: // COORD_FONT
1529 name = appData.coordFont;
1534 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1535 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1536 fontTable[n][squareSize] = strdup(name);
1537 fontValid[n][squareSize] = True;
1540 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1541 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1546 { // nothing to do, as the sounds are at all times represented by their text-string names already
1550 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1551 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1552 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1556 SaveColor(FILE *f, ArgDescriptor *ad)
1557 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1558 if(colorVariable[(int)(intptr_t)ad->argLoc])
1559 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1563 SaveBoardSize(FILE *f, char *name, void *addr)
1564 { // wrapper to shield back-end from BoardSize & sizeInfo
1565 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1569 ParseCommPortSettings(char *s)
1570 { // no such option in XBoard (yet)
1573 extern Widget engineOutputShell;
1576 GetActualPlacement(Widget wg, WindowPlacement *wp)
1586 XtSetArg(args[i], XtNx, &x); i++;
1587 XtSetArg(args[i], XtNy, &y); i++;
1588 XtSetArg(args[i], XtNwidth, &w); i++;
1589 XtSetArg(args[i], XtNheight, &h); i++;
1590 XtGetValues(wg, args, i);
1599 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1600 // In XBoard this will have to wait until awareness of window parameters is implemented
1601 GetActualPlacement(shellWidget, &wpMain);
1602 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1603 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1604 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1605 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1606 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1607 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1611 PrintCommPortSettings(FILE *f, char *name)
1612 { // This option does not exist in XBoard
1616 MySearchPath(char *installDir, char *name, char *fullname)
1617 { // just append installDir and name. Perhaps ExpandPath should be used here?
1618 name = ExpandPathName(name);
1619 if(name && name[0] == '/')
1620 safeStrCpy(fullname, name, MSG_SIZ );
1622 sprintf(fullname, "%s%c%s", installDir, '/', name);
1628 MyGetFullPathName(char *name, char *fullname)
1629 { // should use ExpandPath?
1630 name = ExpandPathName(name);
1631 safeStrCpy(fullname, name, MSG_SIZ );
1636 EnsureOnScreen(int *x, int *y, int minX, int minY)
1643 { // [HGM] args: allows testing if main window is realized from back-end
1644 return xBoardWindow != 0;
1648 PopUpStartupDialog()
1649 { // start menu not implemented in XBoard
1653 ConvertToLine(int argc, char **argv)
1655 static char line[128*1024], buf[1024];
1659 for(i=1; i<argc; i++)
1661 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1662 && argv[i][0] != '{' )
1663 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1665 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1666 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1669 line[strlen(line)-1] = NULLCHAR;
1673 //--------------------------------------------------------------------------------------------
1675 extern Boolean twoBoards, partnerUp;
1678 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1680 #define BoardSize int
1681 void InitDrawingSizes(BoardSize boardSize, int flags)
1682 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1683 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1685 XtGeometryResult gres;
1687 static Dimension oldWidth, oldHeight;
1688 static VariantClass oldVariant;
1689 static int oldDual = -1, oldMono = -1;
1691 if(!formWidget) return;
1693 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1694 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1695 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1697 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1699 * Enable shell resizing.
1701 shellArgs[0].value = (XtArgVal) &w;
1702 shellArgs[1].value = (XtArgVal) &h;
1703 XtGetValues(shellWidget, shellArgs, 2);
1705 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1706 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1707 XtSetValues(shellWidget, &shellArgs[2], 4);
1709 XtSetArg(args[0], XtNdefaultDistance, &sep);
1710 XtGetValues(formWidget, args, 1);
1712 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1714 hOffset = boardWidth + 10;
1715 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1716 secondSegments[i] = gridSegments[i];
1717 secondSegments[i].x1 += hOffset;
1718 secondSegments[i].x2 += hOffset;
1721 XtSetArg(args[0], XtNwidth, boardWidth);
1722 XtSetArg(args[1], XtNheight, boardHeight);
1723 XtSetValues(boardWidget, args, 2);
1725 timerWidth = (boardWidth - sep) / 2;
1726 XtSetArg(args[0], XtNwidth, timerWidth);
1727 XtSetValues(whiteTimerWidget, args, 1);
1728 XtSetValues(blackTimerWidget, args, 1);
1730 XawFormDoLayout(formWidget, False);
1732 if (appData.titleInWindow) {
1734 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1735 XtSetArg(args[i], XtNheight, &h); i++;
1736 XtGetValues(titleWidget, args, i);
1738 w = boardWidth - 2*bor;
1740 XtSetArg(args[0], XtNwidth, &w);
1741 XtGetValues(menuBarWidget, args, 1);
1742 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1745 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1746 if (gres != XtGeometryYes && appData.debugMode) {
1748 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1749 programName, gres, w, h, wr, hr);
1753 XawFormDoLayout(formWidget, True);
1756 * Inhibit shell resizing.
1758 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1759 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1760 shellArgs[4].value = shellArgs[2].value = w;
1761 shellArgs[5].value = shellArgs[3].value = h;
1762 XtSetValues(shellWidget, &shellArgs[0], 6);
1765 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1768 if(gameInfo.variant != oldVariant) { // and only if variant changed
1771 for(i=0; i<4; i++) {
1773 for(p=0; p<=(int)WhiteKing; p++)
1774 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1775 if(gameInfo.variant == VariantShogi) {
1776 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1777 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1778 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1779 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1780 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1783 if(gameInfo.variant == VariantGothic) {
1784 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1787 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1788 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1789 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1792 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1793 for(p=0; p<=(int)WhiteKing; p++)
1794 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1795 if(gameInfo.variant == VariantShogi) {
1796 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1797 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1798 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1799 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1800 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1803 if(gameInfo.variant == VariantGothic) {
1804 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1807 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1808 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1809 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1814 for(i=0; i<2; i++) {
1816 for(p=0; p<=(int)WhiteKing; p++)
1817 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1818 if(gameInfo.variant == VariantShogi) {
1819 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1820 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1821 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1822 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1823 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1826 if(gameInfo.variant == VariantGothic) {
1827 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1830 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1831 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1832 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1838 if(appData.monoMode == oldMono)
1841 oldMono = appData.monoMode;
1845 void ParseIcsTextColors()
1846 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1847 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1848 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1849 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1850 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1851 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1852 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1853 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1854 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1855 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1856 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1858 if (appData.colorize) {
1860 _("%s: can't parse color names; disabling colorization\n"),
1863 appData.colorize = FALSE;
1868 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1869 XrmValue vFrom, vTo;
1870 int forceMono = False;
1872 if (!appData.monoMode) {
1873 vFrom.addr = (caddr_t) appData.lightSquareColor;
1874 vFrom.size = strlen(appData.lightSquareColor);
1875 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1876 if (vTo.addr == NULL) {
1877 appData.monoMode = True;
1880 lightSquareColor = *(Pixel *) vTo.addr;
1883 if (!appData.monoMode) {
1884 vFrom.addr = (caddr_t) appData.darkSquareColor;
1885 vFrom.size = strlen(appData.darkSquareColor);
1886 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1887 if (vTo.addr == NULL) {
1888 appData.monoMode = True;
1891 darkSquareColor = *(Pixel *) vTo.addr;
1894 if (!appData.monoMode) {
1895 vFrom.addr = (caddr_t) appData.whitePieceColor;
1896 vFrom.size = strlen(appData.whitePieceColor);
1897 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1898 if (vTo.addr == NULL) {
1899 appData.monoMode = True;
1902 whitePieceColor = *(Pixel *) vTo.addr;
1905 if (!appData.monoMode) {
1906 vFrom.addr = (caddr_t) appData.blackPieceColor;
1907 vFrom.size = strlen(appData.blackPieceColor);
1908 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1909 if (vTo.addr == NULL) {
1910 appData.monoMode = True;
1913 blackPieceColor = *(Pixel *) vTo.addr;
1917 if (!appData.monoMode) {
1918 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1919 vFrom.size = strlen(appData.highlightSquareColor);
1920 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1921 if (vTo.addr == NULL) {
1922 appData.monoMode = True;
1925 highlightSquareColor = *(Pixel *) vTo.addr;
1929 if (!appData.monoMode) {
1930 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1931 vFrom.size = strlen(appData.premoveHighlightColor);
1932 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1933 if (vTo.addr == NULL) {
1934 appData.monoMode = True;
1937 premoveHighlightColor = *(Pixel *) vTo.addr;
1945 { // [HGM] taken out of main
1947 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1948 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1949 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1951 if (appData.bitmapDirectory[0] != NULLCHAR) {
1955 CreateXPMBoard(appData.liteBackTextureFile, 1);
1956 CreateXPMBoard(appData.darkBackTextureFile, 0);
1960 /* Create regular pieces */
1961 if (!useImages) CreatePieces();
1970 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1971 XSetWindowAttributes window_attributes;
1973 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1974 XrmValue vFrom, vTo;
1975 XtGeometryResult gres;
1978 int forceMono = False;
1980 srandom(time(0)); // [HGM] book: make random truly random
1982 setbuf(stdout, NULL);
1983 setbuf(stderr, NULL);
1986 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1987 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1991 programName = strrchr(argv[0], '/');
1992 if (programName == NULL)
1993 programName = argv[0];
1998 XtSetLanguageProc(NULL, NULL, NULL);
1999 bindtextdomain(PACKAGE, LOCALEDIR);
2000 textdomain(PACKAGE);
2004 XtAppInitialize(&appContext, "XBoard", shellOptions,
2005 XtNumber(shellOptions),
2006 &argc, argv, xboardResources, NULL, 0);
2007 appData.boardSize = "";
2008 InitAppData(ConvertToLine(argc, argv));
2010 if (p == NULL) p = "/tmp";
2011 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2012 gameCopyFilename = (char*) malloc(i);
2013 gamePasteFilename = (char*) malloc(i);
2014 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2015 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2017 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2018 clientResources, XtNumber(clientResources),
2021 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2022 static char buf[MSG_SIZ];
2023 EscapeExpand(buf, appData.firstInitString);
2024 appData.firstInitString = strdup(buf);
2025 EscapeExpand(buf, appData.secondInitString);
2026 appData.secondInitString = strdup(buf);
2027 EscapeExpand(buf, appData.firstComputerString);
2028 appData.firstComputerString = strdup(buf);
2029 EscapeExpand(buf, appData.secondComputerString);
2030 appData.secondComputerString = strdup(buf);
2033 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2036 if (chdir(chessDir) != 0) {
2037 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2043 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2044 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2045 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2046 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2049 setbuf(debugFP, NULL);
2053 if (appData.debugMode) {
2054 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2058 /* [HGM,HR] make sure board size is acceptable */
2059 if(appData.NrFiles > BOARD_FILES ||
2060 appData.NrRanks > BOARD_RANKS )
2061 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2064 /* This feature does not work; animation needs a rewrite */
2065 appData.highlightDragging = FALSE;
2069 xDisplay = XtDisplay(shellWidget);
2070 xScreen = DefaultScreen(xDisplay);
2071 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2073 gameInfo.variant = StringToVariant(appData.variant);
2074 InitPosition(FALSE);
2077 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2079 if (isdigit(appData.boardSize[0])) {
2080 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2081 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2082 &fontPxlSize, &smallLayout, &tinyLayout);
2084 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2085 programName, appData.boardSize);
2089 /* Find some defaults; use the nearest known size */
2090 SizeDefaults *szd, *nearest;
2091 int distance = 99999;
2092 nearest = szd = sizeDefaults;
2093 while (szd->name != NULL) {
2094 if (abs(szd->squareSize - squareSize) < distance) {
2096 distance = abs(szd->squareSize - squareSize);
2097 if (distance == 0) break;
2101 if (i < 2) lineGap = nearest->lineGap;
2102 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2103 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2104 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2105 if (i < 6) smallLayout = nearest->smallLayout;
2106 if (i < 7) tinyLayout = nearest->tinyLayout;
2109 SizeDefaults *szd = sizeDefaults;
2110 if (*appData.boardSize == NULLCHAR) {
2111 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2112 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2115 if (szd->name == NULL) szd--;
2116 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2118 while (szd->name != NULL &&
2119 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2120 if (szd->name == NULL) {
2121 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2122 programName, appData.boardSize);
2126 squareSize = szd->squareSize;
2127 lineGap = szd->lineGap;
2128 clockFontPxlSize = szd->clockFontPxlSize;
2129 coordFontPxlSize = szd->coordFontPxlSize;
2130 fontPxlSize = szd->fontPxlSize;
2131 smallLayout = szd->smallLayout;
2132 tinyLayout = szd->tinyLayout;
2133 // [HGM] font: use defaults from settings file if available and not overruled
2135 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2136 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2137 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2138 appData.font = fontTable[MESSAGE_FONT][squareSize];
2139 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2140 appData.coordFont = fontTable[COORD_FONT][squareSize];
2142 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2143 if (strlen(appData.pixmapDirectory) > 0) {
2144 p = ExpandPathName(appData.pixmapDirectory);
2146 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2147 appData.pixmapDirectory);
2150 if (appData.debugMode) {
2151 fprintf(stderr, _("\
2152 XBoard square size (hint): %d\n\
2153 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2155 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2156 if (appData.debugMode) {
2157 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2160 defaultLineGap = lineGap;
2161 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2163 /* [HR] height treated separately (hacked) */
2164 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2165 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2166 if (appData.showJail == 1) {
2167 /* Jail on top and bottom */
2168 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2169 XtSetArg(boardArgs[2], XtNheight,
2170 boardHeight + 2*(lineGap + squareSize));
2171 } else if (appData.showJail == 2) {
2173 XtSetArg(boardArgs[1], XtNwidth,
2174 boardWidth + 2*(lineGap + squareSize));
2175 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2178 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2179 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2183 * Determine what fonts to use.
2186 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2187 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2188 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2189 fontSet = CreateFontSet(appData.font);
2190 clockFontSet = CreateFontSet(appData.clockFont);
2192 /* For the coordFont, use the 0th font of the fontset. */
2193 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2194 XFontStruct **font_struct_list;
2195 char **font_name_list;
2196 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2197 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2198 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2201 appData.font = FindFont(appData.font, fontPxlSize);
2202 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2203 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2204 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2205 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2206 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2207 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2209 countFontID = coordFontID; // [HGM] holdings
2210 countFontStruct = coordFontStruct;
2212 xdb = XtDatabase(xDisplay);
2214 XrmPutLineResource(&xdb, "*international: True");
2215 vTo.size = sizeof(XFontSet);
2216 vTo.addr = (XtPointer) &fontSet;
2217 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2219 XrmPutStringResource(&xdb, "*font", appData.font);
2223 * Detect if there are not enough colors available and adapt.
2225 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2226 appData.monoMode = True;
2229 forceMono = MakeColors();
2232 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2234 appData.monoMode = True;
2237 if (appData.lowTimeWarning && !appData.monoMode) {
2238 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2239 vFrom.size = strlen(appData.lowTimeWarningColor);
2240 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2241 if (vTo.addr == NULL)
2242 appData.monoMode = True;
2244 lowTimeWarningColor = *(Pixel *) vTo.addr;
2247 if (appData.monoMode && appData.debugMode) {
2248 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2249 (unsigned long) XWhitePixel(xDisplay, xScreen),
2250 (unsigned long) XBlackPixel(xDisplay, xScreen));
2253 ParseIcsTextColors();
2254 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2255 textColors[ColorNone].attr = 0;
2257 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2263 layoutName = "tinyLayout";
2264 } else if (smallLayout) {
2265 layoutName = "smallLayout";
2267 layoutName = "normalLayout";
2269 /* Outer layoutWidget is there only to provide a name for use in
2270 resources that depend on the layout style */
2272 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2273 layoutArgs, XtNumber(layoutArgs));
2275 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2276 formArgs, XtNumber(formArgs));
2277 XtSetArg(args[0], XtNdefaultDistance, &sep);
2278 XtGetValues(formWidget, args, 1);
2281 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2282 XtSetArg(args[0], XtNtop, XtChainTop);
2283 XtSetArg(args[1], XtNbottom, XtChainTop);
2284 XtSetArg(args[2], XtNright, XtChainLeft);
2285 XtSetValues(menuBarWidget, args, 3);
2287 widgetList[j++] = whiteTimerWidget =
2288 XtCreateWidget("whiteTime", labelWidgetClass,
2289 formWidget, timerArgs, XtNumber(timerArgs));
2291 XtSetArg(args[0], XtNfontSet, clockFontSet);
2293 XtSetArg(args[0], XtNfont, clockFontStruct);
2295 XtSetArg(args[1], XtNtop, XtChainTop);
2296 XtSetArg(args[2], XtNbottom, XtChainTop);
2297 XtSetValues(whiteTimerWidget, args, 3);
2299 widgetList[j++] = blackTimerWidget =
2300 XtCreateWidget("blackTime", labelWidgetClass,
2301 formWidget, timerArgs, XtNumber(timerArgs));
2303 XtSetArg(args[0], XtNfontSet, clockFontSet);
2305 XtSetArg(args[0], XtNfont, clockFontStruct);
2307 XtSetArg(args[1], XtNtop, XtChainTop);
2308 XtSetArg(args[2], XtNbottom, XtChainTop);
2309 XtSetValues(blackTimerWidget, args, 3);
2311 if (appData.titleInWindow) {
2312 widgetList[j++] = titleWidget =
2313 XtCreateWidget("title", labelWidgetClass, formWidget,
2314 titleArgs, XtNumber(titleArgs));
2315 XtSetArg(args[0], XtNtop, XtChainTop);
2316 XtSetArg(args[1], XtNbottom, XtChainTop);
2317 XtSetValues(titleWidget, args, 2);
2320 if (appData.showButtonBar) {
2321 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2322 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2323 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2324 XtSetArg(args[2], XtNtop, XtChainTop);
2325 XtSetArg(args[3], XtNbottom, XtChainTop);
2326 XtSetValues(buttonBarWidget, args, 4);
2329 widgetList[j++] = messageWidget =
2330 XtCreateWidget("message", labelWidgetClass, formWidget,
2331 messageArgs, XtNumber(messageArgs));
2332 XtSetArg(args[0], XtNtop, XtChainTop);
2333 XtSetArg(args[1], XtNbottom, XtChainTop);
2334 XtSetValues(messageWidget, args, 2);
2336 widgetList[j++] = boardWidget =
2337 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2338 XtNumber(boardArgs));
2340 XtManageChildren(widgetList, j);
2342 timerWidth = (boardWidth - sep) / 2;
2343 XtSetArg(args[0], XtNwidth, timerWidth);
2344 XtSetValues(whiteTimerWidget, args, 1);
2345 XtSetValues(blackTimerWidget, args, 1);
2347 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2348 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2349 XtGetValues(whiteTimerWidget, args, 2);
2351 if (appData.showButtonBar) {
2352 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2353 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2354 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2358 * formWidget uses these constraints but they are stored
2362 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2363 XtSetValues(menuBarWidget, args, i);
2364 if (appData.titleInWindow) {
2367 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2368 XtSetValues(whiteTimerWidget, args, i);
2370 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2371 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2372 XtSetValues(blackTimerWidget, args, i);
2374 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2375 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2376 XtSetValues(titleWidget, args, i);
2378 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2379 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2380 XtSetValues(messageWidget, args, i);
2381 if (appData.showButtonBar) {
2383 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2384 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2385 XtSetValues(buttonBarWidget, args, i);
2389 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2390 XtSetValues(whiteTimerWidget, args, i);
2392 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2393 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2394 XtSetValues(blackTimerWidget, args, i);
2396 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2397 XtSetValues(titleWidget, args, i);
2399 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2400 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2401 XtSetValues(messageWidget, args, i);
2402 if (appData.showButtonBar) {
2404 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2405 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2406 XtSetValues(buttonBarWidget, args, i);
2411 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2412 XtSetValues(whiteTimerWidget, args, i);
2414 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2415 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2416 XtSetValues(blackTimerWidget, args, i);
2418 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2419 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2420 XtSetValues(messageWidget, args, i);
2421 if (appData.showButtonBar) {
2423 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2424 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2425 XtSetValues(buttonBarWidget, args, i);
2429 XtSetArg(args[0], XtNfromVert, messageWidget);
2430 XtSetArg(args[1], XtNtop, XtChainTop);
2431 XtSetArg(args[2], XtNbottom, XtChainBottom);
2432 XtSetArg(args[3], XtNleft, XtChainLeft);
2433 XtSetArg(args[4], XtNright, XtChainRight);
2434 XtSetValues(boardWidget, args, 5);
2436 XtRealizeWidget(shellWidget);
2439 XtSetArg(args[0], XtNx, wpMain.x);
2440 XtSetArg(args[1], XtNy, wpMain.y);
2441 XtSetValues(shellWidget, args, 2);
2445 * Correct the width of the message and title widgets.
2446 * It is not known why some systems need the extra fudge term.
2447 * The value "2" is probably larger than needed.
2449 XawFormDoLayout(formWidget, False);
2451 #define WIDTH_FUDGE 2
2453 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2454 XtSetArg(args[i], XtNheight, &h); i++;
2455 XtGetValues(messageWidget, args, i);
2456 if (appData.showButtonBar) {
2458 XtSetArg(args[i], XtNwidth, &w); i++;
2459 XtGetValues(buttonBarWidget, args, i);
2460 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2462 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2465 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2466 if (gres != XtGeometryYes && appData.debugMode) {
2467 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2468 programName, gres, w, h, wr, hr);
2471 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2472 /* The size used for the child widget in layout lags one resize behind
2473 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2475 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2476 if (gres != XtGeometryYes && appData.debugMode) {
2477 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2478 programName, gres, w, h, wr, hr);
2481 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2482 XtSetArg(args[1], XtNright, XtChainRight);
2483 XtSetValues(messageWidget, args, 2);
2485 if (appData.titleInWindow) {
2487 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2488 XtSetArg(args[i], XtNheight, &h); i++;
2489 XtGetValues(titleWidget, args, i);
2491 w = boardWidth - 2*bor;
2493 XtSetArg(args[0], XtNwidth, &w);
2494 XtGetValues(menuBarWidget, args, 1);
2495 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2498 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2499 if (gres != XtGeometryYes && appData.debugMode) {
2501 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2502 programName, gres, w, h, wr, hr);
2505 XawFormDoLayout(formWidget, True);
2507 xBoardWindow = XtWindow(boardWidget);
2509 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2510 // not need to go into InitDrawingSizes().
2514 * Create X checkmark bitmap and initialize option menu checks.
2516 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2517 checkmark_bits, checkmark_width, checkmark_height);
2518 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2519 #ifndef OPTIONSDIALOG
2520 if (appData.alwaysPromoteToQueen) {
2521 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2524 if (appData.animateDragging) {
2525 XtSetValues(XtNameToWidget(menuBarWidget,
2526 "menuOptions.Animate Dragging"),
2529 if (appData.animate) {
2530 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2533 if (appData.autoCallFlag) {
2534 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2537 if (appData.autoFlipView) {
2538 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2541 if (appData.blindfold) {
2542 XtSetValues(XtNameToWidget(menuBarWidget,
2543 "menuOptions.Blindfold"), args, 1);
2545 if (appData.flashCount > 0) {
2546 XtSetValues(XtNameToWidget(menuBarWidget,
2547 "menuOptions.Flash Moves"),
2551 if (appData.highlightDragging) {
2552 XtSetValues(XtNameToWidget(menuBarWidget,
2553 "menuOptions.Highlight Dragging"),
2557 if (appData.highlightLastMove) {
2558 XtSetValues(XtNameToWidget(menuBarWidget,
2559 "menuOptions.Highlight Last Move"),
2562 if (appData.highlightMoveWithArrow) {
2563 XtSetValues(XtNameToWidget(menuBarWidget,
2564 "menuOptions.Arrow"),
2567 // if (appData.icsAlarm) {
2568 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2571 if (appData.ringBellAfterMoves) {
2572 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2575 if (appData.oneClick) {
2576 XtSetValues(XtNameToWidget(menuBarWidget,
2577 "menuOptions.OneClick"), args, 1);
2579 if (appData.periodicUpdates) {
2580 XtSetValues(XtNameToWidget(menuBarWidget,
2581 "menuOptions.Periodic Updates"), args, 1);
2583 if (appData.ponderNextMove) {
2584 XtSetValues(XtNameToWidget(menuBarWidget,
2585 "menuOptions.Ponder Next Move"), args, 1);
2587 if (appData.popupExitMessage) {
2588 XtSetValues(XtNameToWidget(menuBarWidget,
2589 "menuOptions.Popup Exit Message"), args, 1);
2591 if (appData.popupMoveErrors) {
2592 XtSetValues(XtNameToWidget(menuBarWidget,
2593 "menuOptions.Popup Move Errors"), args, 1);
2595 // if (appData.premove) {
2596 // XtSetValues(XtNameToWidget(menuBarWidget,
2597 // "menuOptions.Premove"), args, 1);
2599 if (appData.showCoords) {
2600 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2603 if (appData.hideThinkingFromHuman) {
2604 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2607 if (appData.testLegality) {
2608 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2612 if (saveSettingsOnExit) {
2613 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2620 ReadBitmap(&wIconPixmap, "icon_white.bm",
2621 icon_white_bits, icon_white_width, icon_white_height);
2622 ReadBitmap(&bIconPixmap, "icon_black.bm",
2623 icon_black_bits, icon_black_width, icon_black_height);
2624 iconPixmap = wIconPixmap;
2626 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2627 XtSetValues(shellWidget, args, i);
2630 * Create a cursor for the board widget.
2632 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2633 XChangeWindowAttributes(xDisplay, xBoardWindow,
2634 CWCursor, &window_attributes);
2637 * Inhibit shell resizing.
2639 shellArgs[0].value = (XtArgVal) &w;
2640 shellArgs[1].value = (XtArgVal) &h;
2641 XtGetValues(shellWidget, shellArgs, 2);
2642 shellArgs[4].value = shellArgs[2].value = w;
2643 shellArgs[5].value = shellArgs[3].value = h;
2644 XtSetValues(shellWidget, &shellArgs[2], 4);
2645 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2646 marginH = h - boardHeight;
2648 CatchDeleteWindow(shellWidget, "QuitProc");
2656 if (appData.animate || appData.animateDragging)
2659 XtAugmentTranslations(formWidget,
2660 XtParseTranslationTable(globalTranslations));
2661 XtAugmentTranslations(boardWidget,
2662 XtParseTranslationTable(boardTranslations));
2663 XtAugmentTranslations(whiteTimerWidget,
2664 XtParseTranslationTable(whiteTranslations));
2665 XtAugmentTranslations(blackTimerWidget,
2666 XtParseTranslationTable(blackTranslations));
2668 /* Why is the following needed on some versions of X instead
2669 * of a translation? */
2670 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2671 (XtEventHandler) EventProc, NULL);
2673 XtAddEventHandler(formWidget, KeyPressMask, False,
2674 (XtEventHandler) MoveTypeInProc, NULL);
2676 /* [AS] Restore layout */
2677 if( wpMoveHistory.visible ) {
2681 if( wpEvalGraph.visible )
2686 if( wpEngineOutput.visible ) {
2687 EngineOutputPopUp();
2692 if (errorExitStatus == -1) {
2693 if (appData.icsActive) {
2694 /* We now wait until we see "login:" from the ICS before
2695 sending the logon script (problems with timestamp otherwise) */
2696 /*ICSInitScript();*/
2697 if (appData.icsInputBox) ICSInputBoxPopUp();
2701 signal(SIGWINCH, TermSizeSigHandler);
2703 signal(SIGINT, IntSigHandler);
2704 signal(SIGTERM, IntSigHandler);
2705 if (*appData.cmailGameName != NULLCHAR) {
2706 signal(SIGUSR1, CmailSigHandler);
2709 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2711 // XtSetKeyboardFocus(shellWidget, formWidget);
2712 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2714 XtAppMainLoop(appContext);
2715 if (appData.debugMode) fclose(debugFP); // [DM] debug
2719 static Boolean noEcho;
2724 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2725 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2727 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2728 unlink(gameCopyFilename);
2729 unlink(gamePasteFilename);
2730 if(noEcho) EchoOn();
2733 RETSIGTYPE TermSizeSigHandler(int sig)
2746 CmailSigHandler(sig)
2752 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2754 /* Activate call-back function CmailSigHandlerCallBack() */
2755 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2757 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2761 CmailSigHandlerCallBack(isr, closure, message, count, error)
2769 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2771 /**** end signal code ****/
2777 /* try to open the icsLogon script, either in the location given
2778 * or in the users HOME directory
2785 f = fopen(appData.icsLogon, "r");
2788 homedir = getenv("HOME");
2789 if (homedir != NULL)
2791 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2792 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2793 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2794 f = fopen(buf, "r");
2799 ProcessICSInitScript(f);
2801 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2824 if (!menuBarWidget) return;
2825 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2827 DisplayError("menuEdit.Revert", 0);
2829 XtSetSensitive(w, !grey);
2831 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2833 DisplayError("menuEdit.Annotate", 0);
2835 XtSetSensitive(w, !grey);
2840 SetMenuEnables(enab)
2844 if (!menuBarWidget) return;
2845 while (enab->name != NULL) {
2846 w = XtNameToWidget(menuBarWidget, enab->name);
2848 DisplayError(enab->name, 0);
2850 XtSetSensitive(w, enab->value);
2856 Enables icsEnables[] = {
2857 { "menuFile.Mail Move", False },
2858 { "menuFile.Reload CMail Message", False },
2859 { "menuMode.Machine Black", False },
2860 { "menuMode.Machine White", False },
2861 { "menuMode.Analysis Mode", False },
2862 { "menuMode.Analyze File", False },
2863 { "menuMode.Two Machines", False },
2864 { "menuMode.Machine Match", False },
2866 { "menuEngine.Hint", False },
2867 { "menuEngine.Book", False },
2868 { "menuEngine.Move Now", False },
2869 #ifndef OPTIONSDIALOG
2870 { "menuOptions.Periodic Updates", False },
2871 { "menuOptions.Hide Thinking", False },
2872 { "menuOptions.Ponder Next Move", False },
2875 { "menuEngine.Engine #1 Settings", False },
2876 { "menuEngine.Engine #2 Settings", False },
2877 { "menuEngine.Load Engine", False },
2878 { "menuEdit.Annotate", False },
2879 { "menuOptions.Match", False },
2883 Enables ncpEnables[] = {
2884 { "menuFile.Mail Move", False },
2885 { "menuFile.Reload CMail Message", False },
2886 { "menuMode.Machine White", False },
2887 { "menuMode.Machine Black", False },
2888 { "menuMode.Analysis Mode", False },
2889 { "menuMode.Analyze File", False },
2890 { "menuMode.Two Machines", False },
2891 { "menuMode.Machine Match", False },
2892 { "menuMode.ICS Client", False },
2893 { "menuView.ICStex", False },
2894 { "menuView.ICS Input Box", False },
2895 { "Action", False },
2896 { "menuEdit.Revert", False },
2897 { "menuEdit.Annotate", False },
2898 { "menuEngine.Engine #1 Settings", False },
2899 { "menuEngine.Engine #2 Settings", False },
2900 { "menuEngine.Move Now", False },
2901 { "menuEngine.Retract Move", False },
2902 { "menuOptions.ICS", False },
2903 #ifndef OPTIONSDIALOG
2904 { "menuOptions.Auto Flag", False },
2905 { "menuOptions.Auto Flip View", False },
2906 // { "menuOptions.ICS Alarm", False },
2907 { "menuOptions.Move Sound", False },
2908 { "menuOptions.Hide Thinking", False },
2909 { "menuOptions.Periodic Updates", False },
2910 { "menuOptions.Ponder Next Move", False },
2912 { "menuEngine.Hint", False },
2913 { "menuEngine.Book", False },
2917 Enables gnuEnables[] = {
2918 { "menuMode.ICS Client", False },
2919 { "menuView.ICStex", False },
2920 { "menuView.ICS Input Box", False },
2921 { "menuAction.Accept", False },
2922 { "menuAction.Decline", False },
2923 { "menuAction.Rematch", False },
2924 { "menuAction.Adjourn", False },
2925 { "menuAction.Stop Examining", False },
2926 { "menuAction.Stop Observing", False },
2927 { "menuAction.Upload to Examine", False },
2928 { "menuEdit.Revert", False },
2929 { "menuEdit.Annotate", False },
2930 { "menuOptions.ICS", False },
2932 /* The next two options rely on SetCmailMode being called *after* */
2933 /* SetGNUMode so that when GNU is being used to give hints these */
2934 /* menu options are still available */
2936 { "menuFile.Mail Move", False },
2937 { "menuFile.Reload CMail Message", False },
2938 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2939 { "menuMode.Machine White", True },
2940 { "menuMode.Machine Black", True },
2941 { "menuMode.Analysis Mode", True },
2942 { "menuMode.Analyze File", True },
2943 { "menuMode.Two Machines", True },
2944 { "menuMode.Machine Match", True },
2945 { "menuEngine.Engine #1 Settings", True },
2946 { "menuEngine.Engine #2 Settings", True },
2947 { "menuEngine.Hint", True },
2948 { "menuEngine.Book", True },
2949 { "menuEngine.Move Now", True },
2950 { "menuEngine.Retract Move", True },
2955 Enables cmailEnables[] = {
2957 { "menuAction.Call Flag", False },
2958 { "menuAction.Draw", True },
2959 { "menuAction.Adjourn", False },
2960 { "menuAction.Abort", False },
2961 { "menuAction.Stop Observing", False },
2962 { "menuAction.Stop Examining", False },
2963 { "menuFile.Mail Move", True },
2964 { "menuFile.Reload CMail Message", True },
2968 Enables trainingOnEnables[] = {
2969 { "menuMode.Edit Comment", False },
2970 { "menuMode.Pause", False },
2971 { "menuEdit.Forward", False },
2972 { "menuEdit.Backward", False },
2973 { "menuEdit.Forward to End", False },
2974 { "menuEdit.Back to Start", False },
2975 { "menuEngine.Move Now", False },
2976 { "menuEdit.Truncate Game", False },
2980 Enables trainingOffEnables[] = {
2981 { "menuMode.Edit Comment", True },
2982 { "menuMode.Pause", True },
2983 { "menuEdit.Forward", True },
2984 { "menuEdit.Backward", True },
2985 { "menuEdit.Forward to End", True },
2986 { "menuEdit.Back to Start", True },
2987 { "menuEngine.Move Now", True },
2988 { "menuEdit.Truncate Game", True },
2992 Enables machineThinkingEnables[] = {
2993 { "menuFile.Load Game", False },
2994 // { "menuFile.Load Next Game", False },
2995 // { "menuFile.Load Previous Game", False },
2996 // { "menuFile.Reload Same Game", False },
2997 { "menuEdit.Paste Game", False },
2998 { "menuFile.Load Position", False },
2999 // { "menuFile.Load Next Position", False },
3000 // { "menuFile.Load Previous Position", False },
3001 // { "menuFile.Reload Same Position", False },
3002 { "menuEdit.Paste Position", False },
3003 { "menuMode.Machine White", False },
3004 { "menuMode.Machine Black", False },
3005 { "menuMode.Two Machines", False },
3006 // { "menuMode.Machine Match", False },
3007 { "menuEngine.Retract Move", False },
3011 Enables userThinkingEnables[] = {
3012 { "menuFile.Load Game", True },
3013 // { "menuFile.Load Next Game", True },
3014 // { "menuFile.Load Previous Game", True },
3015 // { "menuFile.Reload Same Game", True },
3016 { "menuEdit.Paste Game", True },
3017 { "menuFile.Load Position", True },
3018 // { "menuFile.Load Next Position", True },
3019 // { "menuFile.Load Previous Position", True },
3020 // { "menuFile.Reload Same Position", True },
3021 { "menuEdit.Paste Position", True },
3022 { "menuMode.Machine White", True },
3023 { "menuMode.Machine Black", True },
3024 { "menuMode.Two Machines", True },
3025 // { "menuMode.Machine Match", True },
3026 { "menuEngine.Retract Move", True },
3032 SetMenuEnables(icsEnables);
3035 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3036 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3037 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3045 SetMenuEnables(ncpEnables);
3051 SetMenuEnables(gnuEnables);
3057 SetMenuEnables(cmailEnables);
3063 SetMenuEnables(trainingOnEnables);
3064 if (appData.showButtonBar) {
3065 XtSetSensitive(buttonBarWidget, False);
3071 SetTrainingModeOff()
3073 SetMenuEnables(trainingOffEnables);
3074 if (appData.showButtonBar) {
3075 XtSetSensitive(buttonBarWidget, True);
3080 SetUserThinkingEnables()
3082 if (appData.noChessProgram) return;
3083 SetMenuEnables(userThinkingEnables);
3087 SetMachineThinkingEnables()
3089 if (appData.noChessProgram) return;
3090 SetMenuEnables(machineThinkingEnables);
3092 case MachinePlaysBlack:
3093 case MachinePlaysWhite:
3094 case TwoMachinesPlay:
3095 XtSetSensitive(XtNameToWidget(menuBarWidget,
3096 ModeToWidgetName(gameMode)), True);
3103 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3104 #define HISTORY_SIZE 64
3105 static char *history[HISTORY_SIZE];
3106 int histIn = 0, histP = 0;
3109 SaveInHistory(char *cmd)
3111 if (history[histIn] != NULL) {
3112 free(history[histIn]);
3113 history[histIn] = NULL;
3115 if (*cmd == NULLCHAR) return;
3116 history[histIn] = StrSave(cmd);
3117 histIn = (histIn + 1) % HISTORY_SIZE;
3118 if (history[histIn] != NULL) {
3119 free(history[histIn]);
3120 history[histIn] = NULL;
3126 PrevInHistory(char *cmd)
3129 if (histP == histIn) {
3130 if (history[histIn] != NULL) free(history[histIn]);
3131 history[histIn] = StrSave(cmd);
3133 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3134 if (newhp == histIn || history[newhp] == NULL) return NULL;
3136 return history[histP];
3142 if (histP == histIn) return NULL;
3143 histP = (histP + 1) % HISTORY_SIZE;
3144 return history[histP];
3146 // end of borrowed code
3148 #define Abs(n) ((n)<0 ? -(n) : (n))
3152 InsertPxlSize(pattern, targetPxlSize)
3156 char *base_fnt_lst, strInt[12], *p, *q;
3157 int alternatives, i, len, strIntLen;
3160 * Replace the "*" (if present) in the pixel-size slot of each
3161 * alternative with the targetPxlSize.
3165 while ((p = strchr(p, ',')) != NULL) {
3169 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3170 strIntLen = strlen(strInt);
3171 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3175 while (alternatives--) {
3176 char *comma = strchr(p, ',');
3177 for (i=0; i<14; i++) {
3178 char *hyphen = strchr(p, '-');
3180 if (comma && hyphen > comma) break;
3181 len = hyphen + 1 - p;
3182 if (i == 7 && *p == '*' && len == 2) {
3184 memcpy(q, strInt, strIntLen);
3194 len = comma + 1 - p;
3201 return base_fnt_lst;
3205 CreateFontSet(base_fnt_lst)
3209 char **missing_list;
3213 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3214 &missing_list, &missing_count, &def_string);
3215 if (appData.debugMode) {
3217 XFontStruct **font_struct_list;
3218 char **font_name_list;
3219 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3221 fprintf(debugFP, " got list %s, locale %s\n",
3222 XBaseFontNameListOfFontSet(fntSet),
3223 XLocaleOfFontSet(fntSet));
3224 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3225 for (i = 0; i < count; i++) {
3226 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3229 for (i = 0; i < missing_count; i++) {
3230 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3233 if (fntSet == NULL) {
3234 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3239 #else // not ENABLE_NLS
3241 * Find a font that matches "pattern" that is as close as
3242 * possible to the targetPxlSize. Prefer fonts that are k
3243 * pixels smaller to fonts that are k pixels larger. The
3244 * pattern must be in the X Consortium standard format,
3245 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3246 * The return value should be freed with XtFree when no
3250 FindFont(pattern, targetPxlSize)
3254 char **fonts, *p, *best, *scalable, *scalableTail;
3255 int i, j, nfonts, minerr, err, pxlSize;
3257 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3259 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3260 programName, pattern);
3267 for (i=0; i<nfonts; i++) {
3270 if (*p != '-') continue;
3272 if (*p == NULLCHAR) break;
3273 if (*p++ == '-') j++;
3275 if (j < 7) continue;
3278 scalable = fonts[i];
3281 err = pxlSize - targetPxlSize;
3282 if (Abs(err) < Abs(minerr) ||
3283 (minerr > 0 && err < 0 && -err == minerr)) {
3289 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3290 /* If the error is too big and there is a scalable font,
3291 use the scalable font. */
3292 int headlen = scalableTail - scalable;
3293 p = (char *) XtMalloc(strlen(scalable) + 10);
3294 while (isdigit(*scalableTail)) scalableTail++;
3295 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3297 p = (char *) XtMalloc(strlen(best) + 2);
3298 safeStrCpy(p, best, strlen(best)+1 );
3300 if (appData.debugMode) {
3301 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3302 pattern, targetPxlSize, p);
3304 XFreeFontNames(fonts);
3310 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3311 // must be called before all non-first callse to CreateGCs()
3312 XtReleaseGC(shellWidget, highlineGC);
3313 XtReleaseGC(shellWidget, lightSquareGC);
3314 XtReleaseGC(shellWidget, darkSquareGC);
3315 XtReleaseGC(shellWidget, lineGC);
3316 if (appData.monoMode) {
3317 if (DefaultDepth(xDisplay, xScreen) == 1) {
3318 XtReleaseGC(shellWidget, wbPieceGC);
3320 XtReleaseGC(shellWidget, bwPieceGC);
3323 XtReleaseGC(shellWidget, prelineGC);
3324 XtReleaseGC(shellWidget, jailSquareGC);
3325 XtReleaseGC(shellWidget, wdPieceGC);
3326 XtReleaseGC(shellWidget, wlPieceGC);
3327 XtReleaseGC(shellWidget, wjPieceGC);
3328 XtReleaseGC(shellWidget, bdPieceGC);
3329 XtReleaseGC(shellWidget, blPieceGC);
3330 XtReleaseGC(shellWidget, bjPieceGC);
3334 void CreateGCs(int redo)
3336 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3337 | GCBackground | GCFunction | GCPlaneMask;
3338 XGCValues gc_values;
3341 gc_values.plane_mask = AllPlanes;
3342 gc_values.line_width = lineGap;
3343 gc_values.line_style = LineSolid;
3344 gc_values.function = GXcopy;
3347 DeleteGCs(); // called a second time; clean up old GCs first
3348 } else { // [HGM] grid and font GCs created on first call only
3349 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3350 gc_values.background = XWhitePixel(xDisplay, xScreen);
3351 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3352 XSetFont(xDisplay, coordGC, coordFontID);
3354 // [HGM] make font for holdings counts (white on black)
3355 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3356 gc_values.background = XBlackPixel(xDisplay, xScreen);
3357 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3358 XSetFont(xDisplay, countGC, countFontID);
3360 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3361 gc_values.background = XBlackPixel(xDisplay, xScreen);
3362 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3364 if (appData.monoMode) {
3365 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3366 gc_values.background = XWhitePixel(xDisplay, xScreen);
3367 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3369 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3370 gc_values.background = XBlackPixel(xDisplay, xScreen);
3371 lightSquareGC = wbPieceGC
3372 = XtGetGC(shellWidget, value_mask, &gc_values);
3374 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3375 gc_values.background = XWhitePixel(xDisplay, xScreen);
3376 darkSquareGC = bwPieceGC
3377 = XtGetGC(shellWidget, value_mask, &gc_values);
3379 if (DefaultDepth(xDisplay, xScreen) == 1) {
3380 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3381 gc_values.function = GXcopyInverted;
3382 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3383 gc_values.function = GXcopy;
3384 if (XBlackPixel(xDisplay, xScreen) == 1) {
3385 bwPieceGC = darkSquareGC;
3386 wbPieceGC = copyInvertedGC;
3388 bwPieceGC = copyInvertedGC;
3389 wbPieceGC = lightSquareGC;
3393 gc_values.foreground = highlightSquareColor;
3394 gc_values.background = highlightSquareColor;
3395 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3397 gc_values.foreground = premoveHighlightColor;
3398 gc_values.background = premoveHighlightColor;
3399 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3401 gc_values.foreground = lightSquareColor;
3402 gc_values.background = darkSquareColor;
3403 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3405 gc_values.foreground = darkSquareColor;
3406 gc_values.background = lightSquareColor;
3407 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3409 gc_values.foreground = jailSquareColor;
3410 gc_values.background = jailSquareColor;
3411 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3413 gc_values.foreground = whitePieceColor;
3414 gc_values.background = darkSquareColor;
3415 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3417 gc_values.foreground = whitePieceColor;
3418 gc_values.background = lightSquareColor;
3419 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3421 gc_values.foreground = whitePieceColor;
3422 gc_values.background = jailSquareColor;
3423 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3425 gc_values.foreground = blackPieceColor;
3426 gc_values.background = darkSquareColor;
3427 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3429 gc_values.foreground = blackPieceColor;
3430 gc_values.background = lightSquareColor;
3431 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3433 gc_values.foreground = blackPieceColor;
3434 gc_values.background = jailSquareColor;
3435 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3439 void loadXIM(xim, xmask, filename, dest, mask)
3452 fp = fopen(filename, "rb");
3454 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3461 for (y=0; y<h; ++y) {
3462 for (x=0; x<h; ++x) {
3467 XPutPixel(xim, x, y, blackPieceColor);
3469 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3472 XPutPixel(xim, x, y, darkSquareColor);
3474 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3477 XPutPixel(xim, x, y, whitePieceColor);
3479 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3482 XPutPixel(xim, x, y, lightSquareColor);
3484 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3492 /* create Pixmap of piece */
3493 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3495 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3498 /* create Pixmap of clipmask
3499 Note: We assume the white/black pieces have the same
3500 outline, so we make only 6 masks. This is okay
3501 since the XPM clipmask routines do the same. */
3503 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3505 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3508 /* now create the 1-bit version */
3509 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3512 values.foreground = 1;
3513 values.background = 0;
3515 /* Don't use XtGetGC, not read only */
3516 maskGC = XCreateGC(xDisplay, *mask,
3517 GCForeground | GCBackground, &values);
3518 XCopyPlane(xDisplay, temp, *mask, maskGC,
3519 0, 0, squareSize, squareSize, 0, 0, 1);
3520 XFreePixmap(xDisplay, temp);
3525 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3527 void CreateXIMPieces()
3532 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3537 /* The XSynchronize calls were copied from CreatePieces.
3538 Not sure if needed, but can't hurt */
3539 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3542 /* temp needed by loadXIM() */
3543 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3544 0, 0, ss, ss, AllPlanes, XYPixmap);
3546 if (strlen(appData.pixmapDirectory) == 0) {
3550 if (appData.monoMode) {
3551 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3555 fprintf(stderr, _("\nLoading XIMs...\n"));
3557 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3558 fprintf(stderr, "%d", piece+1);
3559 for (kind=0; kind<4; kind++) {
3560 fprintf(stderr, ".");
3561 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3562 ExpandPathName(appData.pixmapDirectory),
3563 piece <= (int) WhiteKing ? "" : "w",
3564 pieceBitmapNames[piece],
3566 ximPieceBitmap[kind][piece] =
3567 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3568 0, 0, ss, ss, AllPlanes, XYPixmap);
3569 if (appData.debugMode)
3570 fprintf(stderr, _("(File:%s:) "), buf);
3571 loadXIM(ximPieceBitmap[kind][piece],
3573 &(xpmPieceBitmap2[kind][piece]),
3574 &(ximMaskPm2[piece]));
3575 if(piece <= (int)WhiteKing)
3576 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3578 fprintf(stderr," ");
3580 /* Load light and dark squares */
3581 /* If the LSQ and DSQ pieces don't exist, we will
3582 draw them with solid squares. */
3583 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3584 if (access(buf, 0) != 0) {
3588 fprintf(stderr, _("light square "));
3590 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3591 0, 0, ss, ss, AllPlanes, XYPixmap);
3592 if (appData.debugMode)
3593 fprintf(stderr, _("(File:%s:) "), buf);
3595 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3596 fprintf(stderr, _("dark square "));
3597 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3598 ExpandPathName(appData.pixmapDirectory), ss);
3599 if (appData.debugMode)
3600 fprintf(stderr, _("(File:%s:) "), buf);
3602 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3603 0, 0, ss, ss, AllPlanes, XYPixmap);
3604 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3605 xpmJailSquare = xpmLightSquare;
3607 fprintf(stderr, _("Done.\n"));
3609 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3612 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3615 void CreateXPMBoard(char *s, int kind)
3619 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3620 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3621 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3625 void FreeXPMPieces()
3626 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3627 // thisroutine has to be called t free the old piece pixmaps
3629 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3630 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3632 XFreePixmap(xDisplay, xpmLightSquare);
3633 XFreePixmap(xDisplay, xpmDarkSquare);
3637 void CreateXPMPieces()
3641 u_int ss = squareSize;
3643 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3644 XpmColorSymbol symbols[4];
3645 static int redo = False;
3647 if(redo) FreeXPMPieces(); else redo = 1;
3649 /* The XSynchronize calls were copied from CreatePieces.
3650 Not sure if needed, but can't hurt */
3651 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3653 /* Setup translations so piece colors match square colors */
3654 symbols[0].name = "light_piece";
3655 symbols[0].value = appData.whitePieceColor;
3656 symbols[1].name = "dark_piece";
3657 symbols[1].value = appData.blackPieceColor;
3658 symbols[2].name = "light_square";
3659 symbols[2].value = appData.lightSquareColor;
3660 symbols[3].name = "dark_square";
3661 symbols[3].value = appData.darkSquareColor;
3663 attr.valuemask = XpmColorSymbols;
3664 attr.colorsymbols = symbols;
3665 attr.numsymbols = 4;
3667 if (appData.monoMode) {
3668 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3672 if (strlen(appData.pixmapDirectory) == 0) {
3673 XpmPieces* pieces = builtInXpms;
3676 while (pieces->size != squareSize && pieces->size) pieces++;
3677 if (!pieces->size) {
3678 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3681 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3682 for (kind=0; kind<4; kind++) {
3684 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3685 pieces->xpm[piece][kind],
3686 &(xpmPieceBitmap2[kind][piece]),
3687 NULL, &attr)) != 0) {
3688 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3692 if(piece <= (int) WhiteKing)
3693 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3697 xpmJailSquare = xpmLightSquare;
3701 fprintf(stderr, _("\nLoading XPMs...\n"));
3704 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3705 fprintf(stderr, "%d ", piece+1);
3706 for (kind=0; kind<4; kind++) {
3707 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3708 ExpandPathName(appData.pixmapDirectory),
3709 piece > (int) WhiteKing ? "w" : "",
3710 pieceBitmapNames[piece],
3712 if (appData.debugMode) {
3713 fprintf(stderr, _("(File:%s:) "), buf);
3715 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3716 &(xpmPieceBitmap2[kind][piece]),
3717 NULL, &attr)) != 0) {
3718 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3719 // [HGM] missing: read of unorthodox piece failed; substitute King.
3720 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3721 ExpandPathName(appData.pixmapDirectory),
3723 if (appData.debugMode) {
3724 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3726 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3727 &(xpmPieceBitmap2[kind][piece]),
3731 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3736 if(piece <= (int) WhiteKing)
3737 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3740 /* Load light and dark squares */
3741 /* If the LSQ and DSQ pieces don't exist, we will
3742 draw them with solid squares. */
3743 fprintf(stderr, _("light square "));
3744 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3745 if (access(buf, 0) != 0) {
3749 if (appData.debugMode)
3750 fprintf(stderr, _("(File:%s:) "), buf);
3752 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3753 &xpmLightSquare, NULL, &attr)) != 0) {
3754 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3757 fprintf(stderr, _("dark square "));
3758 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3759 ExpandPathName(appData.pixmapDirectory), ss);
3760 if (appData.debugMode) {
3761 fprintf(stderr, _("(File:%s:) "), buf);
3763 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3764 &xpmDarkSquare, NULL, &attr)) != 0) {
3765 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3769 xpmJailSquare = xpmLightSquare;
3770 fprintf(stderr, _("Done.\n"));
3772 oldVariant = -1; // kludge to force re-makig of animation masks
3773 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3776 #endif /* HAVE_LIBXPM */
3779 /* No built-in bitmaps */
3784 u_int ss = squareSize;
3786 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3789 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3790 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3791 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3792 pieceBitmapNames[piece],
3793 ss, kind == SOLID ? 's' : 'o');
3794 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3795 if(piece <= (int)WhiteKing)
3796 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3800 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3804 /* With built-in bitmaps */
3807 BuiltInBits* bib = builtInBits;
3810 u_int ss = squareSize;
3812 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3815 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3817 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3818 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3819 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3820 pieceBitmapNames[piece],
3821 ss, kind == SOLID ? 's' : 'o');
3822 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3823 bib->bits[kind][piece], ss, ss);
3824 if(piece <= (int)WhiteKing)
3825 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3829 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3834 void ReadBitmap(pm, name, bits, wreq, hreq)
3837 unsigned char bits[];
3843 char msg[MSG_SIZ], fullname[MSG_SIZ];
3845 if (*appData.bitmapDirectory != NULLCHAR) {
3846 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3847 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3848 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3849 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3850 &w, &h, pm, &x_hot, &y_hot);
3851 fprintf(stderr, "load %s\n", name);
3852 if (errcode != BitmapSuccess) {
3854 case BitmapOpenFailed:
3855 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3857 case BitmapFileInvalid:
3858 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3860 case BitmapNoMemory:
3861 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3865 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3869 fprintf(stderr, _("%s: %s...using built-in\n"),
3871 } else if (w != wreq || h != hreq) {
3873 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3874 programName, fullname, w, h, wreq, hreq);
3880 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3889 if (lineGap == 0) return;
3891 /* [HR] Split this into 2 loops for non-square boards. */
3893 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3894 gridSegments[i].x1 = 0;
3895 gridSegments[i].x2 =
3896 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3897 gridSegments[i].y1 = gridSegments[i].y2
3898 = lineGap / 2 + (i * (squareSize + lineGap));
3901 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3902 gridSegments[j + i].y1 = 0;
3903 gridSegments[j + i].y2 =
3904 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3905 gridSegments[j + i].x1 = gridSegments[j + i].x2
3906 = lineGap / 2 + (j * (squareSize + lineGap));
3910 static void MenuBarSelect(w, addr, index)
3915 XtActionProc proc = (XtActionProc) addr;
3917 (proc)(NULL, NULL, NULL, NULL);
3920 void CreateMenuBarPopup(parent, name, mb)
3930 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3933 XtSetArg(args[j], XtNleftMargin, 20); j++;
3934 XtSetArg(args[j], XtNrightMargin, 20); j++;
3936 while (mi->string != NULL) {
3937 if (strcmp(mi->string, "----") == 0) {
3938 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3941 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3942 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3944 XtAddCallback(entry, XtNcallback,
3945 (XtCallbackProc) MenuBarSelect,
3946 (caddr_t) mi->proc);
3952 Widget CreateMenuBar(mb)
3955 int j, nChar=0, nr=0, boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3956 Widget anchor, menuBar;
3958 char menuName[MSG_SIZ];
3960 while(mb[nr].name != NULL) nChar += strlen(_(mb[nr++].name)) + 3;
3963 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3964 XtSetArg(args[j], XtNvSpace, 0); j++;
3965 XtSetArg(args[j], XtNborderWidth, 0); j++;
3966 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3967 formWidget, args, j);
3969 while (mb->name != NULL) {
3970 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3971 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3973 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3976 shortName[0] = mb->name[0];
3977 shortName[1] = NULLCHAR;
3978 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3981 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3982 XtSetArg(args[j], XtNwidth, (boardWidth-40)*(strlen(_(mb->name)) + 3)/nChar); j++;
3985 XtSetArg(args[j], XtNborderWidth, 0); j++;
3986 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3988 CreateMenuBarPopup(menuBar, menuName, mb);
3994 Widget CreateButtonBar(mi)
3998 Widget button, buttonBar;
4002 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4004 XtSetArg(args[j], XtNhSpace, 0); j++;
4006 XtSetArg(args[j], XtNborderWidth, 0); j++;
4007 XtSetArg(args[j], XtNvSpace, 0); j++;
4008 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4009 formWidget, args, j);
4011 while (mi->string != NULL) {
4014 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4015 XtSetArg(args[j], XtNborderWidth, 0); j++;
4017 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4018 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4019 buttonBar, args, j);
4020 XtAddCallback(button, XtNcallback,
4021 (XtCallbackProc) MenuBarSelect,
4022 (caddr_t) mi->proc);
4029 CreatePieceMenu(name, color)
4036 ChessSquare selection;
4038 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4039 boardWidget, args, 0);
4041 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4042 String item = pieceMenuStrings[color][i];
4044 if (strcmp(item, "----") == 0) {
4045 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4048 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4049 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4051 selection = pieceMenuTranslation[color][i];
4052 XtAddCallback(entry, XtNcallback,
4053 (XtCallbackProc) PieceMenuSelect,
4054 (caddr_t) selection);
4055 if (selection == WhitePawn || selection == BlackPawn) {
4056 XtSetArg(args[0], XtNpopupOnEntry, entry);
4057 XtSetValues(menu, args, 1);
4070 ChessSquare selection;
4072 whitePieceMenu = CreatePieceMenu("menuW", 0);
4073 blackPieceMenu = CreatePieceMenu("menuB", 1);
4075 XtRegisterGrabAction(PieceMenuPopup, True,
4076 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4077 GrabModeAsync, GrabModeAsync);
4079 XtSetArg(args[0], XtNlabel, _("Drop"));
4080 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4081 boardWidget, args, 1);
4082 for (i = 0; i < DROP_MENU_SIZE; i++) {
4083 String item = dropMenuStrings[i];
4085 if (strcmp(item, "----") == 0) {
4086 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4089 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4090 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4092 selection = dropMenuTranslation[i];
4093 XtAddCallback(entry, XtNcallback,
4094 (XtCallbackProc) DropMenuSelect,
4095 (caddr_t) selection);
4100 void SetupDropMenu()
4108 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4109 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4110 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4111 dmEnables[i].piece);
4112 XtSetSensitive(entry, p != NULL || !appData.testLegality
4113 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4114 && !appData.icsActive));
4116 while (p && *p++ == dmEnables[i].piece) count++;
4117 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4119 XtSetArg(args[j], XtNlabel, label); j++;
4120 XtSetValues(entry, args, j);
4124 void PieceMenuPopup(w, event, params, num_params)
4128 Cardinal *num_params;
4130 String whichMenu; int menuNr = -2;
4131 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4132 if (event->type == ButtonRelease)
4133 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4134 else if (event->type == ButtonPress)
4135 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4137 case 0: whichMenu = params[0]; break;
4138 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4140 case -1: if (errorUp) ErrorPopDown();
4143 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4146 static void PieceMenuSelect(w, piece, junk)
4151 if (pmFromX < 0 || pmFromY < 0) return;
4152 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4155 static void DropMenuSelect(w, piece, junk)
4160 if (pmFromX < 0 || pmFromY < 0) return;
4161 DropMenuEvent(piece, pmFromX, pmFromY);
4164 void WhiteClock(w, event, prms, nprms)
4170 shiftKey = prms[0][0] & 1;
4174 void BlackClock(w, event, prms, nprms)
4180 shiftKey = prms[0][0] & 1;
4186 * If the user selects on a border boundary, return -1; if off the board,
4187 * return -2. Otherwise map the event coordinate to the square.
4189 int EventToSquare(x, limit)
4197 if ((x % (squareSize + lineGap)) >= squareSize)
4199 x /= (squareSize + lineGap);
4205 static void do_flash_delay(msec)
4211 static void drawHighlight(file, rank, gc)
4217 if (lineGap == 0) return;
4220 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4221 (squareSize + lineGap);
4222 y = lineGap/2 + rank * (squareSize + lineGap);
4224 x = lineGap/2 + file * (squareSize + lineGap);
4225 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4226 (squareSize + lineGap);
4229 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4230 squareSize+lineGap, squareSize+lineGap);
4233 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4234 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4237 SetHighlights(fromX, fromY, toX, toY)
4238 int fromX, fromY, toX, toY;
4240 if (hi1X != fromX || hi1Y != fromY) {
4241 if (hi1X >= 0 && hi1Y >= 0) {
4242 drawHighlight(hi1X, hi1Y, lineGC);
4244 } // [HGM] first erase both, then draw new!
4245 if (hi2X != toX || hi2Y != toY) {
4246 if (hi2X >= 0 && hi2Y >= 0) {
4247 drawHighlight(hi2X, hi2Y, lineGC);
4250 if (hi1X != fromX || hi1Y != fromY) {
4251 if (fromX >= 0 && fromY >= 0) {
4252 drawHighlight(fromX, fromY, highlineGC);
4255 if (hi2X != toX || hi2Y != toY) {
4256 if (toX >= 0 && toY >= 0) {
4257 drawHighlight(toX, toY, highlineGC);
4269 SetHighlights(-1, -1, -1, -1);
4274 SetPremoveHighlights(fromX, fromY, toX, toY)
4275 int fromX, fromY, toX, toY;
4277 if (pm1X != fromX || pm1Y != fromY) {
4278 if (pm1X >= 0 && pm1Y >= 0) {
4279 drawHighlight(pm1X, pm1Y, lineGC);
4281 if (fromX >= 0 && fromY >= 0) {
4282 drawHighlight(fromX, fromY, prelineGC);
4285 if (pm2X != toX || pm2Y != toY) {
4286 if (pm2X >= 0 && pm2Y >= 0) {
4287 drawHighlight(pm2X, pm2Y, lineGC);
4289 if (toX >= 0 && toY >= 0) {
4290 drawHighlight(toX, toY, prelineGC);
4300 ClearPremoveHighlights()
4302 SetPremoveHighlights(-1, -1, -1, -1);
4305 static int CutOutSquare(x, y, x0, y0, kind)
4306 int x, y, *x0, *y0, kind;
4308 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4309 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4311 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4312 if(textureW[kind] < W*squareSize)
4313 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4315 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4316 if(textureH[kind] < H*squareSize)
4317 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4319 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4323 static void BlankSquare(x, y, color, piece, dest, fac)
4324 int x, y, color, fac;
4327 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4329 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4330 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4331 squareSize, squareSize, x*fac, y*fac);
4333 if (useImages && useImageSqs) {
4337 pm = xpmLightSquare;
4342 case 2: /* neutral */
4347 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4348 squareSize, squareSize, x*fac, y*fac);
4358 case 2: /* neutral */
4363 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4368 I split out the routines to draw a piece so that I could
4369 make a generic flash routine.
4371 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4373 int square_color, x, y;
4376 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4377 switch (square_color) {
4379 case 2: /* neutral */
4381 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4382 ? *pieceToOutline(piece)
4383 : *pieceToSolid(piece),
4384 dest, bwPieceGC, 0, 0,
4385 squareSize, squareSize, x, y);
4388 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4389 ? *pieceToSolid(piece)
4390 : *pieceToOutline(piece),
4391 dest, wbPieceGC, 0, 0,
4392 squareSize, squareSize, x, y);
4397 static void monoDrawPiece(piece, square_color, x, y, dest)
4399 int square_color, x, y;
4402 switch (square_color) {
4404 case 2: /* neutral */
4406 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4407 ? *pieceToOutline(piece)
4408 : *pieceToSolid(piece),
4409 dest, bwPieceGC, 0, 0,
4410 squareSize, squareSize, x, y, 1);
4413 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4414 ? *pieceToSolid(piece)
4415 : *pieceToOutline(piece),
4416 dest, wbPieceGC, 0, 0,
4417 squareSize, squareSize, x, y, 1);
4422 static void colorDrawPiece(piece, square_color, x, y, dest)
4424 int square_color, x, y;
4427 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4428 switch (square_color) {
4430 XCopyPlane(xDisplay, *pieceToSolid(piece),
4431 dest, (int) piece < (int) BlackPawn
4432 ? wlPieceGC : blPieceGC, 0, 0,
4433 squareSize, squareSize, x, y, 1);
4436 XCopyPlane(xDisplay, *pieceToSolid(piece),
4437 dest, (int) piece < (int) BlackPawn
4438 ? wdPieceGC : bdPieceGC, 0, 0,
4439 squareSize, squareSize, x, y, 1);
4441 case 2: /* neutral */
4443 XCopyPlane(xDisplay, *pieceToSolid(piece),
4444 dest, (int) piece < (int) BlackPawn
4445 ? wjPieceGC : bjPieceGC, 0, 0,
4446 squareSize, squareSize, x, y, 1);
4451 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4453 int square_color, x, y;
4456 int kind, p = piece;
4458 switch (square_color) {
4460 case 2: /* neutral */
4462 if ((int)piece < (int) BlackPawn) {
4470 if ((int)piece < (int) BlackPawn) {
4478 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4479 if(useTexture & square_color+1) {
4480 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4481 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4482 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4483 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4484 XSetClipMask(xDisplay, wlPieceGC, None);
4485 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4487 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4488 dest, wlPieceGC, 0, 0,
4489 squareSize, squareSize, x, y);
4492 typedef void (*DrawFunc)();
4494 DrawFunc ChooseDrawFunc()
4496 if (appData.monoMode) {
4497 if (DefaultDepth(xDisplay, xScreen) == 1) {
4498 return monoDrawPiece_1bit;
4500 return monoDrawPiece;
4504 return colorDrawPieceImage;
4506 return colorDrawPiece;
4510 /* [HR] determine square color depending on chess variant. */
4511 static int SquareColor(row, column)
4516 if (gameInfo.variant == VariantXiangqi) {
4517 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4519 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4521 } else if (row <= 4) {
4527 square_color = ((column + row) % 2) == 1;
4530 /* [hgm] holdings: next line makes all holdings squares light */
4531 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4533 return square_color;
4536 void DrawSquare(row, column, piece, do_flash)
4537 int row, column, do_flash;
4540 int square_color, x, y, direction, font_ascent, font_descent;
4543 XCharStruct overall;
4547 /* Calculate delay in milliseconds (2-delays per complete flash) */
4548 flash_delay = 500 / appData.flashRate;
4551 x = lineGap + ((BOARD_WIDTH-1)-column) *
4552 (squareSize + lineGap);
4553 y = lineGap + row * (squareSize + lineGap);
4555 x = lineGap + column * (squareSize + lineGap);
4556 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4557 (squareSize + lineGap);
4560 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4562 square_color = SquareColor(row, column);
4564 if ( // [HGM] holdings: blank out area between board and holdings
4565 column == BOARD_LEFT-1 || column == BOARD_RGHT
4566 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4567 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4568 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4570 // [HGM] print piece counts next to holdings
4571 string[1] = NULLCHAR;
4572 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4573 string[0] = '0' + piece;
4574 XTextExtents(countFontStruct, string, 1, &direction,
4575 &font_ascent, &font_descent, &overall);
4576 if (appData.monoMode) {
4577 XDrawImageString(xDisplay, xBoardWindow, countGC,
4578 x + squareSize - overall.width - 2,
4579 y + font_ascent + 1, string, 1);
4581 XDrawString(xDisplay, xBoardWindow, countGC,
4582 x + squareSize - overall.width - 2,
4583 y + font_ascent + 1, string, 1);
4586 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4587 string[0] = '0' + piece;
4588 XTextExtents(countFontStruct, string, 1, &direction,
4589 &font_ascent, &font_descent, &overall);
4590 if (appData.monoMode) {
4591 XDrawImageString(xDisplay, xBoardWindow, countGC,
4592 x + 2, y + font_ascent + 1, string, 1);
4594 XDrawString(xDisplay, xBoardWindow, countGC,
4595 x + 2, y + font_ascent + 1, string, 1);
4599 if (piece == EmptySquare || appData.blindfold) {
4600 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4602 drawfunc = ChooseDrawFunc();
4604 if (do_flash && appData.flashCount > 0) {
4605 for (i=0; i<appData.flashCount; ++i) {
4606 drawfunc(piece, square_color, x, y, xBoardWindow);
4607 XSync(xDisplay, False);
4608 do_flash_delay(flash_delay);
4610 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4611 XSync(xDisplay, False);
4612 do_flash_delay(flash_delay);
4615 drawfunc(piece, square_color, x, y, xBoardWindow);
4619 string[1] = NULLCHAR;
4620 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4621 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4622 string[0] = 'a' + column - BOARD_LEFT;
4623 XTextExtents(coordFontStruct, string, 1, &direction,
4624 &font_ascent, &font_descent, &overall);
4625 if (appData.monoMode) {
4626 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4627 x + squareSize - overall.width - 2,
4628 y + squareSize - font_descent - 1, string, 1);
4630 XDrawString(xDisplay, xBoardWindow, coordGC,
4631 x + squareSize - overall.width - 2,
4632 y + squareSize - font_descent - 1, string, 1);
4635 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4636 string[0] = ONE + row;
4637 XTextExtents(coordFontStruct, string, 1, &direction,
4638 &font_ascent, &font_descent, &overall);
4639 if (appData.monoMode) {
4640 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4641 x + 2, y + font_ascent + 1, string, 1);
4643 XDrawString(xDisplay, xBoardWindow, coordGC,
4644 x + 2, y + font_ascent + 1, string, 1);
4647 if(!partnerUp && marker[row][column]) {
4648 if(appData.monoMode) {
4649 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4650 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4651 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4652 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4654 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4655 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4660 /* Why is this needed on some versions of X? */
4661 void EventProc(widget, unused, event)
4666 if (!XtIsRealized(widget))
4669 switch (event->type) {
4671 if (event->xexpose.count > 0) return; /* no clipping is done */
4672 XDrawPosition(widget, True, NULL);
4673 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4674 flipView = !flipView; partnerUp = !partnerUp;
4675 XDrawPosition(widget, True, NULL);
4676 flipView = !flipView; partnerUp = !partnerUp;
4680 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4687 void DrawPosition(fullRedraw, board)
4688 /*Boolean*/int fullRedraw;
4691 XDrawPosition(boardWidget, fullRedraw, board);
4694 /* Returns 1 if there are "too many" differences between b1 and b2
4695 (i.e. more than 1 move was made) */
4696 static int too_many_diffs(b1, b2)
4702 for (i=0; i<BOARD_HEIGHT; ++i) {
4703 for (j=0; j<BOARD_WIDTH; ++j) {
4704 if (b1[i][j] != b2[i][j]) {
4705 if (++c > 4) /* Castling causes 4 diffs */
4713 /* Matrix describing castling maneuvers */
4714 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4715 static int castling_matrix[4][5] = {
4716 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4717 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4718 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4719 { 7, 7, 4, 5, 6 } /* 0-0, black */
4722 /* Checks whether castling occurred. If it did, *rrow and *rcol
4723 are set to the destination (row,col) of the rook that moved.
4725 Returns 1 if castling occurred, 0 if not.
4727 Note: Only handles a max of 1 castling move, so be sure
4728 to call too_many_diffs() first.
4730 static int check_castle_draw(newb, oldb, rrow, rcol)
4737 /* For each type of castling... */
4738 for (i=0; i<4; ++i) {
4739 r = castling_matrix[i];
4741 /* Check the 4 squares involved in the castling move */
4743 for (j=1; j<=4; ++j) {
4744 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4751 /* All 4 changed, so it must be a castling move */
4760 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4761 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4763 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4766 void DrawSeekBackground( int left, int top, int right, int bottom )
4768 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4771 void DrawSeekText(char *buf, int x, int y)
4773 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4776 void DrawSeekDot(int x, int y, int colorNr)
4778 int square = colorNr & 0x80;
4781 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4783 XFillRectangle(xDisplay, xBoardWindow, color,
4784 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4786 XFillArc(xDisplay, xBoardWindow, color,
4787 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4790 static int damage[2][BOARD_RANKS][BOARD_FILES];
4793 * event handler for redrawing the board
4795 void XDrawPosition(w, repaint, board)
4797 /*Boolean*/int repaint;
4801 static int lastFlipView = 0;
4802 static int lastBoardValid[2] = {0, 0};
4803 static Board lastBoard[2];
4806 int nr = twoBoards*partnerUp;
4808 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4810 if (board == NULL) {
4811 if (!lastBoardValid[nr]) return;
4812 board = lastBoard[nr];
4814 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4815 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4816 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4821 * It would be simpler to clear the window with XClearWindow()
4822 * but this causes a very distracting flicker.
4825 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4827 if ( lineGap && IsDrawArrowEnabled())
4828 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4829 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4831 /* If too much changes (begin observing new game, etc.), don't
4833 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4835 /* Special check for castling so we don't flash both the king
4836 and the rook (just flash the king). */
4838 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4839 /* Draw rook with NO flashing. King will be drawn flashing later */
4840 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4841 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4845 /* First pass -- Draw (newly) empty squares and repair damage.
4846 This prevents you from having a piece show up twice while it
4847 is flashing on its new square */
4848 for (i = 0; i < BOARD_HEIGHT; i++)
4849 for (j = 0; j < BOARD_WIDTH; j++)
4850 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4851 || damage[nr][i][j]) {
4852 DrawSquare(i, j, board[i][j], 0);
4853 damage[nr][i][j] = False;
4856 /* Second pass -- Draw piece(s) in new position and flash them */
4857 for (i = 0; i < BOARD_HEIGHT; i++)
4858 for (j = 0; j < BOARD_WIDTH; j++)
4859 if (board[i][j] != lastBoard[nr][i][j]) {
4860 DrawSquare(i, j, board[i][j], do_flash);
4864 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4865 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4866 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4868 for (i = 0; i < BOARD_HEIGHT; i++)
4869 for (j = 0; j < BOARD_WIDTH; j++) {
4870 DrawSquare(i, j, board[i][j], 0);
4871 damage[nr][i][j] = False;
4875 CopyBoard(lastBoard[nr], board);
4876 lastBoardValid[nr] = 1;
4877 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4878 lastFlipView = flipView;
4880 /* Draw highlights */
4881 if (pm1X >= 0 && pm1Y >= 0) {
4882 drawHighlight(pm1X, pm1Y, prelineGC);
4884 if (pm2X >= 0 && pm2Y >= 0) {
4885 drawHighlight(pm2X, pm2Y, prelineGC);
4887 if (hi1X >= 0 && hi1Y >= 0) {
4888 drawHighlight(hi1X, hi1Y, highlineGC);
4890 if (hi2X >= 0 && hi2Y >= 0) {
4891 drawHighlight(hi2X, hi2Y, highlineGC);
4893 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4895 /* If piece being dragged around board, must redraw that too */
4898 XSync(xDisplay, False);
4903 * event handler for redrawing the board
4905 void DrawPositionProc(w, event, prms, nprms)
4911 XDrawPosition(w, True, NULL);
4916 * event handler for parsing user moves
4918 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4919 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4920 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4921 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4922 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4923 // and at the end FinishMove() to perform the move after optional promotion popups.
4924 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4925 void HandleUserMove(w, event, prms, nprms)
4931 if (w != boardWidget || errorExitStatus != -1) return;
4932 if(nprms) shiftKey = !strcmp(prms[0], "1");
4935 if (event->type == ButtonPress) {
4936 XtPopdown(promotionShell);
4937 XtDestroyWidget(promotionShell);
4938 promotionUp = False;
4946 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4947 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4948 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4951 void AnimateUserMove (Widget w, XEvent * event,
4952 String * params, Cardinal * nParams)
4954 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4955 DragPieceMove(event->xmotion.x, event->xmotion.y);
4958 void HandlePV (Widget w, XEvent * event,
4959 String * params, Cardinal * nParams)
4960 { // [HGM] pv: walk PV
4961 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4964 static int savedIndex; /* gross that this is global */
4966 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4969 XawTextPosition index, dummy;
4972 XawTextGetSelectionPos(w, &index, &dummy);
4973 XtSetArg(arg, XtNstring, &val);
4974 XtGetValues(w, &arg, 1);
4975 ReplaceComment(savedIndex, val);
4976 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4977 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4980 void EditCommentPopUp(index, title, text)
4985 if (text == NULL) text = "";
4986 NewCommentPopup(title, text, index);
4989 void ICSInputBoxPopUp()
4994 extern Option boxOptions[];
4996 void ICSInputSendText()
5003 edit = boxOptions[0].handle;
5005 XtSetArg(args[j], XtNstring, &val); j++;
5006 XtGetValues(edit, args, j);
5008 SendMultiLineToICS(val);
5009 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5010 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5013 void ICSInputBoxPopDown()
5018 void CommentPopUp(title, text)
5021 savedIndex = currentMove; // [HGM] vari
5022 NewCommentPopup(title, text, currentMove);
5025 void CommentPopDown()
5030 static char *openName;
5035 (void) (*fileProc)(openFP, 0, openName);
5038 void FileNamePopUp(label, def, filter, proc, openMode)
5045 fileProc = proc; /* I can't see a way not */
5046 fileOpenMode = openMode; /* to use globals here */
5047 { // [HGM] use file-selector dialog stolen from Ghostview
5048 int index; // this is not supported yet
5049 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5050 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5051 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5052 ScheduleDelayedEvent(&DelayedLoad, 50);
5056 void FileNamePopDown()
5058 if (!filenameUp) return;
5059 XtPopdown(fileNameShell);
5060 XtDestroyWidget(fileNameShell);
5065 void FileNameCallback(w, client_data, call_data)
5067 XtPointer client_data, call_data;
5072 XtSetArg(args[0], XtNlabel, &name);
5073 XtGetValues(w, args, 1);
5075 if (strcmp(name, _("cancel")) == 0) {
5080 FileNameAction(w, NULL, NULL, NULL);
5083 void FileNameAction(w, event, prms, nprms)
5095 name = XawDialogGetValueString(w = XtParent(w));
5097 if ((name != NULL) && (*name != NULLCHAR)) {
5098 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5099 XtPopdown(w = XtParent(XtParent(w)));
5103 p = strrchr(buf, ' ');
5110 fullname = ExpandPathName(buf);
5112 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5115 f = fopen(fullname, fileOpenMode);
5117 DisplayError(_("Failed to open file"), errno);
5119 (void) (*fileProc)(f, index, buf);
5126 XtPopdown(w = XtParent(XtParent(w)));
5132 void PromotionPopUp()
5135 Widget dialog, layout;
5137 Dimension bw_width, pw_width;
5139 char *PromoChars = "wglcqrbnkac+=\0";
5142 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5143 XtGetValues(boardWidget, args, j);
5146 XtSetArg(args[j], XtNresizable, True); j++;
5147 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5149 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5150 shellWidget, args, j);
5152 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5153 layoutArgs, XtNumber(layoutArgs));
5156 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5157 XtSetArg(args[j], XtNborderWidth, 0); j++;
5158 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5161 if(gameInfo.variant != VariantShogi) {
5162 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5163 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5164 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5165 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5166 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5168 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5169 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5170 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5171 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5173 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5174 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5175 gameInfo.variant == VariantGiveaway) {
5176 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5178 if(gameInfo.variant == VariantCapablanca ||
5179 gameInfo.variant == VariantGothic ||
5180 gameInfo.variant == VariantCapaRandom) {
5181 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5182 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5184 } else // [HGM] shogi
5186 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5187 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5189 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5191 XtRealizeWidget(promotionShell);
5192 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5195 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5196 XtGetValues(promotionShell, args, j);
5198 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5199 lineGap + squareSize/3 +
5200 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5201 0 : 6*(squareSize + lineGap)), &x, &y);
5204 XtSetArg(args[j], XtNx, x); j++;
5205 XtSetArg(args[j], XtNy, y); j++;
5206 XtSetValues(promotionShell, args, j);
5208 XtPopup(promotionShell, XtGrabNone);
5213 void PromotionPopDown()
5215 if (!promotionUp) return;
5216 XtPopdown(promotionShell);
5217 XtDestroyWidget(promotionShell);
5218 promotionUp = False;
5221 void PromotionCallback(w, client_data, call_data)
5223 XtPointer client_data, call_data;
5225 int promoChar = * (const char *) client_data;
5229 if (fromX == -1) return;
5236 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5238 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5239 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5244 void ErrorCallback(w, client_data, call_data)
5246 XtPointer client_data, call_data;
5249 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5251 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5257 if (!errorUp) return;
5259 XtPopdown(errorShell);
5260 XtDestroyWidget(errorShell);
5261 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5264 void ErrorPopUp(title, label, modal)
5265 char *title, *label;
5269 Widget dialog, layout;
5273 Dimension bw_width, pw_width;
5274 Dimension pw_height;
5278 XtSetArg(args[i], XtNresizable, True); i++;
5279 XtSetArg(args[i], XtNtitle, title); i++;
5281 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5282 shellWidget, args, i);
5284 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5285 layoutArgs, XtNumber(layoutArgs));
5288 XtSetArg(args[i], XtNlabel, label); i++;
5289 XtSetArg(args[i], XtNborderWidth, 0); i++;
5290 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5293 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5295 XtRealizeWidget(errorShell);
5296 CatchDeleteWindow(errorShell, "ErrorPopDown");
5299 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5300 XtGetValues(boardWidget, args, i);
5302 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5303 XtSetArg(args[i], XtNheight, &pw_height); i++;
5304 XtGetValues(errorShell, args, i);
5307 /* This code seems to tickle an X bug if it is executed too soon
5308 after xboard starts up. The coordinates get transformed as if
5309 the main window was positioned at (0, 0).
5311 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5312 0 - pw_height + squareSize / 3, &x, &y);
5314 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5315 RootWindowOfScreen(XtScreen(boardWidget)),
5316 (bw_width - pw_width) / 2,
5317 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5321 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5324 XtSetArg(args[i], XtNx, x); i++;
5325 XtSetArg(args[i], XtNy, y); i++;
5326 XtSetValues(errorShell, args, i);
5329 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5332 /* Disable all user input other than deleting the window */
5333 static int frozen = 0;
5337 /* Grab by a widget that doesn't accept input */
5338 XtAddGrab(messageWidget, TRUE, FALSE);
5342 /* Undo a FreezeUI */
5345 if (!frozen) return;
5346 XtRemoveGrab(messageWidget);
5350 char *ModeToWidgetName(mode)
5354 case BeginningOfGame:
5355 if (appData.icsActive)
5356 return "menuMode.ICS Client";
5357 else if (appData.noChessProgram ||
5358 *appData.cmailGameName != NULLCHAR)
5359 return "menuMode.Edit Game";
5361 return "menuMode.Machine Black";
5362 case MachinePlaysBlack:
5363 return "menuMode.Machine Black";
5364 case MachinePlaysWhite:
5365 return "menuMode.Machine White";
5367 return "menuMode.Analysis Mode";
5369 return "menuMode.Analyze File";
5370 case TwoMachinesPlay:
5371 return "menuMode.Two Machines";
5373 return "menuMode.Edit Game";
5374 case PlayFromGameFile:
5375 return "menuFile.Load Game";
5377 return "menuMode.Edit Position";
5379 return "menuMode.Training";
5380 case IcsPlayingWhite:
5381 case IcsPlayingBlack:
5385 return "menuMode.ICS Client";
5392 void ModeHighlight()
5395 static int oldPausing = FALSE;
5396 static GameMode oldmode = (GameMode) -1;
5399 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5401 if (pausing != oldPausing) {
5402 oldPausing = pausing;
5404 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5406 XtSetArg(args[0], XtNleftBitmap, None);
5408 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5411 if (appData.showButtonBar) {
5412 /* Always toggle, don't set. Previous code messes up when
5413 invoked while the button is pressed, as releasing it
5414 toggles the state again. */
5417 XtSetArg(args[0], XtNbackground, &oldbg);
5418 XtSetArg(args[1], XtNforeground, &oldfg);
5419 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5421 XtSetArg(args[0], XtNbackground, oldfg);
5422 XtSetArg(args[1], XtNforeground, oldbg);
5424 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5428 wname = ModeToWidgetName(oldmode);
5429 if (wname != NULL) {
5430 XtSetArg(args[0], XtNleftBitmap, None);
5431 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5433 wname = ModeToWidgetName(gameMode);
5434 if (wname != NULL) {
5435 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5436 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5439 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5440 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5442 /* Maybe all the enables should be handled here, not just this one */
5443 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5444 gameMode == Training || gameMode == PlayFromGameFile);
5449 * Button/menu procedures
5451 void ResetProc(w, event, prms, nprms)
5460 int LoadGamePopUp(f, gameNumber, title)
5465 cmailMsgLoaded = FALSE;
5466 if (gameNumber == 0) {
5467 int error = GameListBuild(f);
5469 DisplayError(_("Cannot build game list"), error);
5470 } else if (!ListEmpty(&gameList) &&
5471 ((ListGame *) gameList.tailPred)->number > 1) {
5472 GameListPopUp(f, title);
5478 return LoadGame(f, gameNumber, title, FALSE);
5481 void LoadGameProc(w, event, prms, nprms)
5487 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5490 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5493 void LoadNextGameProc(w, event, prms, nprms)
5502 void LoadPrevGameProc(w, event, prms, nprms)
5511 void ReloadGameProc(w, event, prms, nprms)
5520 void LoadNextPositionProc(w, event, prms, nprms)
5529 void LoadPrevPositionProc(w, event, prms, nprms)
5538 void ReloadPositionProc(w, event, prms, nprms)
5547 void LoadPositionProc(w, event, prms, nprms)
5553 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5556 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5559 void SaveGameProc(w, event, prms, nprms)
5565 FileNamePopUp(_("Save game file name?"),
5566 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5567 appData.oldSaveStyle ? ".game" : ".pgn",
5571 void SavePositionProc(w, event, prms, nprms)
5577 FileNamePopUp(_("Save position file name?"),
5578 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5579 appData.oldSaveStyle ? ".pos" : ".fen",
5583 void ReloadCmailMsgProc(w, event, prms, nprms)
5589 ReloadCmailMsgEvent(FALSE);
5592 void MailMoveProc(w, event, prms, nprms)
5601 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5602 char *selected_fen_position=NULL;
5605 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5606 Atom *type_return, XtPointer *value_return,
5607 unsigned long *length_return, int *format_return)
5609 char *selection_tmp;
5611 if (!selected_fen_position) return False; /* should never happen */
5612 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5613 /* note: since no XtSelectionDoneProc was registered, Xt will
5614 * automatically call XtFree on the value returned. So have to
5615 * make a copy of it allocated with XtMalloc */
5616 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5617 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5619 *value_return=selection_tmp;
5620 *length_return=strlen(selection_tmp);
5621 *type_return=*target;
5622 *format_return = 8; /* bits per byte */
5624 } else if (*target == XA_TARGETS(xDisplay)) {
5625 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5626 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5627 targets_tmp[1] = XA_STRING;
5628 *value_return = targets_tmp;
5629 *type_return = XA_ATOM;
5632 // This code leads to a read of value_return out of bounds on 64-bit systems.
5633 // Other code which I have seen always sets *format_return to 32 independent of
5634 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5635 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5636 *format_return = 8 * sizeof(Atom);
5637 if (*format_return > 32) {
5638 *length_return *= *format_return / 32;
5639 *format_return = 32;
5642 *format_return = 32;
5650 /* note: when called from menu all parameters are NULL, so no clue what the
5651 * Widget which was clicked on was, or what the click event was
5653 void CopyPositionProc(w, event, prms, nprms)
5660 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5661 * have a notion of a position that is selected but not copied.
5662 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5664 if(gameMode == EditPosition) EditPositionDone(TRUE);
5665 if (selected_fen_position) free(selected_fen_position);
5666 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5667 if (!selected_fen_position) return;
5668 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5670 SendPositionSelection,
5671 NULL/* lose_ownership_proc */ ,
5672 NULL/* transfer_done_proc */);
5673 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5675 SendPositionSelection,
5676 NULL/* lose_ownership_proc */ ,
5677 NULL/* transfer_done_proc */);
5680 /* function called when the data to Paste is ready */
5682 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5683 Atom *type, XtPointer value, unsigned long *len, int *format)
5686 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5687 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5688 EditPositionPasteFEN(fenstr);
5692 /* called when Paste Position button is pressed,
5693 * all parameters will be NULL */
5694 void PastePositionProc(w, event, prms, nprms)
5700 XtGetSelectionValue(menuBarWidget,
5701 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5702 /* (XtSelectionCallbackProc) */ PastePositionCB,
5703 NULL, /* client_data passed to PastePositionCB */
5705 /* better to use the time field from the event that triggered the
5706 * call to this function, but that isn't trivial to get
5714 SendGameSelection(Widget w, Atom *selection, Atom *target,
5715 Atom *type_return, XtPointer *value_return,
5716 unsigned long *length_return, int *format_return)
5718 char *selection_tmp;
5720 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5721 FILE* f = fopen(gameCopyFilename, "r");
5724 if (f == NULL) return False;
5728 selection_tmp = XtMalloc(len + 1);
5729 count = fread(selection_tmp, 1, len, f);
5732 XtFree(selection_tmp);
5735 selection_tmp[len] = NULLCHAR;
5736 *value_return = selection_tmp;
5737 *length_return = len;
5738 *type_return = *target;
5739 *format_return = 8; /* bits per byte */
5741 } else if (*target == XA_TARGETS(xDisplay)) {
5742 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5743 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5744 targets_tmp[1] = XA_STRING;
5745 *value_return = targets_tmp;
5746 *type_return = XA_ATOM;
5749 // This code leads to a read of value_return out of bounds on 64-bit systems.
5750 // Other code which I have seen always sets *format_return to 32 independent of
5751 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5752 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5753 *format_return = 8 * sizeof(Atom);
5754 if (*format_return > 32) {
5755 *length_return *= *format_return / 32;
5756 *format_return = 32;
5759 *format_return = 32;
5767 void CopySomething()
5770 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5771 * have a notion of a game that is selected but not copied.
5772 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5774 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5777 NULL/* lose_ownership_proc */ ,
5778 NULL/* transfer_done_proc */);
5779 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5782 NULL/* lose_ownership_proc */ ,
5783 NULL/* transfer_done_proc */);
5786 /* note: when called from menu all parameters are NULL, so no clue what the
5787 * Widget which was clicked on was, or what the click event was
5789 void CopyGameProc(w, event, prms, nprms)
5797 ret = SaveGameToFile(gameCopyFilename, FALSE);
5803 void CopyGameListProc(w, event, prms, nprms)
5809 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5813 /* function called when the data to Paste is ready */
5815 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5816 Atom *type, XtPointer value, unsigned long *len, int *format)
5819 if (value == NULL || *len == 0) {
5820 return; /* nothing had been selected to copy */
5822 f = fopen(gamePasteFilename, "w");
5824 DisplayError(_("Can't open temp file"), errno);
5827 fwrite(value, 1, *len, f);
5830 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5833 /* called when Paste Game button is pressed,
5834 * all parameters will be NULL */
5835 void PasteGameProc(w, event, prms, nprms)
5841 XtGetSelectionValue(menuBarWidget,
5842 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5843 /* (XtSelectionCallbackProc) */ PasteGameCB,
5844 NULL, /* client_data passed to PasteGameCB */
5846 /* better to use the time field from the event that triggered the
5847 * call to this function, but that isn't trivial to get
5857 SaveGameProc(NULL, NULL, NULL, NULL);
5861 void QuitProc(w, event, prms, nprms)
5870 void PauseProc(w, event, prms, nprms)
5880 void MachineBlackProc(w, event, prms, nprms)
5886 MachineBlackEvent();
5889 void MachineWhiteProc(w, event, prms, nprms)
5895 MachineWhiteEvent();
5898 void AnalyzeModeProc(w, event, prms, nprms)
5906 if (!first.analysisSupport) {
5907 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5908 DisplayError(buf, 0);
5911 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5912 if (appData.icsActive) {
5913 if (gameMode != IcsObserving) {
5914 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5915 DisplayError(buf, 0);
5917 if (appData.icsEngineAnalyze) {
5918 if (appData.debugMode)
5919 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5925 /* if enable, use want disable icsEngineAnalyze */
5926 if (appData.icsEngineAnalyze) {
5931 appData.icsEngineAnalyze = TRUE;
5932 if (appData.debugMode)
5933 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5935 #ifndef OPTIONSDIALOG
5936 if (!appData.showThinking)
5937 ShowThinkingProc(w,event,prms,nprms);
5943 void AnalyzeFileProc(w, event, prms, nprms)
5949 if (!first.analysisSupport) {
5951 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5952 DisplayError(buf, 0);
5955 // Reset(FALSE, TRUE);
5956 #ifndef OPTIONSDIALOG
5957 if (!appData.showThinking)
5958 ShowThinkingProc(w,event,prms,nprms);
5961 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5962 AnalysisPeriodicEvent(1);
5965 void TwoMachinesProc(w, event, prms, nprms)
5974 void MatchProc(w, event, prms, nprms)
5983 void IcsClientProc(w, event, prms, nprms)
5992 void EditGameProc(w, event, prms, nprms)
6001 void EditPositionProc(w, event, prms, nprms)
6007 EditPositionEvent();
6010 void TrainingProc(w, event, prms, nprms)
6019 void EditCommentProc(w, event, prms, nprms)
6027 if (PopDown(1)) { // popdown succesful
6029 XtSetArg(args[j], XtNleftBitmap, None); j++;
6030 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6031 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6032 } else // was not up
6036 void IcsInputBoxProc(w, event, prms, nprms)
6042 if (!PopDown(4)) ICSInputBoxPopUp();
6045 void AcceptProc(w, event, prms, nprms)
6054 void DeclineProc(w, event, prms, nprms)
6063 void RematchProc(w, event, prms, nprms)
6072 void CallFlagProc(w, event, prms, nprms)
6081 void DrawProc(w, event, prms, nprms)
6090 void AbortProc(w, event, prms, nprms)
6099 void AdjournProc(w, event, prms, nprms)
6108 void ResignProc(w, event, prms, nprms)
6117 void AdjuWhiteProc(w, event, prms, nprms)
6123 UserAdjudicationEvent(+1);
6126 void AdjuBlackProc(w, event, prms, nprms)
6132 UserAdjudicationEvent(-1);
6135 void AdjuDrawProc(w, event, prms, nprms)
6141 UserAdjudicationEvent(0);
6144 void EnterKeyProc(w, event, prms, nprms)
6150 if (shellUp[4] == True)
6154 void UpKeyProc(w, event, prms, nprms)
6159 { // [HGM] input: let up-arrow recall previous line from history
6166 if (!shellUp[4]) return;
6167 edit = boxOptions[0].handle;
6169 XtSetArg(args[j], XtNstring, &val); j++;
6170 XtGetValues(edit, args, j);
6171 val = PrevInHistory(val);
6172 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6173 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6175 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6176 XawTextReplace(edit, 0, 0, &t);
6177 XawTextSetInsertionPoint(edit, 9999);
6181 void DownKeyProc(w, event, prms, nprms)
6186 { // [HGM] input: let down-arrow recall next line from history
6191 if (!shellUp[4]) return;
6192 edit = boxOptions[0].handle;
6193 val = NextInHistory();
6194 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6195 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6197 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6198 XawTextReplace(edit, 0, 0, &t);
6199 XawTextSetInsertionPoint(edit, 9999);
6203 void StopObservingProc(w, event, prms, nprms)
6209 StopObservingEvent();
6212 void StopExaminingProc(w, event, prms, nprms)
6218 StopExaminingEvent();
6221 void UploadProc(w, event, prms, nprms)
6231 void ForwardProc(w, event, prms, nprms)
6241 void BackwardProc(w, event, prms, nprms)
6250 void TempBackwardProc(w, event, prms, nprms)
6256 if (!TempBackwardActive) {
6257 TempBackwardActive = True;
6262 void TempForwardProc(w, event, prms, nprms)
6268 /* Check to see if triggered by a key release event for a repeating key.
6269 * If so the next queued event will be a key press of the same key at the same time */
6270 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6272 XPeekEvent(xDisplay, &next);
6273 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6274 next.xkey.keycode == event->xkey.keycode)
6278 TempBackwardActive = False;
6281 void ToStartProc(w, event, prms, nprms)
6290 void ToEndProc(w, event, prms, nprms)
6299 void RevertProc(w, event, prms, nprms)
6308 void AnnotateProc(w, event, prms, nprms)
6317 void TruncateGameProc(w, event, prms, nprms)
6323 TruncateGameEvent();
6325 void RetractMoveProc(w, event, prms, nprms)
6334 void MoveNowProc(w, event, prms, nprms)
6343 void FlipViewProc(w, event, prms, nprms)
6349 flipView = !flipView;
6350 DrawPosition(True, NULL);
6353 void PonderNextMoveProc(w, event, prms, nprms)
6361 PonderNextMoveEvent(!appData.ponderNextMove);
6362 #ifndef OPTIONSDIALOG
6363 if (appData.ponderNextMove) {
6364 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6366 XtSetArg(args[0], XtNleftBitmap, None);
6368 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6373 #ifndef OPTIONSDIALOG
6374 void AlwaysQueenProc(w, event, prms, nprms)
6382 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6384 if (appData.alwaysPromoteToQueen) {
6385 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6387 XtSetArg(args[0], XtNleftBitmap, None);
6389 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6393 void AnimateDraggingProc(w, event, prms, nprms)
6401 appData.animateDragging = !appData.animateDragging;
6403 if (appData.animateDragging) {
6404 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6407 XtSetArg(args[0], XtNleftBitmap, None);
6409 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6413 void AnimateMovingProc(w, event, prms, nprms)
6421 appData.animate = !appData.animate;
6423 if (appData.animate) {
6424 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6427 XtSetArg(args[0], XtNleftBitmap, None);
6429 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6433 void AutoflagProc(w, event, prms, nprms)
6441 appData.autoCallFlag = !appData.autoCallFlag;
6443 if (appData.autoCallFlag) {
6444 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6446 XtSetArg(args[0], XtNleftBitmap, None);
6448 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6452 void AutoflipProc(w, event, prms, nprms)
6460 appData.autoFlipView = !appData.autoFlipView;
6462 if (appData.autoFlipView) {
6463 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6465 XtSetArg(args[0], XtNleftBitmap, None);
6467 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6471 void BlindfoldProc(w, event, prms, nprms)
6479 appData.blindfold = !appData.blindfold;
6481 if (appData.blindfold) {
6482 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6484 XtSetArg(args[0], XtNleftBitmap, None);
6486 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6489 DrawPosition(True, NULL);
6492 void TestLegalityProc(w, event, prms, nprms)
6500 appData.testLegality = !appData.testLegality;
6502 if (appData.testLegality) {
6503 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6505 XtSetArg(args[0], XtNleftBitmap, None);
6507 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6512 void FlashMovesProc(w, event, prms, nprms)
6520 if (appData.flashCount == 0) {
6521 appData.flashCount = 3;
6523 appData.flashCount = -appData.flashCount;
6526 if (appData.flashCount > 0) {
6527 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6529 XtSetArg(args[0], XtNleftBitmap, None);
6531 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6536 void HighlightDraggingProc(w, event, prms, nprms)
6544 appData.highlightDragging = !appData.highlightDragging;
6546 if (appData.highlightDragging) {
6547 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6549 XtSetArg(args[0], XtNleftBitmap, None);
6551 XtSetValues(XtNameToWidget(menuBarWidget,
6552 "menuOptions.Highlight Dragging"), args, 1);
6556 void HighlightLastMoveProc(w, event, prms, nprms)
6564 appData.highlightLastMove = !appData.highlightLastMove;
6566 if (appData.highlightLastMove) {
6567 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6569 XtSetArg(args[0], XtNleftBitmap, None);
6571 XtSetValues(XtNameToWidget(menuBarWidget,
6572 "menuOptions.Highlight Last Move"), args, 1);
6575 void HighlightArrowProc(w, event, prms, nprms)
6583 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6585 if (appData.highlightMoveWithArrow) {
6586 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6588 XtSetArg(args[0], XtNleftBitmap, None);
6590 XtSetValues(XtNameToWidget(menuBarWidget,
6591 "menuOptions.Arrow"), args, 1);
6595 void IcsAlarmProc(w, event, prms, nprms)
6603 appData.icsAlarm = !appData.icsAlarm;
6605 if (appData.icsAlarm) {
6606 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6608 XtSetArg(args[0], XtNleftBitmap, None);
6610 XtSetValues(XtNameToWidget(menuBarWidget,
6611 "menuOptions.ICS Alarm"), args, 1);
6615 void MoveSoundProc(w, event, prms, nprms)
6623 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6625 if (appData.ringBellAfterMoves) {
6626 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6628 XtSetArg(args[0], XtNleftBitmap, None);
6630 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6634 void OneClickProc(w, event, prms, nprms)
6642 appData.oneClick = !appData.oneClick;
6644 if (appData.oneClick) {
6645 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6647 XtSetArg(args[0], XtNleftBitmap, None);
6649 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6653 void PeriodicUpdatesProc(w, event, prms, nprms)
6661 PeriodicUpdatesEvent(!appData.periodicUpdates);
6663 if (appData.periodicUpdates) {
6664 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6666 XtSetArg(args[0], XtNleftBitmap, None);
6668 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6672 void PopupExitMessageProc(w, event, prms, nprms)
6680 appData.popupExitMessage = !appData.popupExitMessage;
6682 if (appData.popupExitMessage) {
6683 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6685 XtSetArg(args[0], XtNleftBitmap, None);
6687 XtSetValues(XtNameToWidget(menuBarWidget,
6688 "menuOptions.Popup Exit Message"), args, 1);
6691 void PopupMoveErrorsProc(w, event, prms, nprms)
6699 appData.popupMoveErrors = !appData.popupMoveErrors;
6701 if (appData.popupMoveErrors) {
6702 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6704 XtSetArg(args[0], XtNleftBitmap, None);
6706 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6711 void PremoveProc(w, event, prms, nprms)
6719 appData.premove = !appData.premove;
6721 if (appData.premove) {
6722 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6724 XtSetArg(args[0], XtNleftBitmap, None);
6726 XtSetValues(XtNameToWidget(menuBarWidget,
6727 "menuOptions.Premove"), args, 1);
6731 void ShowCoordsProc(w, event, prms, nprms)
6739 appData.showCoords = !appData.showCoords;
6741 if (appData.showCoords) {
6742 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6744 XtSetArg(args[0], XtNleftBitmap, None);
6746 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6749 DrawPosition(True, NULL);
6752 void ShowThinkingProc(w, event, prms, nprms)
6758 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6759 ShowThinkingEvent();
6762 void HideThinkingProc(w, event, prms, nprms)
6770 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6771 ShowThinkingEvent();
6773 if (appData.hideThinkingFromHuman) {
6774 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6776 XtSetArg(args[0], XtNleftBitmap, None);
6778 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6783 void SaveOnExitProc(w, event, prms, nprms)
6791 saveSettingsOnExit = !saveSettingsOnExit;
6793 if (saveSettingsOnExit) {
6794 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6796 XtSetArg(args[0], XtNleftBitmap, None);
6798 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6802 void SaveSettingsProc(w, event, prms, nprms)
6808 SaveSettings(settingsFileName);
6811 void InfoProc(w, event, prms, nprms)
6818 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6823 void ManProc(w, event, prms, nprms)
6831 if (nprms && *nprms > 0)
6835 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6839 void HintProc(w, event, prms, nprms)
6848 void BookProc(w, event, prms, nprms)
6857 void AboutProc(w, event, prms, nprms)
6865 char *zippy = _(" (with Zippy code)");
6869 snprintf(buf, sizeof(buf),
6871 "Copyright 1991 Digital Equipment Corporation\n"
6872 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6873 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6874 "%s is free software and carries NO WARRANTY;"
6875 "see the file COPYING for more information."),
6876 programVersion, zippy, PACKAGE);
6877 ErrorPopUp(_("About XBoard"), buf, FALSE);
6880 void DebugProc(w, event, prms, nprms)
6886 appData.debugMode = !appData.debugMode;
6889 void AboutGameProc(w, event, prms, nprms)
6898 void NothingProc(w, event, prms, nprms)
6907 void DisplayMessage(message, extMessage)
6908 char *message, *extMessage;
6910 /* display a message in the message widget */
6919 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6924 message = extMessage;
6928 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6930 /* need to test if messageWidget already exists, since this function
6931 can also be called during the startup, if for example a Xresource
6932 is not set up correctly */
6935 XtSetArg(arg, XtNlabel, message);
6936 XtSetValues(messageWidget, &arg, 1);
6942 void DisplayTitle(text)
6947 char title[MSG_SIZ];
6950 if (text == NULL) text = "";
6952 if (appData.titleInWindow) {
6954 XtSetArg(args[i], XtNlabel, text); i++;
6955 XtSetValues(titleWidget, args, i);
6958 if (*text != NULLCHAR) {
6959 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6960 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6961 } else if (appData.icsActive) {
6962 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6963 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6964 } else if (appData.cmailGameName[0] != NULLCHAR) {
6965 snprintf(icon, sizeof(icon), "%s", "CMail");
6966 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6968 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6969 } else if (gameInfo.variant == VariantGothic) {
6970 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6971 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6974 } else if (gameInfo.variant == VariantFalcon) {
6975 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6976 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6978 } else if (appData.noChessProgram) {
6979 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6980 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6982 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6983 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6986 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6987 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6988 XtSetValues(shellWidget, args, i);
6989 XSync(xDisplay, False);
6994 DisplayError(message, error)
7001 if (appData.debugMode || appData.matchMode) {
7002 fprintf(stderr, "%s: %s\n", programName, message);
7005 if (appData.debugMode || appData.matchMode) {
7006 fprintf(stderr, "%s: %s: %s\n",
7007 programName, message, strerror(error));
7009 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7012 ErrorPopUp(_("Error"), message, FALSE);
7016 void DisplayMoveError(message)
7021 DrawPosition(FALSE, NULL);
7022 if (appData.debugMode || appData.matchMode) {
7023 fprintf(stderr, "%s: %s\n", programName, message);
7025 if (appData.popupMoveErrors) {
7026 ErrorPopUp(_("Error"), message, FALSE);
7028 DisplayMessage(message, "");
7033 void DisplayFatalError(message, error, status)
7039 errorExitStatus = status;
7041 fprintf(stderr, "%s: %s\n", programName, message);
7043 fprintf(stderr, "%s: %s: %s\n",
7044 programName, message, strerror(error));
7045 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7048 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7049 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7055 void DisplayInformation(message)
7059 ErrorPopUp(_("Information"), message, TRUE);
7062 void DisplayNote(message)
7066 ErrorPopUp(_("Note"), message, FALSE);
7070 NullXErrorCheck(dpy, error_event)
7072 XErrorEvent *error_event;
7077 void DisplayIcsInteractionTitle(message)
7080 if (oldICSInteractionTitle == NULL) {
7081 /* Magic to find the old window title, adapted from vim */
7082 char *wina = getenv("WINDOWID");
7084 Window win = (Window) atoi(wina);
7085 Window root, parent, *children;
7086 unsigned int nchildren;
7087 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7089 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7090 if (!XQueryTree(xDisplay, win, &root, &parent,
7091 &children, &nchildren)) break;
7092 if (children) XFree((void *)children);
7093 if (parent == root || parent == 0) break;
7096 XSetErrorHandler(oldHandler);
7098 if (oldICSInteractionTitle == NULL) {
7099 oldICSInteractionTitle = "xterm";
7102 printf("\033]0;%s\007", message);
7106 char pendingReplyPrefix[MSG_SIZ];
7107 ProcRef pendingReplyPR;
7109 void AskQuestionProc(w, event, prms, nprms)
7116 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7120 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7123 void AskQuestionPopDown()
7125 if (!askQuestionUp) return;
7126 XtPopdown(askQuestionShell);
7127 XtDestroyWidget(askQuestionShell);
7128 askQuestionUp = False;
7131 void AskQuestionReplyAction(w, event, prms, nprms)
7141 reply = XawDialogGetValueString(w = XtParent(w));
7142 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7143 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7144 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7145 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7146 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7147 AskQuestionPopDown();
7149 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7152 void AskQuestionCallback(w, client_data, call_data)
7154 XtPointer client_data, call_data;
7159 XtSetArg(args[0], XtNlabel, &name);
7160 XtGetValues(w, args, 1);
7162 if (strcmp(name, _("cancel")) == 0) {
7163 AskQuestionPopDown();
7165 AskQuestionReplyAction(w, NULL, NULL, NULL);
7169 void AskQuestion(title, question, replyPrefix, pr)
7170 char *title, *question, *replyPrefix;
7174 Widget popup, layout, dialog, edit;
7180 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7181 pendingReplyPR = pr;
7184 XtSetArg(args[i], XtNresizable, True); i++;
7185 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7186 askQuestionShell = popup =
7187 XtCreatePopupShell(title, transientShellWidgetClass,
7188 shellWidget, args, i);
7191 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7192 layoutArgs, XtNumber(layoutArgs));
7195 XtSetArg(args[i], XtNlabel, question); i++;
7196 XtSetArg(args[i], XtNvalue, ""); i++;
7197 XtSetArg(args[i], XtNborderWidth, 0); i++;
7198 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7201 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7202 (XtPointer) dialog);
7203 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7204 (XtPointer) dialog);
7206 XtRealizeWidget(popup);
7207 CatchDeleteWindow(popup, "AskQuestionPopDown");
7209 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7210 &x, &y, &win_x, &win_y, &mask);
7212 XtSetArg(args[0], XtNx, x - 10);
7213 XtSetArg(args[1], XtNy, y - 30);
7214 XtSetValues(popup, args, 2);
7216 XtPopup(popup, XtGrabExclusive);
7217 askQuestionUp = True;
7219 edit = XtNameToWidget(dialog, "*value");
7220 XtSetKeyboardFocus(popup, edit);
7228 if (*name == NULLCHAR) {
7230 } else if (strcmp(name, "$") == 0) {
7231 putc(BELLCHAR, stderr);
7234 char *prefix = "", *sep = "";
7235 if(appData.soundProgram[0] == NULLCHAR) return;
7236 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7237 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7245 PlaySound(appData.soundMove);
7251 PlaySound(appData.soundIcsWin);
7257 PlaySound(appData.soundIcsLoss);
7263 PlaySound(appData.soundIcsDraw);
7267 PlayIcsUnfinishedSound()
7269 PlaySound(appData.soundIcsUnfinished);
7275 PlaySound(appData.soundIcsAlarm);
7281 PlaySound(appData.soundTell);
7287 system("stty echo");
7294 system("stty -echo");
7299 RunCommand(char *buf)
7305 Colorize(cc, continuation)
7310 int count, outCount, error;
7312 if (textColors[(int)cc].bg > 0) {
7313 if (textColors[(int)cc].fg > 0) {
7314 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7315 textColors[(int)cc].fg, textColors[(int)cc].bg);
7317 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7318 textColors[(int)cc].bg);
7321 if (textColors[(int)cc].fg > 0) {
7322 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7323 textColors[(int)cc].fg);
7325 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7328 count = strlen(buf);
7329 outCount = OutputToProcess(NoProc, buf, count, &error);
7330 if (outCount < count) {
7331 DisplayFatalError(_("Error writing to display"), error, 1);
7334 if (continuation) return;
7337 PlaySound(appData.soundShout);
7340 PlaySound(appData.soundSShout);
7343 PlaySound(appData.soundChannel1);
7346 PlaySound(appData.soundChannel);
7349 PlaySound(appData.soundKibitz);
7352 PlaySound(appData.soundTell);
7354 case ColorChallenge:
7355 PlaySound(appData.soundChallenge);
7358 PlaySound(appData.soundRequest);
7361 PlaySound(appData.soundSeek);
7372 return getpwuid(getuid())->pw_name;
7376 ExpandPathName(path)
7379 static char static_buf[4*MSG_SIZ];
7380 char *d, *s, buf[4*MSG_SIZ];
7386 while (*s && isspace(*s))
7395 if (*(s+1) == '/') {
7396 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7400 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7401 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7402 pwd = getpwnam(buf);
7405 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7409 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7410 strcat(d, strchr(s+1, '/'));
7414 safeStrCpy(d, s, 4*MSG_SIZ );
7421 static char host_name[MSG_SIZ];
7423 #if HAVE_GETHOSTNAME
7424 gethostname(host_name, MSG_SIZ);
7426 #else /* not HAVE_GETHOSTNAME */
7427 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7428 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7430 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7432 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7433 #endif /* not HAVE_GETHOSTNAME */
7436 XtIntervalId delayedEventTimerXID = 0;
7437 DelayedEventCallback delayedEventCallback = 0;
7442 delayedEventTimerXID = 0;
7443 delayedEventCallback();
7447 ScheduleDelayedEvent(cb, millisec)
7448 DelayedEventCallback cb; long millisec;
7450 if(delayedEventTimerXID && delayedEventCallback == cb)
7451 // [HGM] alive: replace, rather than add or flush identical event
7452 XtRemoveTimeOut(delayedEventTimerXID);
7453 delayedEventCallback = cb;
7454 delayedEventTimerXID =
7455 XtAppAddTimeOut(appContext, millisec,
7456 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7459 DelayedEventCallback
7462 if (delayedEventTimerXID) {
7463 return delayedEventCallback;
7470 CancelDelayedEvent()
7472 if (delayedEventTimerXID) {
7473 XtRemoveTimeOut(delayedEventTimerXID);
7474 delayedEventTimerXID = 0;
7478 XtIntervalId loadGameTimerXID = 0;
7480 int LoadGameTimerRunning()
7482 return loadGameTimerXID != 0;
7485 int StopLoadGameTimer()
7487 if (loadGameTimerXID != 0) {
7488 XtRemoveTimeOut(loadGameTimerXID);
7489 loadGameTimerXID = 0;
7497 LoadGameTimerCallback(arg, id)
7501 loadGameTimerXID = 0;
7506 StartLoadGameTimer(millisec)
7510 XtAppAddTimeOut(appContext, millisec,
7511 (XtTimerCallbackProc) LoadGameTimerCallback,
7515 XtIntervalId analysisClockXID = 0;
7518 AnalysisClockCallback(arg, id)
7522 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7523 || appData.icsEngineAnalyze) { // [DM]
7524 AnalysisPeriodicEvent(0);
7525 StartAnalysisClock();
7530 StartAnalysisClock()
7533 XtAppAddTimeOut(appContext, 2000,
7534 (XtTimerCallbackProc) AnalysisClockCallback,
7538 XtIntervalId clockTimerXID = 0;
7540 int ClockTimerRunning()
7542 return clockTimerXID != 0;
7545 int StopClockTimer()
7547 if (clockTimerXID != 0) {
7548 XtRemoveTimeOut(clockTimerXID);
7557 ClockTimerCallback(arg, id)
7566 StartClockTimer(millisec)
7570 XtAppAddTimeOut(appContext, millisec,
7571 (XtTimerCallbackProc) ClockTimerCallback,
7576 DisplayTimerLabel(w, color, timer, highlight)
7585 /* check for low time warning */
7586 Pixel foregroundOrWarningColor = timerForegroundPixel;
7589 appData.lowTimeWarning &&
7590 (timer / 1000) < appData.icsAlarmTime)
7591 foregroundOrWarningColor = lowTimeWarningColor;
7593 if (appData.clockMode) {
7594 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7595 XtSetArg(args[0], XtNlabel, buf);
7597 snprintf(buf, MSG_SIZ, "%s ", color);
7598 XtSetArg(args[0], XtNlabel, buf);
7603 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7604 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7606 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7607 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7610 XtSetValues(w, args, 3);
7614 DisplayWhiteClock(timeRemaining, highlight)
7620 if(appData.noGUI) return;
7621 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7622 if (highlight && iconPixmap == bIconPixmap) {
7623 iconPixmap = wIconPixmap;
7624 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7625 XtSetValues(shellWidget, args, 1);
7630 DisplayBlackClock(timeRemaining, highlight)
7636 if(appData.noGUI) return;
7637 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7638 if (highlight && iconPixmap == wIconPixmap) {
7639 iconPixmap = bIconPixmap;
7640 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7641 XtSetValues(shellWidget, args, 1);
7659 int StartChildProcess(cmdLine, dir, pr)
7666 int to_prog[2], from_prog[2];
7670 if (appData.debugMode) {
7671 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7674 /* We do NOT feed the cmdLine to the shell; we just
7675 parse it into blank-separated arguments in the
7676 most simple-minded way possible.
7679 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7682 while(*p == ' ') p++;
7684 if(*p == '"' || *p == '\'')
7685 p = strchr(++argv[i-1], *p);
7686 else p = strchr(p, ' ');
7687 if (p == NULL) break;
7692 SetUpChildIO(to_prog, from_prog);
7694 if ((pid = fork()) == 0) {
7696 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7697 close(to_prog[1]); // first close the unused pipe ends
7698 close(from_prog[0]);
7699 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7700 dup2(from_prog[1], 1);
7701 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7702 close(from_prog[1]); // and closing again loses one of the pipes!
7703 if(fileno(stderr) >= 2) // better safe than sorry...
7704 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7706 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7711 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7713 execvp(argv[0], argv);
7715 /* If we get here, exec failed */
7720 /* Parent process */
7722 close(from_prog[1]);
7724 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7727 cp->fdFrom = from_prog[0];
7728 cp->fdTo = to_prog[1];
7733 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7734 static RETSIGTYPE AlarmCallBack(int n)
7740 DestroyChildProcess(pr, signalType)
7744 ChildProc *cp = (ChildProc *) pr;
7746 if (cp->kind != CPReal) return;
7748 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7749 signal(SIGALRM, AlarmCallBack);
7751 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7752 kill(cp->pid, SIGKILL); // kill it forcefully
7753 wait((int *) 0); // and wait again
7757 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7759 /* Process is exiting either because of the kill or because of
7760 a quit command sent by the backend; either way, wait for it to die.
7769 InterruptChildProcess(pr)
7772 ChildProc *cp = (ChildProc *) pr;
7774 if (cp->kind != CPReal) return;
7775 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7778 int OpenTelnet(host, port, pr)
7783 char cmdLine[MSG_SIZ];
7785 if (port[0] == NULLCHAR) {
7786 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7788 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7790 return StartChildProcess(cmdLine, "", pr);
7793 int OpenTCP(host, port, pr)
7799 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7800 #else /* !OMIT_SOCKETS */
7801 struct addrinfo hints;
7802 struct addrinfo *ais, *ai;
7807 memset(&hints, 0, sizeof(hints));
7808 hints.ai_family = AF_UNSPEC;
7809 hints.ai_socktype = SOCK_STREAM;
7811 error = getaddrinfo(host, port, &hints, &ais);
7813 /* a getaddrinfo error is not an errno, so can't return it */
7814 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7815 host, port, gai_strerror(error));
7819 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7820 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7824 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7837 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7843 #endif /* !OMIT_SOCKETS */
7848 int OpenCommPort(name, pr)
7855 fd = open(name, 2, 0);
7856 if (fd < 0) return errno;
7858 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7868 int OpenLoopback(pr)
7874 SetUpChildIO(to, from);
7876 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7879 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7886 int OpenRcmd(host, user, cmd, pr)
7887 char *host, *user, *cmd;
7890 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7894 #define INPUT_SOURCE_BUF_SIZE 8192
7903 char buf[INPUT_SOURCE_BUF_SIZE];
7908 DoInputCallback(closure, source, xid)
7913 InputSource *is = (InputSource *) closure;
7918 if (is->lineByLine) {
7919 count = read(is->fd, is->unused,
7920 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7922 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7925 is->unused += count;
7927 while (p < is->unused) {
7928 q = memchr(p, '\n', is->unused - p);
7929 if (q == NULL) break;
7931 (is->func)(is, is->closure, p, q - p, 0);
7935 while (p < is->unused) {
7940 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7945 (is->func)(is, is->closure, is->buf, count, error);
7949 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7956 ChildProc *cp = (ChildProc *) pr;
7958 is = (InputSource *) calloc(1, sizeof(InputSource));
7959 is->lineByLine = lineByLine;
7963 is->fd = fileno(stdin);
7965 is->kind = cp->kind;
7966 is->fd = cp->fdFrom;
7969 is->unused = is->buf;
7972 is->xid = XtAppAddInput(appContext, is->fd,
7973 (XtPointer) (XtInputReadMask),
7974 (XtInputCallbackProc) DoInputCallback,
7976 is->closure = closure;
7977 return (InputSourceRef) is;
7981 RemoveInputSource(isr)
7984 InputSource *is = (InputSource *) isr;
7986 if (is->xid == 0) return;
7987 XtRemoveInput(is->xid);
7991 int OutputToProcess(pr, message, count, outError)
7997 static int line = 0;
7998 ChildProc *cp = (ChildProc *) pr;
8003 if (appData.noJoin || !appData.useInternalWrap)
8004 outCount = fwrite(message, 1, count, stdout);
8007 int width = get_term_width();
8008 int len = wrap(NULL, message, count, width, &line);
8009 char *msg = malloc(len);
8013 outCount = fwrite(message, 1, count, stdout);
8016 dbgchk = wrap(msg, message, count, width, &line);
8017 if (dbgchk != len && appData.debugMode)
8018 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8019 outCount = fwrite(msg, 1, dbgchk, stdout);
8025 outCount = write(cp->fdTo, message, count);
8035 /* Output message to process, with "ms" milliseconds of delay
8036 between each character. This is needed when sending the logon
8037 script to ICC, which for some reason doesn't like the
8038 instantaneous send. */
8039 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8046 ChildProc *cp = (ChildProc *) pr;
8051 r = write(cp->fdTo, message++, 1);
8064 /**** Animation code by Hugh Fisher, DCS, ANU.
8066 Known problem: if a window overlapping the board is
8067 moved away while a piece is being animated underneath,
8068 the newly exposed area won't be updated properly.
8069 I can live with this.
8071 Known problem: if you look carefully at the animation
8072 of pieces in mono mode, they are being drawn as solid
8073 shapes without interior detail while moving. Fixing
8074 this would be a major complication for minimal return.
8077 /* Masks for XPM pieces. Black and white pieces can have
8078 different shapes, but in the interest of retaining my
8079 sanity pieces must have the same outline on both light
8080 and dark squares, and all pieces must use the same
8081 background square colors/images. */
8083 static int xpmDone = 0;
8086 CreateAnimMasks (pieceDepth)
8093 unsigned long plane;
8096 /* Need a bitmap just to get a GC with right depth */
8097 buf = XCreatePixmap(xDisplay, xBoardWindow,
8099 values.foreground = 1;
8100 values.background = 0;
8101 /* Don't use XtGetGC, not read only */
8102 maskGC = XCreateGC(xDisplay, buf,
8103 GCForeground | GCBackground, &values);
8104 XFreePixmap(xDisplay, buf);
8106 buf = XCreatePixmap(xDisplay, xBoardWindow,
8107 squareSize, squareSize, pieceDepth);
8108 values.foreground = XBlackPixel(xDisplay, xScreen);
8109 values.background = XWhitePixel(xDisplay, xScreen);
8110 bufGC = XCreateGC(xDisplay, buf,
8111 GCForeground | GCBackground, &values);
8113 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8114 /* Begin with empty mask */
8115 if(!xpmDone) // [HGM] pieces: keep using existing
8116 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8117 squareSize, squareSize, 1);
8118 XSetFunction(xDisplay, maskGC, GXclear);
8119 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8120 0, 0, squareSize, squareSize);
8122 /* Take a copy of the piece */
8127 XSetFunction(xDisplay, bufGC, GXcopy);
8128 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8130 0, 0, squareSize, squareSize, 0, 0);
8132 /* XOR the background (light) over the piece */
8133 XSetFunction(xDisplay, bufGC, GXxor);
8135 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8136 0, 0, squareSize, squareSize, 0, 0);
8138 XSetForeground(xDisplay, bufGC, lightSquareColor);
8139 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8142 /* We now have an inverted piece image with the background
8143 erased. Construct mask by just selecting all the non-zero
8144 pixels - no need to reconstruct the original image. */
8145 XSetFunction(xDisplay, maskGC, GXor);
8147 /* Might be quicker to download an XImage and create bitmap
8148 data from it rather than this N copies per piece, but it
8149 only takes a fraction of a second and there is a much
8150 longer delay for loading the pieces. */
8151 for (n = 0; n < pieceDepth; n ++) {
8152 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8153 0, 0, squareSize, squareSize,
8159 XFreePixmap(xDisplay, buf);
8160 XFreeGC(xDisplay, bufGC);
8161 XFreeGC(xDisplay, maskGC);
8165 InitAnimState (anim, info)
8167 XWindowAttributes * info;
8172 /* Each buffer is square size, same depth as window */
8173 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8174 squareSize, squareSize, info->depth);
8175 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8176 squareSize, squareSize, info->depth);
8178 /* Create a plain GC for blitting */
8179 mask = GCForeground | GCBackground | GCFunction |
8180 GCPlaneMask | GCGraphicsExposures;
8181 values.foreground = XBlackPixel(xDisplay, xScreen);
8182 values.background = XWhitePixel(xDisplay, xScreen);
8183 values.function = GXcopy;
8184 values.plane_mask = AllPlanes;
8185 values.graphics_exposures = False;
8186 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8188 /* Piece will be copied from an existing context at
8189 the start of each new animation/drag. */
8190 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8192 /* Outline will be a read-only copy of an existing */
8193 anim->outlineGC = None;
8199 XWindowAttributes info;
8201 if (xpmDone && gameInfo.variant == oldVariant) return;
8202 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8203 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8205 InitAnimState(&game, &info);
8206 InitAnimState(&player, &info);
8208 /* For XPM pieces, we need bitmaps to use as masks. */
8210 CreateAnimMasks(info.depth), xpmDone = 1;
8215 static Boolean frameWaiting;
8217 static RETSIGTYPE FrameAlarm (sig)
8220 frameWaiting = False;
8221 /* In case System-V style signals. Needed?? */
8222 signal(SIGALRM, FrameAlarm);
8229 struct itimerval delay;
8231 XSync(xDisplay, False);
8234 frameWaiting = True;
8235 signal(SIGALRM, FrameAlarm);
8236 delay.it_interval.tv_sec =
8237 delay.it_value.tv_sec = time / 1000;
8238 delay.it_interval.tv_usec =
8239 delay.it_value.tv_usec = (time % 1000) * 1000;
8240 setitimer(ITIMER_REAL, &delay, NULL);
8241 while (frameWaiting) pause();
8242 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8243 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8244 setitimer(ITIMER_REAL, &delay, NULL);
8254 XSync(xDisplay, False);
8256 usleep(time * 1000);
8267 /* Convert board position to corner of screen rect and color */
8270 ScreenSquare(column, row, pt, color)
8271 int column; int row; XPoint * pt; int * color;
8274 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8275 pt->y = lineGap + row * (squareSize + lineGap);
8277 pt->x = lineGap + column * (squareSize + lineGap);
8278 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8280 *color = SquareColor(row, column);
8283 /* Convert window coords to square */
8286 BoardSquare(x, y, column, row)
8287 int x; int y; int * column; int * row;
8289 *column = EventToSquare(x, BOARD_WIDTH);
8290 if (flipView && *column >= 0)
8291 *column = BOARD_WIDTH - 1 - *column;
8292 *row = EventToSquare(y, BOARD_HEIGHT);
8293 if (!flipView && *row >= 0)
8294 *row = BOARD_HEIGHT - 1 - *row;
8299 #undef Max /* just in case */
8301 #define Max(a, b) ((a) > (b) ? (a) : (b))
8302 #define Min(a, b) ((a) < (b) ? (a) : (b))
8305 SetRect(rect, x, y, width, height)
8306 XRectangle * rect; int x; int y; int width; int height;
8310 rect->width = width;
8311 rect->height = height;
8314 /* Test if two frames overlap. If they do, return
8315 intersection rect within old and location of
8316 that rect within new. */
8319 Intersect(old, new, size, area, pt)
8320 XPoint * old; XPoint * new;
8321 int size; XRectangle * area; XPoint * pt;
8323 if (old->x > new->x + size || new->x > old->x + size ||
8324 old->y > new->y + size || new->y > old->y + size) {
8327 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8328 size - abs(old->x - new->x), size - abs(old->y - new->y));
8329 pt->x = Max(old->x - new->x, 0);
8330 pt->y = Max(old->y - new->y, 0);
8335 /* For two overlapping frames, return the rect(s)
8336 in the old that do not intersect with the new. */
8339 CalcUpdateRects(old, new, size, update, nUpdates)
8340 XPoint * old; XPoint * new; int size;
8341 XRectangle update[]; int * nUpdates;
8345 /* If old = new (shouldn't happen) then nothing to draw */
8346 if (old->x == new->x && old->y == new->y) {
8350 /* Work out what bits overlap. Since we know the rects
8351 are the same size we don't need a full intersect calc. */
8353 /* Top or bottom edge? */
8354 if (new->y > old->y) {
8355 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8357 } else if (old->y > new->y) {
8358 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8359 size, old->y - new->y);
8362 /* Left or right edge - don't overlap any update calculated above. */
8363 if (new->x > old->x) {
8364 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8365 new->x - old->x, size - abs(new->y - old->y));
8367 } else if (old->x > new->x) {
8368 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8369 old->x - new->x, size - abs(new->y - old->y));
8376 /* Generate a series of frame coords from start->mid->finish.
8377 The movement rate doubles until the half way point is
8378 reached, then halves back down to the final destination,
8379 which gives a nice slow in/out effect. The algorithmn
8380 may seem to generate too many intermediates for short
8381 moves, but remember that the purpose is to attract the
8382 viewers attention to the piece about to be moved and
8383 then to where it ends up. Too few frames would be less
8387 Tween(start, mid, finish, factor, frames, nFrames)
8388 XPoint * start; XPoint * mid;
8389 XPoint * finish; int factor;
8390 XPoint frames[]; int * nFrames;
8392 int fraction, n, count;
8396 /* Slow in, stepping 1/16th, then 1/8th, ... */
8398 for (n = 0; n < factor; n++)
8400 for (n = 0; n < factor; n++) {
8401 frames[count].x = start->x + (mid->x - start->x) / fraction;
8402 frames[count].y = start->y + (mid->y - start->y) / fraction;
8404 fraction = fraction / 2;
8408 frames[count] = *mid;
8411 /* Slow out, stepping 1/2, then 1/4, ... */
8413 for (n = 0; n < factor; n++) {
8414 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8415 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8417 fraction = fraction * 2;
8422 /* Draw a piece on the screen without disturbing what's there */
8425 SelectGCMask(piece, clip, outline, mask)
8426 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8430 /* Bitmap for piece being moved. */
8431 if (appData.monoMode) {
8432 *mask = *pieceToSolid(piece);
8433 } else if (useImages) {
8435 *mask = xpmMask[piece];
8437 *mask = ximMaskPm[piece];
8440 *mask = *pieceToSolid(piece);
8443 /* GC for piece being moved. Square color doesn't matter, but
8444 since it gets modified we make a copy of the original. */
8446 if (appData.monoMode)
8451 if (appData.monoMode)
8456 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8458 /* Outline only used in mono mode and is not modified */
8460 *outline = bwPieceGC;
8462 *outline = wbPieceGC;
8466 OverlayPiece(piece, clip, outline, dest)
8467 ChessSquare piece; GC clip; GC outline; Drawable dest;
8472 /* Draw solid rectangle which will be clipped to shape of piece */
8473 XFillRectangle(xDisplay, dest, clip,
8474 0, 0, squareSize, squareSize);
8475 if (appData.monoMode)
8476 /* Also draw outline in contrasting color for black
8477 on black / white on white cases */
8478 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8479 0, 0, squareSize, squareSize, 0, 0, 1);
8481 /* Copy the piece */
8486 if(appData.upsideDown && flipView) kind ^= 2;
8487 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8489 0, 0, squareSize, squareSize,
8494 /* Animate the movement of a single piece */
8497 BeginAnimation(anim, piece, startColor, start)
8505 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8506 /* The old buffer is initialised with the start square (empty) */
8507 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8508 anim->prevFrame = *start;
8510 /* The piece will be drawn using its own bitmap as a matte */
8511 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8512 XSetClipMask(xDisplay, anim->pieceGC, mask);
8516 AnimationFrame(anim, frame, piece)
8521 XRectangle updates[4];
8526 /* Save what we are about to draw into the new buffer */
8527 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8528 frame->x, frame->y, squareSize, squareSize,
8531 /* Erase bits of the previous frame */
8532 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8533 /* Where the new frame overlapped the previous,
8534 the contents in newBuf are wrong. */
8535 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8536 overlap.x, overlap.y,
8537 overlap.width, overlap.height,
8539 /* Repaint the areas in the old that don't overlap new */
8540 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8541 for (i = 0; i < count; i++)
8542 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8543 updates[i].x - anim->prevFrame.x,
8544 updates[i].y - anim->prevFrame.y,
8545 updates[i].width, updates[i].height,
8546 updates[i].x, updates[i].y);
8548 /* Easy when no overlap */
8549 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8550 0, 0, squareSize, squareSize,
8551 anim->prevFrame.x, anim->prevFrame.y);
8554 /* Save this frame for next time round */
8555 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8556 0, 0, squareSize, squareSize,
8558 anim->prevFrame = *frame;
8560 /* Draw piece over original screen contents, not current,
8561 and copy entire rect. Wipes out overlapping piece images. */
8562 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8563 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8564 0, 0, squareSize, squareSize,
8565 frame->x, frame->y);
8569 EndAnimation (anim, finish)
8573 XRectangle updates[4];
8578 /* The main code will redraw the final square, so we
8579 only need to erase the bits that don't overlap. */
8580 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8581 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8582 for (i = 0; i < count; i++)
8583 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8584 updates[i].x - anim->prevFrame.x,
8585 updates[i].y - anim->prevFrame.y,
8586 updates[i].width, updates[i].height,
8587 updates[i].x, updates[i].y);
8589 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8590 0, 0, squareSize, squareSize,
8591 anim->prevFrame.x, anim->prevFrame.y);
8596 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8598 ChessSquare piece; int startColor;
8599 XPoint * start; XPoint * finish;
8600 XPoint frames[]; int nFrames;
8604 BeginAnimation(anim, piece, startColor, start);
8605 for (n = 0; n < nFrames; n++) {
8606 AnimationFrame(anim, &(frames[n]), piece);
8607 FrameDelay(appData.animSpeed);
8609 EndAnimation(anim, finish);
8613 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8616 ChessSquare piece = board[fromY][toY];
8617 board[fromY][toY] = EmptySquare;
8618 DrawPosition(FALSE, board);
8620 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8621 y = lineGap + toY * (squareSize + lineGap);
8623 x = lineGap + toX * (squareSize + lineGap);
8624 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8626 for(i=1; i<4*kFactor; i++) {
8627 int r = squareSize * 9 * i/(20*kFactor - 5);
8628 XFillArc(xDisplay, xBoardWindow, highlineGC,
8629 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8630 FrameDelay(appData.animSpeed);
8632 board[fromY][toY] = piece;
8635 /* Main control logic for deciding what to animate and how */
8638 AnimateMove(board, fromX, fromY, toX, toY)
8647 XPoint start, finish, mid;
8648 XPoint frames[kFactor * 2 + 1];
8649 int nFrames, startColor, endColor;
8651 /* Are we animating? */
8652 if (!appData.animate || appData.blindfold)
8655 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8656 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8657 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8659 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8660 piece = board[fromY][fromX];
8661 if (piece >= EmptySquare) return;
8666 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8669 if (appData.debugMode) {
8670 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8671 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8672 piece, fromX, fromY, toX, toY); }
8674 ScreenSquare(fromX, fromY, &start, &startColor);
8675 ScreenSquare(toX, toY, &finish, &endColor);
8678 /* Knight: make straight movement then diagonal */
8679 if (abs(toY - fromY) < abs(toX - fromX)) {
8680 mid.x = start.x + (finish.x - start.x) / 2;
8684 mid.y = start.y + (finish.y - start.y) / 2;
8687 mid.x = start.x + (finish.x - start.x) / 2;
8688 mid.y = start.y + (finish.y - start.y) / 2;
8691 /* Don't use as many frames for very short moves */
8692 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8693 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8695 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8696 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8697 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8699 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8700 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8703 /* Be sure end square is redrawn */
8704 damage[0][toY][toX] = True;
8708 DragPieceBegin(x, y, instantly)
8709 int x; int y; Boolean instantly;
8711 int boardX, boardY, color;
8714 /* Are we animating? */
8715 if (!appData.animateDragging || appData.blindfold)
8718 /* Figure out which square we start in and the
8719 mouse position relative to top left corner. */
8720 BoardSquare(x, y, &boardX, &boardY);
8721 player.startBoardX = boardX;
8722 player.startBoardY = boardY;
8723 ScreenSquare(boardX, boardY, &corner, &color);
8724 player.startSquare = corner;
8725 player.startColor = color;
8726 /* As soon as we start dragging, the piece will jump slightly to
8727 be centered over the mouse pointer. */
8728 player.mouseDelta.x = squareSize/2;
8729 player.mouseDelta.y = squareSize/2;
8730 /* Initialise animation */
8731 player.dragPiece = PieceForSquare(boardX, boardY);
8733 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8734 player.dragActive = True;
8735 BeginAnimation(&player, player.dragPiece, color, &corner);
8736 /* Mark this square as needing to be redrawn. Note that
8737 we don't remove the piece though, since logically (ie
8738 as seen by opponent) the move hasn't been made yet. */
8739 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8740 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8741 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8742 corner.x, corner.y, squareSize, squareSize,
8743 0, 0); // [HGM] zh: unstack in stead of grab
8744 if(gatingPiece != EmptySquare) {
8745 /* Kludge alert: When gating we want the introduced
8746 piece to appear on the from square. To generate an
8747 image of it, we draw it on the board, copy the image,
8748 and draw the original piece again. */
8749 ChessSquare piece = boards[currentMove][boardY][boardX];
8750 DrawSquare(boardY, boardX, gatingPiece, 0);
8751 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8752 corner.x, corner.y, squareSize, squareSize, 0, 0);
8753 DrawSquare(boardY, boardX, piece, 0);
8755 damage[0][boardY][boardX] = True;
8757 player.dragActive = False;
8762 ChangeDragPiece(ChessSquare piece)
8765 player.dragPiece = piece;
8766 /* The piece will be drawn using its own bitmap as a matte */
8767 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8768 XSetClipMask(xDisplay, player.pieceGC, mask);
8777 /* Are we animating? */
8778 if (!appData.animateDragging || appData.blindfold)
8782 if (! player.dragActive)
8784 /* Move piece, maintaining same relative position
8785 of mouse within square */
8786 corner.x = x - player.mouseDelta.x;
8787 corner.y = y - player.mouseDelta.y;
8788 AnimationFrame(&player, &corner, player.dragPiece);
8790 if (appData.highlightDragging) {
8792 BoardSquare(x, y, &boardX, &boardY);
8793 SetHighlights(fromX, fromY, boardX, boardY);
8802 int boardX, boardY, color;
8805 /* Are we animating? */
8806 if (!appData.animateDragging || appData.blindfold)
8810 if (! player.dragActive)
8812 /* Last frame in sequence is square piece is
8813 placed on, which may not match mouse exactly. */
8814 BoardSquare(x, y, &boardX, &boardY);
8815 ScreenSquare(boardX, boardY, &corner, &color);
8816 EndAnimation(&player, &corner);
8818 /* Be sure end square is redrawn */
8819 damage[0][boardY][boardX] = True;
8821 /* This prevents weird things happening with fast successive
8822 clicks which on my Sun at least can cause motion events
8823 without corresponding press/release. */
8824 player.dragActive = False;
8827 /* Handle expose event while piece being dragged */
8832 if (!player.dragActive || appData.blindfold)
8835 /* What we're doing: logically, the move hasn't been made yet,
8836 so the piece is still in it's original square. But visually
8837 it's being dragged around the board. So we erase the square
8838 that the piece is on and draw it at the last known drag point. */
8839 BlankSquare(player.startSquare.x, player.startSquare.y,
8840 player.startColor, EmptySquare, xBoardWindow, 1);
8841 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8842 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8845 #include <sys/ioctl.h>
8846 int get_term_width()
8848 int fd, default_width;
8851 default_width = 79; // this is FICS default anyway...
8853 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8855 if (!ioctl(fd, TIOCGSIZE, &win))
8856 default_width = win.ts_cols;
8857 #elif defined(TIOCGWINSZ)
8859 if (!ioctl(fd, TIOCGWINSZ, &win))
8860 default_width = win.ws_col;
8862 return default_width;
8868 static int old_width = 0;
8869 int new_width = get_term_width();
8871 if (old_width != new_width)
8872 ics_printf("set width %d\n", new_width);
8873 old_width = new_width;
8876 void NotifyFrontendLogin()
8881 /* [AS] Arrow highlighting support */
8883 static double A_WIDTH = 5; /* Width of arrow body */
8885 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8886 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8888 static double Sqr( double x )
8893 static int Round( double x )
8895 return (int) (x + 0.5);
8898 void SquareToPos(int rank, int file, int *x, int *y)
8901 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8902 *y = lineGap + rank * (squareSize + lineGap);
8904 *x = lineGap + file * (squareSize + lineGap);
8905 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8909 /* Draw an arrow between two points using current settings */
8910 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8913 double dx, dy, j, k, x, y;
8916 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8918 arrow[0].x = s_x + A_WIDTH + 0.5;
8921 arrow[1].x = s_x + A_WIDTH + 0.5;
8922 arrow[1].y = d_y - h;
8924 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8925 arrow[2].y = d_y - h;
8930 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8931 arrow[5].y = d_y - h;
8933 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8934 arrow[4].y = d_y - h;
8936 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8939 else if( d_y == s_y ) {
8940 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8943 arrow[0].y = s_y + A_WIDTH + 0.5;
8945 arrow[1].x = d_x - w;
8946 arrow[1].y = s_y + A_WIDTH + 0.5;
8948 arrow[2].x = d_x - w;
8949 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8954 arrow[5].x = d_x - w;
8955 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8957 arrow[4].x = d_x - w;
8958 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8961 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8964 /* [AS] Needed a lot of paper for this! :-) */
8965 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8966 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8968 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8970 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8975 arrow[0].x = Round(x - j);
8976 arrow[0].y = Round(y + j*dx);
8978 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8979 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8982 x = (double) d_x - k;
8983 y = (double) d_y - k*dy;
8986 x = (double) d_x + k;
8987 y = (double) d_y + k*dy;
8990 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8992 arrow[6].x = Round(x - j);
8993 arrow[6].y = Round(y + j*dx);
8995 arrow[2].x = Round(arrow[6].x + 2*j);
8996 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8998 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8999 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
9004 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9005 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9008 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9009 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
9010 // Polygon( hdc, arrow, 7 );
9013 /* [AS] Draw an arrow between two squares */
9014 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9016 int s_x, s_y, d_x, d_y, hor, vert, i;
9018 if( s_col == d_col && s_row == d_row ) {
9022 /* Get source and destination points */
9023 SquareToPos( s_row, s_col, &s_x, &s_y);
9024 SquareToPos( d_row, d_col, &d_x, &d_y);
9027 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9029 else if( d_y < s_y ) {
9030 d_y += squareSize / 2 + squareSize / 4;
9033 d_y += squareSize / 2;
9037 d_x += squareSize / 2 - squareSize / 4;
9039 else if( d_x < s_x ) {
9040 d_x += squareSize / 2 + squareSize / 4;
9043 d_x += squareSize / 2;
9046 s_x += squareSize / 2;
9047 s_y += squareSize / 2;
9050 A_WIDTH = squareSize / 14.; //[HGM] make float
9052 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9054 hor = 64*s_col + 32; vert = 64*s_row + 32;
9055 for(i=0; i<= 64; i++) {
9056 damage[0][vert+6>>6][hor+6>>6] = True;
9057 damage[0][vert-6>>6][hor+6>>6] = True;
9058 damage[0][vert+6>>6][hor-6>>6] = True;
9059 damage[0][vert-6>>6][hor-6>>6] = True;
9060 hor += d_col - s_col; vert += d_row - s_row;
9064 Boolean IsDrawArrowEnabled()
9066 return appData.highlightMoveWithArrow && squareSize >= 32;
9069 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9071 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9072 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9075 void UpdateLogos(int displ)
9077 return; // no logos in XBoard yet