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