2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
243 int main P((int argc, char **argv));
244 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
245 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
246 RETSIGTYPE CmailSigHandler P((int sig));
247 RETSIGTYPE IntSigHandler P((int sig));
248 RETSIGTYPE TermSizeSigHandler P((int sig));
249 void CreateGCs P((int redo));
250 void CreateAnyPieces P((void));
251 void CreateXIMPieces P((void));
252 void CreateXPMPieces P((void));
253 void CreateXPMBoard P((char *s, int n));
254 void CreatePieces P((void));
255 void CreatePieceMenus P((void));
256 Widget CreateMenuBar P((Menu *mb));
257 Widget CreateButtonBar P ((MenuItem *mi));
259 char *InsertPxlSize P((char *pattern, int targetPxlSize));
260 XFontSet CreateFontSet P((char *base_fnt_lst));
262 char *FindFont P((char *pattern, int targetPxlSize));
264 void PieceMenuPopup P((Widget w, XEvent *event,
265 String *params, Cardinal *num_params));
266 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
267 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
268 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
269 u_int wreq, u_int hreq));
270 void CreateGrid P((void));
271 int EventToSquare P((int x, int limit));
272 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
273 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
274 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
275 void HandleUserMove P((Widget w, XEvent *event,
276 String *prms, Cardinal *nprms));
277 void AnimateUserMove P((Widget w, XEvent * event,
278 String * params, Cardinal * nParams));
279 void HandlePV P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void SelectPV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void StopPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void WhiteClock P((Widget w, XEvent *event,
286 String *prms, Cardinal *nprms));
287 void BlackClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void DrawPositionProc P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
293 void CommentClick P((Widget w, XEvent * event,
294 String * params, Cardinal * nParams));
295 void CommentPopUp P((char *title, char *label));
296 void CommentPopDown P((void));
297 void ICSInputBoxPopUp P((void));
298 void ICSInputBoxPopDown P((void));
299 void FileNamePopUp P((char *label, char *def, char *filter,
300 FileProc proc, char *openMode));
301 void FileNamePopDown P((void));
302 void FileNameCallback P((Widget w, XtPointer client_data,
303 XtPointer call_data));
304 void FileNameAction P((Widget w, XEvent *event,
305 String *prms, Cardinal *nprms));
306 void AskQuestionReplyAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionProc P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionPopDown P((void));
311 void PromotionPopDown P((void));
312 void PromotionCallback P((Widget w, XtPointer client_data,
313 XtPointer call_data));
314 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
315 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
316 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
317 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
319 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
321 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
323 void LoadPositionProc P((Widget w, XEvent *event,
324 String *prms, Cardinal *nprms));
325 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
327 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
329 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
331 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
333 void PastePositionProc P((Widget w, XEvent *event, String *prms,
335 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
337 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void SavePositionProc P((Widget w, XEvent *event,
340 String *prms, Cardinal *nprms));
341 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
344 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
348 void MachineWhiteProc P((Widget w, XEvent *event,
349 String *prms, Cardinal *nprms));
350 void AnalyzeModeProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeFileProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
356 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void IcsClientProc P((Widget w, XEvent *event, String *prms,
360 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void EditPositionProc P((Widget w, XEvent *event,
362 String *prms, Cardinal *nprms));
363 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void EditCommentProc P((Widget w, XEvent *event,
365 String *prms, Cardinal *nprms));
366 void IcsInputBoxProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void StopObservingProc P((Widget w, XEvent *event, String *prms,
385 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
387 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
388 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
389 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 Boolean TempBackwardActive = False;
393 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
395 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
396 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
399 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
401 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
402 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
404 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
406 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
408 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
409 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
413 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
414 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
416 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
418 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
420 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
421 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
422 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
425 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
427 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
429 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
431 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
434 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
436 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
438 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
440 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void DisplayMove P((int moveNumber));
451 void DisplayTitle P((char *title));
452 void ICSInitScript P((void));
453 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
454 void ErrorPopUp P((char *title, char *text, int modal));
455 void ErrorPopDown P((void));
456 static char *ExpandPathName P((char *path));
457 static void CreateAnimVars P((void));
458 static void DragPieceMove P((int x, int y));
459 static void DrawDragPiece P((void));
460 char *ModeToWidgetName P((GameMode mode));
461 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
477 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
478 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
479 void GameListOptionsPopDown P(());
480 void GenericPopDown P(());
481 void update_ics_width P(());
482 int get_term_width P(());
483 int CopyMemoProc P(());
484 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
485 Boolean IsDrawArrowEnabled P(());
488 * XBoard depends on Xt R4 or higher
490 int xtVersion = XtSpecificationRelease;
495 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
496 jailSquareColor, highlightSquareColor, premoveHighlightColor;
497 Pixel lowTimeWarningColor;
498 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
499 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
500 wjPieceGC, bjPieceGC, prelineGC, countGC;
501 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
502 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
503 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
504 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
505 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
506 ICSInputShell, fileNameShell, askQuestionShell;
507 Widget historyShell, evalGraphShell, gameListShell;
508 int hOffset; // [HGM] dual
509 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
510 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
511 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
513 XFontSet fontSet, clockFontSet;
516 XFontStruct *clockFontStruct;
518 Font coordFontID, countFontID;
519 XFontStruct *coordFontStruct, *countFontStruct;
520 XtAppContext appContext;
522 char *oldICSInteractionTitle;
526 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
528 Position commentX = -1, commentY = -1;
529 Dimension commentW, commentH;
530 typedef unsigned int BoardSize;
532 Boolean chessProgram;
534 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
535 int squareSize, smallLayout = 0, tinyLayout = 0,
536 marginW, marginH, // [HGM] for run-time resizing
537 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
538 ICSInputBoxUp = False, askQuestionUp = False,
539 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
540 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
541 Pixel timerForegroundPixel, timerBackgroundPixel;
542 Pixel buttonForegroundPixel, buttonBackgroundPixel;
543 char *chessDir, *programName, *programVersion,
544 *gameCopyFilename, *gamePasteFilename;
545 Boolean alwaysOnTop = False;
546 Boolean saveSettingsOnExit;
547 char *settingsFileName;
548 char *icsTextMenuString;
550 char *firstChessProgramNames;
551 char *secondChessProgramNames;
553 WindowPlacement wpMain;
554 WindowPlacement wpConsole;
555 WindowPlacement wpComment;
556 WindowPlacement wpMoveHistory;
557 WindowPlacement wpEvalGraph;
558 WindowPlacement wpEngineOutput;
559 WindowPlacement wpGameList;
560 WindowPlacement wpTags;
562 extern Widget shells[];
563 extern Boolean shellUp[];
567 Pixmap pieceBitmap[2][(int)BlackPawn];
568 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
569 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
570 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
571 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
572 Pixmap xpmBoardBitmap[2];
573 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
574 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
575 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
576 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
577 XImage *ximLightSquare, *ximDarkSquare;
580 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
581 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
583 #define White(piece) ((int)(piece) < (int)BlackPawn)
585 /* Variables for doing smooth animation. This whole thing
586 would be much easier if the board was double-buffered,
587 but that would require a fairly major rewrite. */
592 GC blitGC, pieceGC, outlineGC;
593 XPoint startSquare, prevFrame, mouseDelta;
597 int startBoardX, startBoardY;
600 /* There can be two pieces being animated at once: a player
601 can begin dragging a piece before the remote opponent has moved. */
603 static AnimState game, player;
605 /* Bitmaps for use as masks when drawing XPM pieces.
606 Need one for each black and white piece. */
607 static Pixmap xpmMask[BlackKing + 1];
609 /* This magic number is the number of intermediate frames used
610 in each half of the animation. For short moves it's reduced
611 by 1. The total number of frames will be factor * 2 + 1. */
614 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
616 MenuItem fileMenu[] = {
617 {N_("New Game Ctrl+N"), "New Game", ResetProc},
618 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
619 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
620 {"----", NULL, NothingProc},
621 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
622 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
623 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
624 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
625 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
626 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
627 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
628 {"----", NULL, NothingProc},
629 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
630 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
631 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
632 {"----", NULL, NothingProc},
633 {N_("Mail Move"), "Mail Move", MailMoveProc},
634 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
635 {"----", NULL, NothingProc},
636 {N_("Quit Ctr+Q"), "Exit", QuitProc},
640 MenuItem editMenu[] = {
641 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
642 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
643 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
644 {"----", NULL, NothingProc},
645 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
646 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
647 {"----", NULL, NothingProc},
648 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
649 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
650 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
651 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
652 {N_("Edit Book"), "Edit Book", EditBookProc},
653 {"----", NULL, NothingProc},
654 {N_("Revert Home"), "Revert", RevertProc},
655 {N_("Annotate"), "Annotate", AnnotateProc},
656 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
657 {"----", NULL, NothingProc},
658 {N_("Backward Alt+Left"), "Backward", BackwardProc},
659 {N_("Forward Alt+Right"), "Forward", ForwardProc},
660 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
661 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
665 MenuItem viewMenu[] = {
666 {N_("Flip View F2"), "Flip View", FlipViewProc},
667 {"----", NULL, NothingProc},
668 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
669 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
670 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
671 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
672 {N_("ICS text menu"), "ICStex", IcsTextProc},
673 {"----", NULL, NothingProc},
674 {N_("Tags"), "Show Tags", EditTagsProc},
675 {N_("Comments"), "Show Comments", EditCommentProc},
676 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
677 {"----", NULL, NothingProc},
678 {N_("Board..."), "Board Options", BoardOptionsProc},
679 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
683 MenuItem modeMenu[] = {
684 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
685 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
686 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
687 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
688 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
689 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
690 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
691 {N_("Training"), "Training", TrainingProc},
692 {N_("ICS Client"), "ICS Client", IcsClientProc},
693 {"----", NULL, NothingProc},
694 {N_("Machine Match"), "Machine Match", MatchProc},
695 {N_("Pause Pause"), "Pause", PauseProc},
699 MenuItem actionMenu[] = {
700 {N_("Accept F3"), "Accept", AcceptProc},
701 {N_("Decline F4"), "Decline", DeclineProc},
702 {N_("Rematch F12"), "Rematch", RematchProc},
703 {"----", NULL, NothingProc},
704 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
705 {N_("Draw F6"), "Draw", DrawProc},
706 {N_("Adjourn F7"), "Adjourn", AdjournProc},
707 {N_("Abort F8"),"Abort", AbortProc},
708 {N_("Resign F9"), "Resign", ResignProc},
709 {"----", NULL, NothingProc},
710 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
711 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
712 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
713 {"----", NULL, NothingProc},
714 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
715 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
716 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
720 MenuItem engineMenu[] = {
721 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
722 {"----", NULL, NothingProc},
723 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
724 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
725 {"----", NULL, NothingProc},
726 {N_("Hint"), "Hint", HintProc},
727 {N_("Book"), "Book", BookProc},
728 {"----", NULL, NothingProc},
729 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
730 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
734 MenuItem optionsMenu[] = {
735 #define OPTIONSDIALOG
737 {N_("General ..."), "General", OptionsProc},
739 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
740 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
741 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
742 {N_("ICS ..."), "ICS", IcsOptionsProc},
743 {N_("Match ..."), "Match", MatchOptionsProc},
744 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
745 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
746 // {N_(" ..."), "", OptionsProc},
747 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
748 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
749 {"----", NULL, NothingProc},
750 #ifndef OPTIONSDIALOG
751 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
752 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
753 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
754 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
755 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
756 {N_("Blindfold"), "Blindfold", BlindfoldProc},
757 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
759 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
761 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
762 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
763 {N_("Move Sound"), "Move Sound", MoveSoundProc},
764 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
765 {N_("One-Click Moving"), "OneClick", OneClickProc},
766 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
767 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
768 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
769 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
770 // {N_("Premove"), "Premove", PremoveProc},
771 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
772 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
773 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
774 {"----", NULL, NothingProc},
776 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
777 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
781 MenuItem helpMenu[] = {
782 {N_("Info XBoard"), "Info XBoard", InfoProc},
783 {N_("Man XBoard F1"), "Man XBoard", ManProc},
784 {"----", NULL, NothingProc},
785 {N_("About XBoard"), "About XBoard", AboutProc},
790 {N_("File"), "File", fileMenu},
791 {N_("Edit"), "Edit", editMenu},
792 {N_("View"), "View", viewMenu},
793 {N_("Mode"), "Mode", modeMenu},
794 {N_("Action"), "Action", actionMenu},
795 {N_("Engine"), "Engine", engineMenu},
796 {N_("Options"), "Options", optionsMenu},
797 {N_("Help"), "Help", helpMenu},
801 #define PAUSE_BUTTON "P"
802 MenuItem buttonBar[] = {
803 {"<<", "<<", ToStartProc},
804 {"<", "<", BackwardProc},
805 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
806 {">", ">", ForwardProc},
807 {">>", ">>", ToEndProc},
811 #define PIECE_MENU_SIZE 18
812 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
813 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
814 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
815 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
816 N_("Empty square"), N_("Clear board") },
817 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
818 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
819 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
820 N_("Empty square"), N_("Clear board") }
822 /* must be in same order as pieceMenuStrings! */
823 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
824 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
825 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
826 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
827 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
828 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
829 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
830 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
831 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
834 #define DROP_MENU_SIZE 6
835 String dropMenuStrings[DROP_MENU_SIZE] = {
836 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
838 /* must be in same order as dropMenuStrings! */
839 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
840 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
841 WhiteRook, WhiteQueen
849 DropMenuEnables dmEnables[] = {
867 { XtNborderWidth, 0 },
868 { XtNdefaultDistance, 0 },
872 { XtNborderWidth, 0 },
873 { XtNresizable, (XtArgVal) True },
877 { XtNborderWidth, 0 },
883 { XtNjustify, (XtArgVal) XtJustifyRight },
884 { XtNlabel, (XtArgVal) "..." },
885 { XtNresizable, (XtArgVal) True },
886 { XtNresize, (XtArgVal) False }
889 Arg messageArgs[] = {
890 { XtNjustify, (XtArgVal) XtJustifyLeft },
891 { XtNlabel, (XtArgVal) "..." },
892 { XtNresizable, (XtArgVal) True },
893 { XtNresize, (XtArgVal) False }
897 { XtNborderWidth, 0 },
898 { XtNjustify, (XtArgVal) XtJustifyLeft }
901 XtResource clientResources[] = {
902 { "flashCount", "flashCount", XtRInt, sizeof(int),
903 XtOffset(AppDataPtr, flashCount), XtRImmediate,
904 (XtPointer) FLASH_COUNT },
907 XrmOptionDescRec shellOptions[] = {
908 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
909 { "-flash", "flashCount", XrmoptionNoArg, "3" },
910 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
913 XtActionsRec boardActions[] = {
914 { "DrawPosition", DrawPositionProc },
915 { "HandleUserMove", HandleUserMove },
916 { "AnimateUserMove", AnimateUserMove },
917 { "HandlePV", HandlePV },
918 { "SelectPV", SelectPV },
919 { "StopPV", StopPV },
920 { "FileNameAction", FileNameAction },
921 { "AskQuestionProc", AskQuestionProc },
922 { "AskQuestionReplyAction", AskQuestionReplyAction },
923 { "PieceMenuPopup", PieceMenuPopup },
924 { "WhiteClock", WhiteClock },
925 { "BlackClock", BlackClock },
926 { "ResetProc", ResetProc },
927 { "NewVariantProc", NewVariantProc },
928 { "LoadGameProc", LoadGameProc },
929 { "LoadNextGameProc", LoadNextGameProc },
930 { "LoadPrevGameProc", LoadPrevGameProc },
931 { "LoadSelectedProc", LoadSelectedProc },
932 { "SetFilterProc", SetFilterProc },
933 { "ReloadGameProc", ReloadGameProc },
934 { "LoadPositionProc", LoadPositionProc },
935 { "LoadNextPositionProc", LoadNextPositionProc },
936 { "LoadPrevPositionProc", LoadPrevPositionProc },
937 { "ReloadPositionProc", ReloadPositionProc },
938 { "CopyPositionProc", CopyPositionProc },
939 { "PastePositionProc", PastePositionProc },
940 { "CopyGameProc", CopyGameProc },
941 { "CopyGameListProc", CopyGameListProc },
942 { "PasteGameProc", PasteGameProc },
943 { "SaveGameProc", SaveGameProc },
944 { "SavePositionProc", SavePositionProc },
945 { "MailMoveProc", MailMoveProc },
946 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
947 { "QuitProc", QuitProc },
948 { "MachineWhiteProc", MachineWhiteProc },
949 { "MachineBlackProc", MachineBlackProc },
950 { "AnalysisModeProc", AnalyzeModeProc },
951 { "AnalyzeFileProc", AnalyzeFileProc },
952 { "TwoMachinesProc", TwoMachinesProc },
953 { "IcsClientProc", IcsClientProc },
954 { "EditGameProc", EditGameProc },
955 { "EditPositionProc", EditPositionProc },
956 { "TrainingProc", EditPositionProc },
957 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
958 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
959 { "ShowGameListProc", ShowGameListProc },
960 { "ShowMoveListProc", HistoryShowProc},
961 { "EditTagsProc", EditCommentProc },
962 { "EditBookProc", EditBookProc },
963 { "EditCommentProc", EditCommentProc },
964 { "IcsInputBoxProc", IcsInputBoxProc },
965 { "PauseProc", PauseProc },
966 { "AcceptProc", AcceptProc },
967 { "DeclineProc", DeclineProc },
968 { "RematchProc", RematchProc },
969 { "CallFlagProc", CallFlagProc },
970 { "DrawProc", DrawProc },
971 { "AdjournProc", AdjournProc },
972 { "AbortProc", AbortProc },
973 { "ResignProc", ResignProc },
974 { "AdjuWhiteProc", AdjuWhiteProc },
975 { "AdjuBlackProc", AdjuBlackProc },
976 { "AdjuDrawProc", AdjuDrawProc },
977 { "TypeInProc", TypeInProc },
978 { "EnterKeyProc", EnterKeyProc },
979 { "UpKeyProc", UpKeyProc },
980 { "DownKeyProc", DownKeyProc },
981 { "StopObservingProc", StopObservingProc },
982 { "StopExaminingProc", StopExaminingProc },
983 { "UploadProc", UploadProc },
984 { "BackwardProc", BackwardProc },
985 { "ForwardProc", ForwardProc },
986 { "TempBackwardProc", TempBackwardProc },
987 { "TempForwardProc", TempForwardProc },
988 { "ToStartProc", ToStartProc },
989 { "ToEndProc", ToEndProc },
990 { "RevertProc", RevertProc },
991 { "AnnotateProc", AnnotateProc },
992 { "TruncateGameProc", TruncateGameProc },
993 { "MoveNowProc", MoveNowProc },
994 { "RetractMoveProc", RetractMoveProc },
995 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
996 { "UciMenuProc", (XtActionProc) UciMenuProc },
997 { "TimeControlProc", (XtActionProc) TimeControlProc },
998 { "FlipViewProc", FlipViewProc },
999 { "PonderNextMoveProc", PonderNextMoveProc },
1000 #ifndef OPTIONSDIALOG
1001 { "AlwaysQueenProc", AlwaysQueenProc },
1002 { "AnimateDraggingProc", AnimateDraggingProc },
1003 { "AnimateMovingProc", AnimateMovingProc },
1004 { "AutoflagProc", AutoflagProc },
1005 { "AutoflipProc", AutoflipProc },
1006 { "BlindfoldProc", BlindfoldProc },
1007 { "FlashMovesProc", FlashMovesProc },
1009 { "HighlightDraggingProc", HighlightDraggingProc },
1011 { "HighlightLastMoveProc", HighlightLastMoveProc },
1012 // { "IcsAlarmProc", IcsAlarmProc },
1013 { "MoveSoundProc", MoveSoundProc },
1014 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1015 { "PopupExitMessageProc", PopupExitMessageProc },
1016 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1017 // { "PremoveProc", PremoveProc },
1018 { "ShowCoordsProc", ShowCoordsProc },
1019 { "ShowThinkingProc", ShowThinkingProc },
1020 { "HideThinkingProc", HideThinkingProc },
1021 { "TestLegalityProc", TestLegalityProc },
1023 { "SaveSettingsProc", SaveSettingsProc },
1024 { "SaveOnExitProc", SaveOnExitProc },
1025 { "InfoProc", InfoProc },
1026 { "ManProc", ManProc },
1027 { "HintProc", HintProc },
1028 { "BookProc", BookProc },
1029 { "AboutGameProc", AboutGameProc },
1030 { "AboutProc", AboutProc },
1031 { "DebugProc", DebugProc },
1032 { "NothingProc", NothingProc },
1033 { "CommentClick", (XtActionProc) CommentClick },
1034 { "CommentPopDown", (XtActionProc) CommentPopDown },
1035 { "TagsPopDown", (XtActionProc) TagsPopDown },
1036 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1037 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1038 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1039 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1040 { "GameListPopDown", (XtActionProc) GameListPopDown },
1041 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1042 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1043 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1044 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1045 { "GenericPopDown", (XtActionProc) GenericPopDown },
1046 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1047 { "SelectMove", (XtActionProc) SelectMove },
1050 char globalTranslations[] =
1051 ":<Key>F9: ResignProc() \n \
1052 :Ctrl<Key>n: ResetProc() \n \
1053 :Meta<Key>V: NewVariantProc() \n \
1054 :Ctrl<Key>o: LoadGameProc() \n \
1055 :Meta<Key>Next: LoadNextGameProc() \n \
1056 :Meta<Key>Prior: LoadPrevGameProc() \n \
1057 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1058 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1059 :Ctrl<Key>s: SaveGameProc() \n \
1060 :Ctrl<Key>c: CopyGameProc() \n \
1061 :Ctrl<Key>v: PasteGameProc() \n \
1062 :Ctrl<Key>O: LoadPositionProc() \n \
1063 :Shift<Key>Next: LoadNextPositionProc() \n \
1064 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1065 :Ctrl<Key>S: SavePositionProc() \n \
1066 :Ctrl<Key>C: CopyPositionProc() \n \
1067 :Ctrl<Key>V: PastePositionProc() \n \
1068 :Ctrl<Key>q: QuitProc() \n \
1069 :Ctrl<Key>w: MachineWhiteProc() \n \
1070 :Ctrl<Key>b: MachineBlackProc() \n \
1071 :Ctrl<Key>t: TwoMachinesProc() \n \
1072 :Ctrl<Key>a: AnalysisModeProc() \n \
1073 :Ctrl<Key>g: AnalyzeFileProc() \n \
1074 :Ctrl<Key>e: EditGameProc() \n \
1075 :Ctrl<Key>E: EditPositionProc() \n \
1076 :Meta<Key>O: EngineOutputProc() \n \
1077 :Meta<Key>E: EvalGraphProc() \n \
1078 :Meta<Key>G: ShowGameListProc() \n \
1079 :Meta<Key>H: ShowMoveListProc() \n \
1080 :<Key>Pause: PauseProc() \n \
1081 :<Key>F3: AcceptProc() \n \
1082 :<Key>F4: DeclineProc() \n \
1083 :<Key>F12: RematchProc() \n \
1084 :<Key>F5: CallFlagProc() \n \
1085 :<Key>F6: DrawProc() \n \
1086 :<Key>F7: AdjournProc() \n \
1087 :<Key>F8: AbortProc() \n \
1088 :<Key>F10: StopObservingProc() \n \
1089 :<Key>F11: StopExaminingProc() \n \
1090 :Meta Ctrl<Key>F12: DebugProc() \n \
1091 :Meta<Key>End: ToEndProc() \n \
1092 :Meta<Key>Right: ForwardProc() \n \
1093 :Meta<Key>Home: ToStartProc() \n \
1094 :Meta<Key>Left: BackwardProc() \n \
1095 :<Key>Left: BackwardProc() \n \
1096 :<Key>Right: ForwardProc() \n \
1097 :<Key>Home: RevertProc() \n \
1098 :<Key>End: TruncateGameProc() \n \
1099 :Ctrl<Key>m: MoveNowProc() \n \
1100 :Ctrl<Key>x: RetractMoveProc() \n \
1101 :Meta<Key>J: EngineMenuProc() \n \
1102 :Meta<Key>U: UciMenuProc() \n \
1103 :Meta<Key>T: TimeControlProc() \n \
1104 :Ctrl<Key>P: PonderNextMoveProc() \n "
1105 #ifndef OPTIONSDIALOG
1107 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1108 :Ctrl<Key>F: AutoflagProc() \n \
1109 :Ctrl<Key>A: AnimateMovingProc() \n \
1110 :Ctrl<Key>L: TestLegalityProc() \n \
1111 :Ctrl<Key>H: HideThinkingProc() \n "
1114 :<Key>F1: ManProc() \n \
1115 :<Key>F2: FlipViewProc() \n \
1116 :Ctrl<KeyDown>.: TempBackwardProc() \n \
1117 :Ctrl<KeyUp>.: TempForwardProc() \n \
1118 :Ctrl<Key>1: AskQuestionProc(\"Direct command\",\
1119 \"Send to chess program:\",,1) \n \
1120 :Ctrl<Key>2: AskQuestionProc(\"Direct command\",\
1121 \"Send to second chess program:\",,2) \n";
1123 char boardTranslations[] =
1124 "<Btn1Down>: HandleUserMove(0) \n \
1125 Shift<Btn1Up>: HandleUserMove(1) \n \
1126 <Btn1Up>: HandleUserMove(0) \n \
1127 <Btn1Motion>: AnimateUserMove() \n \
1128 <Btn3Motion>: HandlePV() \n \
1129 <Btn3Up>: PieceMenuPopup(menuB) \n \
1130 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1131 PieceMenuPopup(menuB) \n \
1132 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1133 PieceMenuPopup(menuW) \n \
1134 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1135 PieceMenuPopup(menuW) \n \
1136 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1137 PieceMenuPopup(menuB) \n";
1139 char whiteTranslations[] =
1140 "Shift<BtnDown>: WhiteClock(1)\n \
1141 <BtnDown>: WhiteClock(0)\n";
1142 char blackTranslations[] =
1143 "Shift<BtnDown>: BlackClock(1)\n \
1144 <BtnDown>: BlackClock(0)\n";
1146 char ICSInputTranslations[] =
1147 "<Key>Up: UpKeyProc() \n "
1148 "<Key>Down: DownKeyProc() \n "
1149 "<Key>Return: EnterKeyProc() \n";
1151 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1152 // as the widget is destroyed before the up-click can call extend-end
1153 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1155 String xboardResources[] = {
1156 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1157 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1158 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1163 /* Max possible square size */
1164 #define MAXSQSIZE 256
1166 static int xpm_avail[MAXSQSIZE];
1168 #ifdef HAVE_DIR_STRUCT
1170 /* Extract piece size from filename */
1172 xpm_getsize(name, len, ext)
1183 if ((p=strchr(name, '.')) == NULL ||
1184 StrCaseCmp(p+1, ext) != 0)
1190 while (*p && isdigit(*p))
1197 /* Setup xpm_avail */
1199 xpm_getavail(dirname, ext)
1207 for (i=0; i<MAXSQSIZE; ++i)
1210 if (appData.debugMode)
1211 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1213 dir = opendir(dirname);
1216 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1217 programName, dirname);
1221 while ((ent=readdir(dir)) != NULL) {
1222 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1223 if (i > 0 && i < MAXSQSIZE)
1233 xpm_print_avail(fp, ext)
1239 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1240 for (i=1; i<MAXSQSIZE; ++i) {
1246 /* Return XPM piecesize closest to size */
1248 xpm_closest_to(dirname, size, ext)
1254 int sm_diff = MAXSQSIZE;
1258 xpm_getavail(dirname, ext);
1260 if (appData.debugMode)
1261 xpm_print_avail(stderr, ext);
1263 for (i=1; i<MAXSQSIZE; ++i) {
1266 diff = (diff<0) ? -diff : diff;
1267 if (diff < sm_diff) {
1275 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1281 #else /* !HAVE_DIR_STRUCT */
1282 /* If we are on a system without a DIR struct, we can't
1283 read the directory, so we can't collect a list of
1284 filenames, etc., so we can't do any size-fitting. */
1286 xpm_closest_to(dirname, size, ext)
1291 fprintf(stderr, _("\
1292 Warning: No DIR structure found on this system --\n\
1293 Unable to autosize for XPM/XIM pieces.\n\
1294 Please report this error to %s.\n\
1295 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1298 #endif /* HAVE_DIR_STRUCT */
1300 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1301 "magenta", "cyan", "white" };
1305 TextColors textColors[(int)NColorClasses];
1307 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1309 parse_color(str, which)
1313 char *p, buf[100], *d;
1316 if (strlen(str) > 99) /* watch bounds on buf */
1321 for (i=0; i<which; ++i) {
1328 /* Could be looking at something like:
1330 .. in which case we want to stop on a comma also */
1331 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1335 return -1; /* Use default for empty field */
1338 if (which == 2 || isdigit(*p))
1341 while (*p && isalpha(*p))
1346 for (i=0; i<8; ++i) {
1347 if (!StrCaseCmp(buf, cnames[i]))
1348 return which? (i+40) : (i+30);
1350 if (!StrCaseCmp(buf, "default")) return -1;
1352 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1357 parse_cpair(cc, str)
1361 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1362 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1367 /* bg and attr are optional */
1368 textColors[(int)cc].bg = parse_color(str, 1);
1369 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1370 textColors[(int)cc].attr = 0;
1376 /* Arrange to catch delete-window events */
1377 Atom wm_delete_window;
1379 CatchDeleteWindow(Widget w, String procname)
1382 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1383 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1384 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1391 XtSetArg(args[0], XtNiconic, False);
1392 XtSetValues(shellWidget, args, 1);
1394 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1397 //---------------------------------------------------------------------------------------------------------
1398 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1401 #define CW_USEDEFAULT (1<<31)
1402 #define ICS_TEXT_MENU_SIZE 90
1403 #define DEBUG_FILE "xboard.debug"
1404 #define SetCurrentDirectory chdir
1405 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1409 // these two must some day move to frontend.h, when they are implemented
1410 Boolean GameListIsUp();
1412 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1415 // front-end part of option handling
1417 // [HGM] This platform-dependent table provides the location for storing the color info
1418 extern char *crWhite, * crBlack;
1422 &appData.whitePieceColor,
1423 &appData.blackPieceColor,
1424 &appData.lightSquareColor,
1425 &appData.darkSquareColor,
1426 &appData.highlightSquareColor,
1427 &appData.premoveHighlightColor,
1428 &appData.lowTimeWarningColor,
1439 // [HGM] font: keep a font for each square size, even non-stndard ones
1440 #define NUM_SIZES 18
1441 #define MAX_SIZE 130
1442 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1443 char *fontTable[NUM_FONTS][MAX_SIZE];
1446 ParseFont(char *name, int number)
1447 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1449 if(sscanf(name, "size%d:", &size)) {
1450 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1451 // defer processing it until we know if it matches our board size
1452 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1453 fontTable[number][size] = strdup(strchr(name, ':')+1);
1454 fontValid[number][size] = True;
1459 case 0: // CLOCK_FONT
1460 appData.clockFont = strdup(name);
1462 case 1: // MESSAGE_FONT
1463 appData.font = strdup(name);
1465 case 2: // COORD_FONT
1466 appData.coordFont = strdup(name);
1471 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1476 { // only 2 fonts currently
1477 appData.clockFont = CLOCK_FONT_NAME;
1478 appData.coordFont = COORD_FONT_NAME;
1479 appData.font = DEFAULT_FONT_NAME;
1484 { // no-op, until we identify the code for this already in XBoard and move it here
1488 ParseColor(int n, char *name)
1489 { // in XBoard, just copy the color-name string
1490 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1494 ParseTextAttribs(ColorClass cc, char *s)
1496 (&appData.colorShout)[cc] = strdup(s);
1500 ParseBoardSize(void *addr, char *name)
1502 appData.boardSize = strdup(name);
1507 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1511 SetCommPortDefaults()
1512 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1515 // [HGM] args: these three cases taken out to stay in front-end
1517 SaveFontArg(FILE *f, ArgDescriptor *ad)
1520 int i, n = (int)(intptr_t)ad->argLoc;
1522 case 0: // CLOCK_FONT
1523 name = appData.clockFont;
1525 case 1: // MESSAGE_FONT
1526 name = appData.font;
1528 case 2: // COORD_FONT
1529 name = appData.coordFont;
1534 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1535 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1536 fontTable[n][squareSize] = strdup(name);
1537 fontValid[n][squareSize] = True;
1540 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1541 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1546 { // nothing to do, as the sounds are at all times represented by their text-string names already
1550 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1551 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1552 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1556 SaveColor(FILE *f, ArgDescriptor *ad)
1557 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1558 if(colorVariable[(int)(intptr_t)ad->argLoc])
1559 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1563 SaveBoardSize(FILE *f, char *name, void *addr)
1564 { // wrapper to shield back-end from BoardSize & sizeInfo
1565 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1569 ParseCommPortSettings(char *s)
1570 { // no such option in XBoard (yet)
1573 extern Widget engineOutputShell;
1576 GetActualPlacement(Widget wg, WindowPlacement *wp)
1586 XtSetArg(args[i], XtNx, &x); i++;
1587 XtSetArg(args[i], XtNy, &y); i++;
1588 XtSetArg(args[i], XtNwidth, &w); i++;
1589 XtSetArg(args[i], XtNheight, &h); i++;
1590 XtGetValues(wg, args, i);
1599 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1600 // In XBoard this will have to wait until awareness of window parameters is implemented
1601 GetActualPlacement(shellWidget, &wpMain);
1602 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1603 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1604 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1605 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1606 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1607 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1611 PrintCommPortSettings(FILE *f, char *name)
1612 { // This option does not exist in XBoard
1616 MySearchPath(char *installDir, char *name, char *fullname)
1617 { // just append installDir and name. Perhaps ExpandPath should be used here?
1618 name = ExpandPathName(name);
1619 if(name && name[0] == '/')
1620 safeStrCpy(fullname, name, MSG_SIZ );
1622 sprintf(fullname, "%s%c%s", installDir, '/', name);
1628 MyGetFullPathName(char *name, char *fullname)
1629 { // should use ExpandPath?
1630 name = ExpandPathName(name);
1631 safeStrCpy(fullname, name, MSG_SIZ );
1636 EnsureOnScreen(int *x, int *y, int minX, int minY)
1643 { // [HGM] args: allows testing if main window is realized from back-end
1644 return xBoardWindow != 0;
1648 PopUpStartupDialog()
1649 { // start menu not implemented in XBoard
1653 ConvertToLine(int argc, char **argv)
1655 static char line[128*1024], buf[1024];
1659 for(i=1; i<argc; i++)
1661 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1662 && argv[i][0] != '{' )
1663 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1665 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1666 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1669 line[strlen(line)-1] = NULLCHAR;
1673 //--------------------------------------------------------------------------------------------
1675 extern Boolean twoBoards, partnerUp;
1678 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1680 #define BoardSize int
1681 void InitDrawingSizes(BoardSize boardSize, int flags)
1682 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1683 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1685 XtGeometryResult gres;
1687 static Dimension oldWidth, oldHeight;
1688 static VariantClass oldVariant;
1689 static int oldDual = -1, oldMono = -1;
1691 if(!formWidget) return;
1693 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1694 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1695 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1697 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1699 * Enable shell resizing.
1701 shellArgs[0].value = (XtArgVal) &w;
1702 shellArgs[1].value = (XtArgVal) &h;
1703 XtGetValues(shellWidget, shellArgs, 2);
1705 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1706 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1707 XtSetValues(shellWidget, &shellArgs[2], 4);
1709 XtSetArg(args[0], XtNdefaultDistance, &sep);
1710 XtGetValues(formWidget, args, 1);
1712 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1714 hOffset = boardWidth + 10;
1715 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1716 secondSegments[i] = gridSegments[i];
1717 secondSegments[i].x1 += hOffset;
1718 secondSegments[i].x2 += hOffset;
1721 XtSetArg(args[0], XtNwidth, boardWidth);
1722 XtSetArg(args[1], XtNheight, boardHeight);
1723 XtSetValues(boardWidget, args, 2);
1725 timerWidth = (boardWidth - sep) / 2;
1726 XtSetArg(args[0], XtNwidth, timerWidth);
1727 XtSetValues(whiteTimerWidget, args, 1);
1728 XtSetValues(blackTimerWidget, args, 1);
1730 XawFormDoLayout(formWidget, False);
1732 if (appData.titleInWindow) {
1734 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1735 XtSetArg(args[i], XtNheight, &h); i++;
1736 XtGetValues(titleWidget, args, i);
1738 w = boardWidth - 2*bor;
1740 XtSetArg(args[0], XtNwidth, &w);
1741 XtGetValues(menuBarWidget, args, 1);
1742 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1745 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1746 if (gres != XtGeometryYes && appData.debugMode) {
1748 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1749 programName, gres, w, h, wr, hr);
1753 XawFormDoLayout(formWidget, True);
1756 * Inhibit shell resizing.
1758 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1759 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1760 shellArgs[4].value = shellArgs[2].value = w;
1761 shellArgs[5].value = shellArgs[3].value = h;
1762 XtSetValues(shellWidget, &shellArgs[0], 6);
1765 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1768 if(gameInfo.variant != oldVariant) { // and only if variant changed
1771 for(i=0; i<4; i++) {
1773 for(p=0; p<=(int)WhiteKing; p++)
1774 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1775 if(gameInfo.variant == VariantShogi) {
1776 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1777 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1778 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1779 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1780 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1783 if(gameInfo.variant == VariantGothic) {
1784 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1787 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1788 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1789 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1792 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1793 for(p=0; p<=(int)WhiteKing; p++)
1794 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1795 if(gameInfo.variant == VariantShogi) {
1796 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1797 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1798 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1799 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1800 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1803 if(gameInfo.variant == VariantGothic) {
1804 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1807 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1808 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1809 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1814 for(i=0; i<2; i++) {
1816 for(p=0; p<=(int)WhiteKing; p++)
1817 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1818 if(gameInfo.variant == VariantShogi) {
1819 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1820 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1821 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1822 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1823 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1826 if(gameInfo.variant == VariantGothic) {
1827 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1830 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1831 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1832 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1838 if(appData.monoMode == oldMono)
1841 oldMono = appData.monoMode;
1845 void ParseIcsTextColors()
1846 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1847 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1848 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1849 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1850 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1851 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1852 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1853 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1854 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1855 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1856 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1858 if (appData.colorize) {
1860 _("%s: can't parse color names; disabling colorization\n"),
1863 appData.colorize = FALSE;
1868 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1869 XrmValue vFrom, vTo;
1870 int forceMono = False;
1872 if (!appData.monoMode) {
1873 vFrom.addr = (caddr_t) appData.lightSquareColor;
1874 vFrom.size = strlen(appData.lightSquareColor);
1875 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1876 if (vTo.addr == NULL) {
1877 appData.monoMode = True;
1880 lightSquareColor = *(Pixel *) vTo.addr;
1883 if (!appData.monoMode) {
1884 vFrom.addr = (caddr_t) appData.darkSquareColor;
1885 vFrom.size = strlen(appData.darkSquareColor);
1886 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1887 if (vTo.addr == NULL) {
1888 appData.monoMode = True;
1891 darkSquareColor = *(Pixel *) vTo.addr;
1894 if (!appData.monoMode) {
1895 vFrom.addr = (caddr_t) appData.whitePieceColor;
1896 vFrom.size = strlen(appData.whitePieceColor);
1897 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1898 if (vTo.addr == NULL) {
1899 appData.monoMode = True;
1902 whitePieceColor = *(Pixel *) vTo.addr;
1905 if (!appData.monoMode) {
1906 vFrom.addr = (caddr_t) appData.blackPieceColor;
1907 vFrom.size = strlen(appData.blackPieceColor);
1908 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1909 if (vTo.addr == NULL) {
1910 appData.monoMode = True;
1913 blackPieceColor = *(Pixel *) vTo.addr;
1917 if (!appData.monoMode) {
1918 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1919 vFrom.size = strlen(appData.highlightSquareColor);
1920 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1921 if (vTo.addr == NULL) {
1922 appData.monoMode = True;
1925 highlightSquareColor = *(Pixel *) vTo.addr;
1929 if (!appData.monoMode) {
1930 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1931 vFrom.size = strlen(appData.premoveHighlightColor);
1932 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1933 if (vTo.addr == NULL) {
1934 appData.monoMode = True;
1937 premoveHighlightColor = *(Pixel *) vTo.addr;
1945 { // [HGM] taken out of main
1947 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1948 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1949 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1951 if (appData.bitmapDirectory[0] != NULLCHAR) {
1955 CreateXPMBoard(appData.liteBackTextureFile, 1);
1956 CreateXPMBoard(appData.darkBackTextureFile, 0);
1960 /* Create regular pieces */
1961 if (!useImages) CreatePieces();
1970 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1971 XSetWindowAttributes window_attributes;
1973 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1974 XrmValue vFrom, vTo;
1975 XtGeometryResult gres;
1978 int forceMono = False;
1980 srandom(time(0)); // [HGM] book: make random truly random
1982 setbuf(stdout, NULL);
1983 setbuf(stderr, NULL);
1986 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1987 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1991 programName = strrchr(argv[0], '/');
1992 if (programName == NULL)
1993 programName = argv[0];
1998 XtSetLanguageProc(NULL, NULL, NULL);
1999 bindtextdomain(PACKAGE, LOCALEDIR);
2000 textdomain(PACKAGE);
2004 XtAppInitialize(&appContext, "XBoard", shellOptions,
2005 XtNumber(shellOptions),
2006 &argc, argv, xboardResources, NULL, 0);
2007 appData.boardSize = "";
2008 InitAppData(ConvertToLine(argc, argv));
2010 if (p == NULL) p = "/tmp";
2011 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2012 gameCopyFilename = (char*) malloc(i);
2013 gamePasteFilename = (char*) malloc(i);
2014 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2015 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2017 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2018 clientResources, XtNumber(clientResources),
2021 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2022 static char buf[MSG_SIZ];
2023 EscapeExpand(buf, appData.firstInitString);
2024 appData.firstInitString = strdup(buf);
2025 EscapeExpand(buf, appData.secondInitString);
2026 appData.secondInitString = strdup(buf);
2027 EscapeExpand(buf, appData.firstComputerString);
2028 appData.firstComputerString = strdup(buf);
2029 EscapeExpand(buf, appData.secondComputerString);
2030 appData.secondComputerString = strdup(buf);
2033 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2036 if (chdir(chessDir) != 0) {
2037 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2043 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2044 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2045 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2046 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2049 setbuf(debugFP, NULL);
2053 if (appData.debugMode) {
2054 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2058 /* [HGM,HR] make sure board size is acceptable */
2059 if(appData.NrFiles > BOARD_FILES ||
2060 appData.NrRanks > BOARD_RANKS )
2061 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2064 /* This feature does not work; animation needs a rewrite */
2065 appData.highlightDragging = FALSE;
2069 xDisplay = XtDisplay(shellWidget);
2070 xScreen = DefaultScreen(xDisplay);
2071 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2073 gameInfo.variant = StringToVariant(appData.variant);
2074 InitPosition(FALSE);
2077 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2079 if (isdigit(appData.boardSize[0])) {
2080 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2081 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2082 &fontPxlSize, &smallLayout, &tinyLayout);
2084 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2085 programName, appData.boardSize);
2089 /* Find some defaults; use the nearest known size */
2090 SizeDefaults *szd, *nearest;
2091 int distance = 99999;
2092 nearest = szd = sizeDefaults;
2093 while (szd->name != NULL) {
2094 if (abs(szd->squareSize - squareSize) < distance) {
2096 distance = abs(szd->squareSize - squareSize);
2097 if (distance == 0) break;
2101 if (i < 2) lineGap = nearest->lineGap;
2102 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2103 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2104 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2105 if (i < 6) smallLayout = nearest->smallLayout;
2106 if (i < 7) tinyLayout = nearest->tinyLayout;
2109 SizeDefaults *szd = sizeDefaults;
2110 if (*appData.boardSize == NULLCHAR) {
2111 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2112 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2115 if (szd->name == NULL) szd--;
2116 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2118 while (szd->name != NULL &&
2119 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2120 if (szd->name == NULL) {
2121 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2122 programName, appData.boardSize);
2126 squareSize = szd->squareSize;
2127 lineGap = szd->lineGap;
2128 clockFontPxlSize = szd->clockFontPxlSize;
2129 coordFontPxlSize = szd->coordFontPxlSize;
2130 fontPxlSize = szd->fontPxlSize;
2131 smallLayout = szd->smallLayout;
2132 tinyLayout = szd->tinyLayout;
2133 // [HGM] font: use defaults from settings file if available and not overruled
2135 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2136 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2137 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2138 appData.font = fontTable[MESSAGE_FONT][squareSize];
2139 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2140 appData.coordFont = fontTable[COORD_FONT][squareSize];
2142 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2143 if (strlen(appData.pixmapDirectory) > 0) {
2144 p = ExpandPathName(appData.pixmapDirectory);
2146 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2147 appData.pixmapDirectory);
2150 if (appData.debugMode) {
2151 fprintf(stderr, _("\
2152 XBoard square size (hint): %d\n\
2153 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2155 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2156 if (appData.debugMode) {
2157 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2160 defaultLineGap = lineGap;
2161 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2163 /* [HR] height treated separately (hacked) */
2164 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2165 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2166 if (appData.showJail == 1) {
2167 /* Jail on top and bottom */
2168 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2169 XtSetArg(boardArgs[2], XtNheight,
2170 boardHeight + 2*(lineGap + squareSize));
2171 } else if (appData.showJail == 2) {
2173 XtSetArg(boardArgs[1], XtNwidth,
2174 boardWidth + 2*(lineGap + squareSize));
2175 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2178 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2179 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2183 * Determine what fonts to use.
2186 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2187 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2188 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2189 fontSet = CreateFontSet(appData.font);
2190 clockFontSet = CreateFontSet(appData.clockFont);
2192 /* For the coordFont, use the 0th font of the fontset. */
2193 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2194 XFontStruct **font_struct_list;
2195 char **font_name_list;
2196 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2197 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2198 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2201 appData.font = FindFont(appData.font, fontPxlSize);
2202 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2203 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2204 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2205 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2206 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2207 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2209 countFontID = coordFontID; // [HGM] holdings
2210 countFontStruct = coordFontStruct;
2212 xdb = XtDatabase(xDisplay);
2214 XrmPutLineResource(&xdb, "*international: True");
2215 vTo.size = sizeof(XFontSet);
2216 vTo.addr = (XtPointer) &fontSet;
2217 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2219 XrmPutStringResource(&xdb, "*font", appData.font);
2223 * Detect if there are not enough colors available and adapt.
2225 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2226 appData.monoMode = True;
2229 forceMono = MakeColors();
2232 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2234 appData.monoMode = True;
2237 if (appData.lowTimeWarning && !appData.monoMode) {
2238 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2239 vFrom.size = strlen(appData.lowTimeWarningColor);
2240 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2241 if (vTo.addr == NULL)
2242 appData.monoMode = True;
2244 lowTimeWarningColor = *(Pixel *) vTo.addr;
2247 if (appData.monoMode && appData.debugMode) {
2248 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2249 (unsigned long) XWhitePixel(xDisplay, xScreen),
2250 (unsigned long) XBlackPixel(xDisplay, xScreen));
2253 ParseIcsTextColors();
2254 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2255 textColors[ColorNone].attr = 0;
2257 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2263 layoutName = "tinyLayout";
2264 } else if (smallLayout) {
2265 layoutName = "smallLayout";
2267 layoutName = "normalLayout";
2269 /* Outer layoutWidget is there only to provide a name for use in
2270 resources that depend on the layout style */
2272 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2273 layoutArgs, XtNumber(layoutArgs));
2275 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2276 formArgs, XtNumber(formArgs));
2277 XtSetArg(args[0], XtNdefaultDistance, &sep);
2278 XtGetValues(formWidget, args, 1);
2281 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2282 XtSetArg(args[0], XtNtop, XtChainTop);
2283 XtSetArg(args[1], XtNbottom, XtChainTop);
2284 XtSetArg(args[2], XtNright, XtChainLeft);
2285 XtSetValues(menuBarWidget, args, 3);
2287 widgetList[j++] = whiteTimerWidget =
2288 XtCreateWidget("whiteTime", labelWidgetClass,
2289 formWidget, timerArgs, XtNumber(timerArgs));
2291 XtSetArg(args[0], XtNfontSet, clockFontSet);
2293 XtSetArg(args[0], XtNfont, clockFontStruct);
2295 XtSetArg(args[1], XtNtop, XtChainTop);
2296 XtSetArg(args[2], XtNbottom, XtChainTop);
2297 XtSetValues(whiteTimerWidget, args, 3);
2299 widgetList[j++] = blackTimerWidget =
2300 XtCreateWidget("blackTime", labelWidgetClass,
2301 formWidget, timerArgs, XtNumber(timerArgs));
2303 XtSetArg(args[0], XtNfontSet, clockFontSet);
2305 XtSetArg(args[0], XtNfont, clockFontStruct);
2307 XtSetArg(args[1], XtNtop, XtChainTop);
2308 XtSetArg(args[2], XtNbottom, XtChainTop);
2309 XtSetValues(blackTimerWidget, args, 3);
2311 if (appData.titleInWindow) {
2312 widgetList[j++] = titleWidget =
2313 XtCreateWidget("title", labelWidgetClass, formWidget,
2314 titleArgs, XtNumber(titleArgs));
2315 XtSetArg(args[0], XtNtop, XtChainTop);
2316 XtSetArg(args[1], XtNbottom, XtChainTop);
2317 XtSetValues(titleWidget, args, 2);
2320 if (appData.showButtonBar) {
2321 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2322 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2323 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2324 XtSetArg(args[2], XtNtop, XtChainTop);
2325 XtSetArg(args[3], XtNbottom, XtChainTop);
2326 XtSetValues(buttonBarWidget, args, 4);
2329 widgetList[j++] = messageWidget =
2330 XtCreateWidget("message", labelWidgetClass, formWidget,
2331 messageArgs, XtNumber(messageArgs));
2332 XtSetArg(args[0], XtNtop, XtChainTop);
2333 XtSetArg(args[1], XtNbottom, XtChainTop);
2334 XtSetValues(messageWidget, args, 2);
2336 widgetList[j++] = boardWidget =
2337 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2338 XtNumber(boardArgs));
2340 XtManageChildren(widgetList, j);
2342 timerWidth = (boardWidth - sep) / 2;
2343 XtSetArg(args[0], XtNwidth, timerWidth);
2344 XtSetValues(whiteTimerWidget, args, 1);
2345 XtSetValues(blackTimerWidget, args, 1);
2347 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2348 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2349 XtGetValues(whiteTimerWidget, args, 2);
2351 if (appData.showButtonBar) {
2352 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2353 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2354 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2358 * formWidget uses these constraints but they are stored
2362 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2363 XtSetValues(menuBarWidget, args, i);
2364 if (appData.titleInWindow) {
2367 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2368 XtSetValues(whiteTimerWidget, args, i);
2370 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2371 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2372 XtSetValues(blackTimerWidget, args, i);
2374 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2375 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2376 XtSetValues(titleWidget, args, i);
2378 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2379 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2380 XtSetValues(messageWidget, args, i);
2381 if (appData.showButtonBar) {
2383 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2384 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2385 XtSetValues(buttonBarWidget, args, i);
2389 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2390 XtSetValues(whiteTimerWidget, args, i);
2392 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2393 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2394 XtSetValues(blackTimerWidget, args, i);
2396 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2397 XtSetValues(titleWidget, args, i);
2399 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2400 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2401 XtSetValues(messageWidget, args, i);
2402 if (appData.showButtonBar) {
2404 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2405 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2406 XtSetValues(buttonBarWidget, args, i);
2411 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2412 XtSetValues(whiteTimerWidget, args, i);
2414 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2415 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2416 XtSetValues(blackTimerWidget, args, i);
2418 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2419 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2420 XtSetValues(messageWidget, args, i);
2421 if (appData.showButtonBar) {
2423 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2424 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2425 XtSetValues(buttonBarWidget, args, i);
2429 XtSetArg(args[0], XtNfromVert, messageWidget);
2430 XtSetArg(args[1], XtNtop, XtChainTop);
2431 XtSetArg(args[2], XtNbottom, XtChainBottom);
2432 XtSetArg(args[3], XtNleft, XtChainLeft);
2433 XtSetArg(args[4], XtNright, XtChainRight);
2434 XtSetValues(boardWidget, args, 5);
2436 XtRealizeWidget(shellWidget);
2439 XtSetArg(args[0], XtNx, wpMain.x);
2440 XtSetArg(args[1], XtNy, wpMain.y);
2441 XtSetValues(shellWidget, args, 2);
2445 * Correct the width of the message and title widgets.
2446 * It is not known why some systems need the extra fudge term.
2447 * The value "2" is probably larger than needed.
2449 XawFormDoLayout(formWidget, False);
2451 #define WIDTH_FUDGE 2
2453 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2454 XtSetArg(args[i], XtNheight, &h); i++;
2455 XtGetValues(messageWidget, args, i);
2456 if (appData.showButtonBar) {
2458 XtSetArg(args[i], XtNwidth, &w); i++;
2459 XtGetValues(buttonBarWidget, args, i);
2460 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2462 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2465 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2466 if (gres != XtGeometryYes && appData.debugMode) {
2467 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2468 programName, gres, w, h, wr, hr);
2471 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2472 /* The size used for the child widget in layout lags one resize behind
2473 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2475 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2476 if (gres != XtGeometryYes && appData.debugMode) {
2477 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2478 programName, gres, w, h, wr, hr);
2481 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2482 XtSetArg(args[1], XtNright, XtChainRight);
2483 XtSetValues(messageWidget, args, 2);
2485 if (appData.titleInWindow) {
2487 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2488 XtSetArg(args[i], XtNheight, &h); i++;
2489 XtGetValues(titleWidget, args, i);
2491 w = boardWidth - 2*bor;
2493 XtSetArg(args[0], XtNwidth, &w);
2494 XtGetValues(menuBarWidget, args, 1);
2495 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2498 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2499 if (gres != XtGeometryYes && appData.debugMode) {
2501 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2502 programName, gres, w, h, wr, hr);
2505 XawFormDoLayout(formWidget, True);
2507 xBoardWindow = XtWindow(boardWidget);
2509 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2510 // not need to go into InitDrawingSizes().
2514 * Create X checkmark bitmap and initialize option menu checks.
2516 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2517 checkmark_bits, checkmark_width, checkmark_height);
2518 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2519 #ifndef OPTIONSDIALOG
2520 if (appData.alwaysPromoteToQueen) {
2521 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2524 if (appData.animateDragging) {
2525 XtSetValues(XtNameToWidget(menuBarWidget,
2526 "menuOptions.Animate Dragging"),
2529 if (appData.animate) {
2530 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2533 if (appData.autoCallFlag) {
2534 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2537 if (appData.autoFlipView) {
2538 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2541 if (appData.blindfold) {
2542 XtSetValues(XtNameToWidget(menuBarWidget,
2543 "menuOptions.Blindfold"), args, 1);
2545 if (appData.flashCount > 0) {
2546 XtSetValues(XtNameToWidget(menuBarWidget,
2547 "menuOptions.Flash Moves"),
2551 if (appData.highlightDragging) {
2552 XtSetValues(XtNameToWidget(menuBarWidget,
2553 "menuOptions.Highlight Dragging"),
2557 if (appData.highlightLastMove) {
2558 XtSetValues(XtNameToWidget(menuBarWidget,
2559 "menuOptions.Highlight Last Move"),
2562 if (appData.highlightMoveWithArrow) {
2563 XtSetValues(XtNameToWidget(menuBarWidget,
2564 "menuOptions.Arrow"),
2567 // if (appData.icsAlarm) {
2568 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2571 if (appData.ringBellAfterMoves) {
2572 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2575 if (appData.oneClick) {
2576 XtSetValues(XtNameToWidget(menuBarWidget,
2577 "menuOptions.OneClick"), args, 1);
2579 if (appData.periodicUpdates) {
2580 XtSetValues(XtNameToWidget(menuBarWidget,
2581 "menuOptions.Periodic Updates"), args, 1);
2583 if (appData.ponderNextMove) {
2584 XtSetValues(XtNameToWidget(menuBarWidget,
2585 "menuOptions.Ponder Next Move"), args, 1);
2587 if (appData.popupExitMessage) {
2588 XtSetValues(XtNameToWidget(menuBarWidget,
2589 "menuOptions.Popup Exit Message"), args, 1);
2591 if (appData.popupMoveErrors) {
2592 XtSetValues(XtNameToWidget(menuBarWidget,
2593 "menuOptions.Popup Move Errors"), args, 1);
2595 // if (appData.premove) {
2596 // XtSetValues(XtNameToWidget(menuBarWidget,
2597 // "menuOptions.Premove"), args, 1);
2599 if (appData.showCoords) {
2600 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2603 if (appData.hideThinkingFromHuman) {
2604 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2607 if (appData.testLegality) {
2608 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2612 if (saveSettingsOnExit) {
2613 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2620 ReadBitmap(&wIconPixmap, "icon_white.bm",
2621 icon_white_bits, icon_white_width, icon_white_height);
2622 ReadBitmap(&bIconPixmap, "icon_black.bm",
2623 icon_black_bits, icon_black_width, icon_black_height);
2624 iconPixmap = wIconPixmap;
2626 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2627 XtSetValues(shellWidget, args, i);
2630 * Create a cursor for the board widget.
2632 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2633 XChangeWindowAttributes(xDisplay, xBoardWindow,
2634 CWCursor, &window_attributes);
2637 * Inhibit shell resizing.
2639 shellArgs[0].value = (XtArgVal) &w;
2640 shellArgs[1].value = (XtArgVal) &h;
2641 XtGetValues(shellWidget, shellArgs, 2);
2642 shellArgs[4].value = shellArgs[2].value = w;
2643 shellArgs[5].value = shellArgs[3].value = h;
2644 XtSetValues(shellWidget, &shellArgs[2], 4);
2645 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2646 marginH = h - boardHeight;
2648 CatchDeleteWindow(shellWidget, "QuitProc");
2656 if (appData.animate || appData.animateDragging)
2659 XtAugmentTranslations(formWidget,
2660 XtParseTranslationTable(globalTranslations));
2661 XtAugmentTranslations(boardWidget,
2662 XtParseTranslationTable(boardTranslations));
2663 XtAugmentTranslations(whiteTimerWidget,
2664 XtParseTranslationTable(whiteTranslations));
2665 XtAugmentTranslations(blackTimerWidget,
2666 XtParseTranslationTable(blackTranslations));
2668 /* Why is the following needed on some versions of X instead
2669 * of a translation? */
2670 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2671 (XtEventHandler) EventProc, NULL);
2673 XtAddEventHandler(formWidget, KeyPressMask, False,
2674 (XtEventHandler) MoveTypeInProc, NULL);
2676 /* [AS] Restore layout */
2677 if( wpMoveHistory.visible ) {
2681 if( wpEvalGraph.visible )
2686 if( wpEngineOutput.visible ) {
2687 EngineOutputPopUp();
2692 if (errorExitStatus == -1) {
2693 if (appData.icsActive) {
2694 /* We now wait until we see "login:" from the ICS before
2695 sending the logon script (problems with timestamp otherwise) */
2696 /*ICSInitScript();*/
2697 if (appData.icsInputBox) ICSInputBoxPopUp();
2701 signal(SIGWINCH, TermSizeSigHandler);
2703 signal(SIGINT, IntSigHandler);
2704 signal(SIGTERM, IntSigHandler);
2705 if (*appData.cmailGameName != NULLCHAR) {
2706 signal(SIGUSR1, CmailSigHandler);
2709 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2711 // XtSetKeyboardFocus(shellWidget, formWidget);
2712 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2714 XtAppMainLoop(appContext);
2715 if (appData.debugMode) fclose(debugFP); // [DM] debug
2719 static Boolean noEcho;
2724 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2725 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2727 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2728 unlink(gameCopyFilename);
2729 unlink(gamePasteFilename);
2730 if(noEcho) EchoOn();
2733 RETSIGTYPE TermSizeSigHandler(int sig)
2746 CmailSigHandler(sig)
2752 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2754 /* Activate call-back function CmailSigHandlerCallBack() */
2755 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2757 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2761 CmailSigHandlerCallBack(isr, closure, message, count, error)
2769 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2771 /**** end signal code ****/
2777 /* try to open the icsLogon script, either in the location given
2778 * or in the users HOME directory
2785 f = fopen(appData.icsLogon, "r");
2788 homedir = getenv("HOME");
2789 if (homedir != NULL)
2791 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2792 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2793 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2794 f = fopen(buf, "r");
2799 ProcessICSInitScript(f);
2801 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2824 if (!menuBarWidget) return;
2825 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2827 DisplayError("menuEdit.Revert", 0);
2829 XtSetSensitive(w, !grey);
2831 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2833 DisplayError("menuEdit.Annotate", 0);
2835 XtSetSensitive(w, !grey);
2840 SetMenuEnables(enab)
2844 if (!menuBarWidget) return;
2845 while (enab->name != NULL) {
2846 w = XtNameToWidget(menuBarWidget, enab->name);
2848 DisplayError(enab->name, 0);
2850 XtSetSensitive(w, enab->value);
2856 Enables icsEnables[] = {
2857 { "menuFile.Mail Move", False },
2858 { "menuFile.Reload CMail Message", False },
2859 { "menuMode.Machine Black", False },
2860 { "menuMode.Machine White", False },
2861 { "menuMode.Analysis Mode", False },
2862 { "menuMode.Analyze File", False },
2863 { "menuMode.Two Machines", False },
2864 { "menuMode.Machine Match", False },
2866 { "menuEngine.Hint", False },
2867 { "menuEngine.Book", False },
2868 { "menuEngine.Move Now", False },
2869 #ifndef OPTIONSDIALOG
2870 { "menuOptions.Periodic Updates", False },
2871 { "menuOptions.Hide Thinking", False },
2872 { "menuOptions.Ponder Next Move", False },
2875 { "menuEngine.Engine #1 Settings", False },
2876 { "menuEngine.Engine #2 Settings", False },
2877 { "menuEngine.Load Engine", False },
2878 { "menuEdit.Annotate", False },
2879 { "menuOptions.Match", False },
2883 Enables ncpEnables[] = {
2884 { "menuFile.Mail Move", False },
2885 { "menuFile.Reload CMail Message", False },
2886 { "menuMode.Machine White", False },
2887 { "menuMode.Machine Black", False },
2888 { "menuMode.Analysis Mode", False },
2889 { "menuMode.Analyze File", False },
2890 { "menuMode.Two Machines", False },
2891 { "menuMode.Machine Match", False },
2892 { "menuMode.ICS Client", False },
2893 { "menuView.ICStex", False },
2894 { "menuView.ICS Input Box", False },
2895 { "Action", False },
2896 { "menuEdit.Revert", False },
2897 { "menuEdit.Annotate", False },
2898 { "menuEngine.Engine #1 Settings", False },
2899 { "menuEngine.Engine #2 Settings", False },
2900 { "menuEngine.Move Now", False },
2901 { "menuEngine.Retract Move", False },
2902 { "menuOptions.ICS", False },
2903 #ifndef OPTIONSDIALOG
2904 { "menuOptions.Auto Flag", False },
2905 { "menuOptions.Auto Flip View", False },
2906 // { "menuOptions.ICS Alarm", False },
2907 { "menuOptions.Move Sound", False },
2908 { "menuOptions.Hide Thinking", False },
2909 { "menuOptions.Periodic Updates", False },
2910 { "menuOptions.Ponder Next Move", False },
2912 { "menuEngine.Hint", False },
2913 { "menuEngine.Book", False },
2917 Enables gnuEnables[] = {
2918 { "menuMode.ICS Client", False },
2919 { "menuView.ICStex", False },
2920 { "menuView.ICS Input Box", False },
2921 { "menuAction.Accept", False },
2922 { "menuAction.Decline", False },
2923 { "menuAction.Rematch", False },
2924 { "menuAction.Adjourn", False },
2925 { "menuAction.Stop Examining", False },
2926 { "menuAction.Stop Observing", False },
2927 { "menuAction.Upload to Examine", False },
2928 { "menuEdit.Revert", False },
2929 { "menuEdit.Annotate", False },
2930 { "menuOptions.ICS", False },
2932 /* The next two options rely on SetCmailMode being called *after* */
2933 /* SetGNUMode so that when GNU is being used to give hints these */
2934 /* menu options are still available */
2936 { "menuFile.Mail Move", False },
2937 { "menuFile.Reload CMail Message", False },
2938 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2939 { "menuMode.Machine White", True },
2940 { "menuMode.Machine Black", True },
2941 { "menuMode.Analysis Mode", True },
2942 { "menuMode.Analyze File", True },
2943 { "menuMode.Two Machines", True },
2944 { "menuMode.Machine Match", True },
2945 { "menuEngine.Engine #1 Settings", True },
2946 { "menuEngine.Engine #2 Settings", True },
2947 { "menuEngine.Hint", True },
2948 { "menuEngine.Book", True },
2949 { "menuEngine.Move Now", True },
2950 { "menuEngine.Retract Move", True },
2955 Enables cmailEnables[] = {
2957 { "menuAction.Call Flag", False },
2958 { "menuAction.Draw", True },
2959 { "menuAction.Adjourn", False },
2960 { "menuAction.Abort", False },
2961 { "menuAction.Stop Observing", False },
2962 { "menuAction.Stop Examining", False },
2963 { "menuFile.Mail Move", True },
2964 { "menuFile.Reload CMail Message", True },
2968 Enables trainingOnEnables[] = {
2969 { "menuMode.Edit Comment", False },
2970 { "menuMode.Pause", False },
2971 { "menuEdit.Forward", False },
2972 { "menuEdit.Backward", False },
2973 { "menuEdit.Forward to End", False },
2974 { "menuEdit.Back to Start", False },
2975 { "menuEngine.Move Now", False },
2976 { "menuEdit.Truncate Game", False },
2980 Enables trainingOffEnables[] = {
2981 { "menuMode.Edit Comment", True },
2982 { "menuMode.Pause", True },
2983 { "menuEdit.Forward", True },
2984 { "menuEdit.Backward", True },
2985 { "menuEdit.Forward to End", True },
2986 { "menuEdit.Back to Start", True },
2987 { "menuEngine.Move Now", True },
2988 { "menuEdit.Truncate Game", True },
2992 Enables machineThinkingEnables[] = {
2993 { "menuFile.Load Game", False },
2994 // { "menuFile.Load Next Game", False },
2995 // { "menuFile.Load Previous Game", False },
2996 // { "menuFile.Reload Same Game", False },
2997 { "menuEdit.Paste Game", False },
2998 { "menuFile.Load Position", False },
2999 // { "menuFile.Load Next Position", False },
3000 // { "menuFile.Load Previous Position", False },
3001 // { "menuFile.Reload Same Position", False },
3002 { "menuEdit.Paste Position", False },
3003 { "menuMode.Machine White", False },
3004 { "menuMode.Machine Black", False },
3005 { "menuMode.Two Machines", False },
3006 // { "menuMode.Machine Match", False },
3007 { "menuEngine.Retract Move", False },
3011 Enables userThinkingEnables[] = {
3012 { "menuFile.Load Game", True },
3013 // { "menuFile.Load Next Game", True },
3014 // { "menuFile.Load Previous Game", True },
3015 // { "menuFile.Reload Same Game", True },
3016 { "menuEdit.Paste Game", True },
3017 { "menuFile.Load Position", True },
3018 // { "menuFile.Load Next Position", True },
3019 // { "menuFile.Load Previous Position", True },
3020 // { "menuFile.Reload Same Position", True },
3021 { "menuEdit.Paste Position", True },
3022 { "menuMode.Machine White", True },
3023 { "menuMode.Machine Black", True },
3024 { "menuMode.Two Machines", True },
3025 // { "menuMode.Machine Match", True },
3026 { "menuEngine.Retract Move", True },
3032 SetMenuEnables(icsEnables);
3035 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3036 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3037 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3045 SetMenuEnables(ncpEnables);
3051 SetMenuEnables(gnuEnables);
3057 SetMenuEnables(cmailEnables);
3063 SetMenuEnables(trainingOnEnables);
3064 if (appData.showButtonBar) {
3065 XtSetSensitive(buttonBarWidget, False);
3071 SetTrainingModeOff()
3073 SetMenuEnables(trainingOffEnables);
3074 if (appData.showButtonBar) {
3075 XtSetSensitive(buttonBarWidget, True);
3080 SetUserThinkingEnables()
3082 if (appData.noChessProgram) return;
3083 SetMenuEnables(userThinkingEnables);
3087 SetMachineThinkingEnables()
3089 if (appData.noChessProgram) return;
3090 SetMenuEnables(machineThinkingEnables);
3092 case MachinePlaysBlack:
3093 case MachinePlaysWhite:
3094 case TwoMachinesPlay:
3095 XtSetSensitive(XtNameToWidget(menuBarWidget,
3096 ModeToWidgetName(gameMode)), True);
3103 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3104 #define HISTORY_SIZE 64
3105 static char *history[HISTORY_SIZE];
3106 int histIn = 0, histP = 0;
3109 SaveInHistory(char *cmd)
3111 if (history[histIn] != NULL) {
3112 free(history[histIn]);
3113 history[histIn] = NULL;
3115 if (*cmd == NULLCHAR) return;
3116 history[histIn] = StrSave(cmd);
3117 histIn = (histIn + 1) % HISTORY_SIZE;
3118 if (history[histIn] != NULL) {
3119 free(history[histIn]);
3120 history[histIn] = NULL;
3126 PrevInHistory(char *cmd)
3129 if (histP == histIn) {
3130 if (history[histIn] != NULL) free(history[histIn]);
3131 history[histIn] = StrSave(cmd);
3133 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3134 if (newhp == histIn || history[newhp] == NULL) return NULL;
3136 return history[histP];
3142 if (histP == histIn) return NULL;
3143 histP = (histP + 1) % HISTORY_SIZE;
3144 return history[histP];
3146 // end of borrowed code
3148 #define Abs(n) ((n)<0 ? -(n) : (n))
3152 InsertPxlSize(pattern, targetPxlSize)
3156 char *base_fnt_lst, strInt[12], *p, *q;
3157 int alternatives, i, len, strIntLen;
3160 * Replace the "*" (if present) in the pixel-size slot of each
3161 * alternative with the targetPxlSize.
3165 while ((p = strchr(p, ',')) != NULL) {
3169 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3170 strIntLen = strlen(strInt);
3171 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3175 while (alternatives--) {
3176 char *comma = strchr(p, ',');
3177 for (i=0; i<14; i++) {
3178 char *hyphen = strchr(p, '-');
3180 if (comma && hyphen > comma) break;
3181 len = hyphen + 1 - p;
3182 if (i == 7 && *p == '*' && len == 2) {
3184 memcpy(q, strInt, strIntLen);
3194 len = comma + 1 - p;
3201 return base_fnt_lst;
3205 CreateFontSet(base_fnt_lst)
3209 char **missing_list;
3213 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3214 &missing_list, &missing_count, &def_string);
3215 if (appData.debugMode) {
3217 XFontStruct **font_struct_list;
3218 char **font_name_list;
3219 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3221 fprintf(debugFP, " got list %s, locale %s\n",
3222 XBaseFontNameListOfFontSet(fntSet),
3223 XLocaleOfFontSet(fntSet));
3224 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3225 for (i = 0; i < count; i++) {
3226 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3229 for (i = 0; i < missing_count; i++) {
3230 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3233 if (fntSet == NULL) {
3234 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3239 #else // not ENABLE_NLS
3241 * Find a font that matches "pattern" that is as close as
3242 * possible to the targetPxlSize. Prefer fonts that are k
3243 * pixels smaller to fonts that are k pixels larger. The
3244 * pattern must be in the X Consortium standard format,
3245 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3246 * The return value should be freed with XtFree when no
3250 FindFont(pattern, targetPxlSize)
3254 char **fonts, *p, *best, *scalable, *scalableTail;
3255 int i, j, nfonts, minerr, err, pxlSize;
3257 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3259 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3260 programName, pattern);
3267 for (i=0; i<nfonts; i++) {
3270 if (*p != '-') continue;
3272 if (*p == NULLCHAR) break;
3273 if (*p++ == '-') j++;
3275 if (j < 7) continue;
3278 scalable = fonts[i];
3281 err = pxlSize - targetPxlSize;
3282 if (Abs(err) < Abs(minerr) ||
3283 (minerr > 0 && err < 0 && -err == minerr)) {
3289 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3290 /* If the error is too big and there is a scalable font,
3291 use the scalable font. */
3292 int headlen = scalableTail - scalable;
3293 p = (char *) XtMalloc(strlen(scalable) + 10);
3294 while (isdigit(*scalableTail)) scalableTail++;
3295 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3297 p = (char *) XtMalloc(strlen(best) + 2);
3298 safeStrCpy(p, best, strlen(best)+1 );
3300 if (appData.debugMode) {
3301 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3302 pattern, targetPxlSize, p);
3304 XFreeFontNames(fonts);
3310 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3311 // must be called before all non-first callse to CreateGCs()
3312 XtReleaseGC(shellWidget, highlineGC);
3313 XtReleaseGC(shellWidget, lightSquareGC);
3314 XtReleaseGC(shellWidget, darkSquareGC);
3315 XtReleaseGC(shellWidget, lineGC);
3316 if (appData.monoMode) {
3317 if (DefaultDepth(xDisplay, xScreen) == 1) {
3318 XtReleaseGC(shellWidget, wbPieceGC);
3320 XtReleaseGC(shellWidget, bwPieceGC);
3323 XtReleaseGC(shellWidget, prelineGC);
3324 XtReleaseGC(shellWidget, jailSquareGC);
3325 XtReleaseGC(shellWidget, wdPieceGC);
3326 XtReleaseGC(shellWidget, wlPieceGC);
3327 XtReleaseGC(shellWidget, wjPieceGC);
3328 XtReleaseGC(shellWidget, bdPieceGC);
3329 XtReleaseGC(shellWidget, blPieceGC);
3330 XtReleaseGC(shellWidget, bjPieceGC);
3334 void CreateGCs(int redo)
3336 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3337 | GCBackground | GCFunction | GCPlaneMask;
3338 XGCValues gc_values;
3341 gc_values.plane_mask = AllPlanes;
3342 gc_values.line_width = lineGap;
3343 gc_values.line_style = LineSolid;
3344 gc_values.function = GXcopy;
3347 DeleteGCs(); // called a second time; clean up old GCs first
3348 } else { // [HGM] grid and font GCs created on first call only
3349 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3350 gc_values.background = XWhitePixel(xDisplay, xScreen);
3351 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3352 XSetFont(xDisplay, coordGC, coordFontID);
3354 // [HGM] make font for holdings counts (white on black)
3355 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3356 gc_values.background = XBlackPixel(xDisplay, xScreen);
3357 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3358 XSetFont(xDisplay, countGC, countFontID);
3360 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3361 gc_values.background = XBlackPixel(xDisplay, xScreen);
3362 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3364 if (appData.monoMode) {
3365 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3366 gc_values.background = XWhitePixel(xDisplay, xScreen);
3367 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3369 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3370 gc_values.background = XBlackPixel(xDisplay, xScreen);
3371 lightSquareGC = wbPieceGC
3372 = XtGetGC(shellWidget, value_mask, &gc_values);
3374 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3375 gc_values.background = XWhitePixel(xDisplay, xScreen);
3376 darkSquareGC = bwPieceGC
3377 = XtGetGC(shellWidget, value_mask, &gc_values);
3379 if (DefaultDepth(xDisplay, xScreen) == 1) {
3380 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3381 gc_values.function = GXcopyInverted;
3382 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3383 gc_values.function = GXcopy;
3384 if (XBlackPixel(xDisplay, xScreen) == 1) {
3385 bwPieceGC = darkSquareGC;
3386 wbPieceGC = copyInvertedGC;
3388 bwPieceGC = copyInvertedGC;
3389 wbPieceGC = lightSquareGC;
3393 gc_values.foreground = highlightSquareColor;
3394 gc_values.background = highlightSquareColor;
3395 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3397 gc_values.foreground = premoveHighlightColor;
3398 gc_values.background = premoveHighlightColor;
3399 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3401 gc_values.foreground = lightSquareColor;
3402 gc_values.background = darkSquareColor;
3403 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3405 gc_values.foreground = darkSquareColor;
3406 gc_values.background = lightSquareColor;
3407 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3409 gc_values.foreground = jailSquareColor;
3410 gc_values.background = jailSquareColor;
3411 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3413 gc_values.foreground = whitePieceColor;
3414 gc_values.background = darkSquareColor;
3415 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3417 gc_values.foreground = whitePieceColor;
3418 gc_values.background = lightSquareColor;
3419 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3421 gc_values.foreground = whitePieceColor;
3422 gc_values.background = jailSquareColor;
3423 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3425 gc_values.foreground = blackPieceColor;
3426 gc_values.background = darkSquareColor;
3427 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3429 gc_values.foreground = blackPieceColor;
3430 gc_values.background = lightSquareColor;
3431 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3433 gc_values.foreground = blackPieceColor;
3434 gc_values.background = jailSquareColor;
3435 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3439 void loadXIM(xim, xmask, filename, dest, mask)
3452 fp = fopen(filename, "rb");
3454 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3461 for (y=0; y<h; ++y) {
3462 for (x=0; x<h; ++x) {
3467 XPutPixel(xim, x, y, blackPieceColor);
3469 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3472 XPutPixel(xim, x, y, darkSquareColor);
3474 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3477 XPutPixel(xim, x, y, whitePieceColor);
3479 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3482 XPutPixel(xim, x, y, lightSquareColor);
3484 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3492 /* create Pixmap of piece */
3493 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3495 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3498 /* create Pixmap of clipmask
3499 Note: We assume the white/black pieces have the same
3500 outline, so we make only 6 masks. This is okay
3501 since the XPM clipmask routines do the same. */
3503 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3505 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3508 /* now create the 1-bit version */
3509 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3512 values.foreground = 1;
3513 values.background = 0;
3515 /* Don't use XtGetGC, not read only */
3516 maskGC = XCreateGC(xDisplay, *mask,
3517 GCForeground | GCBackground, &values);
3518 XCopyPlane(xDisplay, temp, *mask, maskGC,
3519 0, 0, squareSize, squareSize, 0, 0, 1);
3520 XFreePixmap(xDisplay, temp);
3525 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3527 void CreateXIMPieces()
3532 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3537 /* The XSynchronize calls were copied from CreatePieces.
3538 Not sure if needed, but can't hurt */
3539 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3542 /* temp needed by loadXIM() */
3543 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3544 0, 0, ss, ss, AllPlanes, XYPixmap);
3546 if (strlen(appData.pixmapDirectory) == 0) {
3550 if (appData.monoMode) {
3551 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3555 fprintf(stderr, _("\nLoading XIMs...\n"));
3557 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3558 fprintf(stderr, "%d", piece+1);
3559 for (kind=0; kind<4; kind++) {
3560 fprintf(stderr, ".");
3561 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3562 ExpandPathName(appData.pixmapDirectory),
3563 piece <= (int) WhiteKing ? "" : "w",
3564 pieceBitmapNames[piece],
3566 ximPieceBitmap[kind][piece] =
3567 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3568 0, 0, ss, ss, AllPlanes, XYPixmap);
3569 if (appData.debugMode)
3570 fprintf(stderr, _("(File:%s:) "), buf);
3571 loadXIM(ximPieceBitmap[kind][piece],
3573 &(xpmPieceBitmap2[kind][piece]),
3574 &(ximMaskPm2[piece]));
3575 if(piece <= (int)WhiteKing)
3576 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3578 fprintf(stderr," ");
3580 /* Load light and dark squares */
3581 /* If the LSQ and DSQ pieces don't exist, we will
3582 draw them with solid squares. */
3583 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3584 if (access(buf, 0) != 0) {
3588 fprintf(stderr, _("light square "));
3590 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3591 0, 0, ss, ss, AllPlanes, XYPixmap);
3592 if (appData.debugMode)
3593 fprintf(stderr, _("(File:%s:) "), buf);
3595 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3596 fprintf(stderr, _("dark square "));
3597 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3598 ExpandPathName(appData.pixmapDirectory), ss);
3599 if (appData.debugMode)
3600 fprintf(stderr, _("(File:%s:) "), buf);
3602 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3603 0, 0, ss, ss, AllPlanes, XYPixmap);
3604 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3605 xpmJailSquare = xpmLightSquare;
3607 fprintf(stderr, _("Done.\n"));
3609 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3612 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3615 void CreateXPMBoard(char *s, int kind)
3619 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3620 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3621 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3625 void FreeXPMPieces()
3626 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3627 // thisroutine has to be called t free the old piece pixmaps
3629 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3630 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3632 XFreePixmap(xDisplay, xpmLightSquare);
3633 XFreePixmap(xDisplay, xpmDarkSquare);
3637 void CreateXPMPieces()
3641 u_int ss = squareSize;
3643 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3644 XpmColorSymbol symbols[4];
3645 static int redo = False;
3647 if(redo) FreeXPMPieces(); else redo = 1;
3649 /* The XSynchronize calls were copied from CreatePieces.
3650 Not sure if needed, but can't hurt */
3651 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3653 /* Setup translations so piece colors match square colors */
3654 symbols[0].name = "light_piece";
3655 symbols[0].value = appData.whitePieceColor;
3656 symbols[1].name = "dark_piece";
3657 symbols[1].value = appData.blackPieceColor;
3658 symbols[2].name = "light_square";
3659 symbols[2].value = appData.lightSquareColor;
3660 symbols[3].name = "dark_square";
3661 symbols[3].value = appData.darkSquareColor;
3663 attr.valuemask = XpmColorSymbols;
3664 attr.colorsymbols = symbols;
3665 attr.numsymbols = 4;
3667 if (appData.monoMode) {
3668 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3672 if (strlen(appData.pixmapDirectory) == 0) {
3673 XpmPieces* pieces = builtInXpms;
3676 while (pieces->size != squareSize && pieces->size) pieces++;
3677 if (!pieces->size) {
3678 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3681 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3682 for (kind=0; kind<4; kind++) {
3684 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3685 pieces->xpm[piece][kind],
3686 &(xpmPieceBitmap2[kind][piece]),
3687 NULL, &attr)) != 0) {
3688 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3692 if(piece <= (int) WhiteKing)
3693 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3697 xpmJailSquare = xpmLightSquare;
3701 fprintf(stderr, _("\nLoading XPMs...\n"));
3704 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3705 fprintf(stderr, "%d ", piece+1);
3706 for (kind=0; kind<4; kind++) {
3707 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3708 ExpandPathName(appData.pixmapDirectory),
3709 piece > (int) WhiteKing ? "w" : "",
3710 pieceBitmapNames[piece],
3712 if (appData.debugMode) {
3713 fprintf(stderr, _("(File:%s:) "), buf);
3715 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3716 &(xpmPieceBitmap2[kind][piece]),
3717 NULL, &attr)) != 0) {
3718 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3719 // [HGM] missing: read of unorthodox piece failed; substitute King.
3720 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3721 ExpandPathName(appData.pixmapDirectory),
3723 if (appData.debugMode) {
3724 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3726 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3727 &(xpmPieceBitmap2[kind][piece]),
3731 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3736 if(piece <= (int) WhiteKing)
3737 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3740 /* Load light and dark squares */
3741 /* If the LSQ and DSQ pieces don't exist, we will
3742 draw them with solid squares. */
3743 fprintf(stderr, _("light square "));
3744 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3745 if (access(buf, 0) != 0) {
3749 if (appData.debugMode)
3750 fprintf(stderr, _("(File:%s:) "), buf);
3752 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3753 &xpmLightSquare, NULL, &attr)) != 0) {
3754 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3757 fprintf(stderr, _("dark square "));
3758 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3759 ExpandPathName(appData.pixmapDirectory), ss);
3760 if (appData.debugMode) {
3761 fprintf(stderr, _("(File:%s:) "), buf);
3763 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3764 &xpmDarkSquare, NULL, &attr)) != 0) {
3765 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3769 xpmJailSquare = xpmLightSquare;
3770 fprintf(stderr, _("Done.\n"));
3772 oldVariant = -1; // kludge to force re-makig of animation masks
3773 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3776 #endif /* HAVE_LIBXPM */
3779 /* No built-in bitmaps */
3784 u_int ss = squareSize;
3786 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3789 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3790 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3791 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3792 pieceBitmapNames[piece],
3793 ss, kind == SOLID ? 's' : 'o');
3794 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3795 if(piece <= (int)WhiteKing)
3796 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3800 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3804 /* With built-in bitmaps */
3807 BuiltInBits* bib = builtInBits;
3810 u_int ss = squareSize;
3812 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3815 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3817 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3818 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3819 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3820 pieceBitmapNames[piece],
3821 ss, kind == SOLID ? 's' : 'o');
3822 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3823 bib->bits[kind][piece], ss, ss);
3824 if(piece <= (int)WhiteKing)
3825 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3829 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3834 void ReadBitmap(pm, name, bits, wreq, hreq)
3837 unsigned char bits[];
3843 char msg[MSG_SIZ], fullname[MSG_SIZ];
3845 if (*appData.bitmapDirectory != NULLCHAR) {
3846 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3847 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3848 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3849 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3850 &w, &h, pm, &x_hot, &y_hot);
3851 fprintf(stderr, "load %s\n", name);
3852 if (errcode != BitmapSuccess) {
3854 case BitmapOpenFailed:
3855 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3857 case BitmapFileInvalid:
3858 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3860 case BitmapNoMemory:
3861 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3865 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3869 fprintf(stderr, _("%s: %s...using built-in\n"),
3871 } else if (w != wreq || h != hreq) {
3873 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3874 programName, fullname, w, h, wreq, hreq);
3880 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3889 if (lineGap == 0) return;
3891 /* [HR] Split this into 2 loops for non-square boards. */
3893 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3894 gridSegments[i].x1 = 0;
3895 gridSegments[i].x2 =
3896 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3897 gridSegments[i].y1 = gridSegments[i].y2
3898 = lineGap / 2 + (i * (squareSize + lineGap));
3901 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3902 gridSegments[j + i].y1 = 0;
3903 gridSegments[j + i].y2 =
3904 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3905 gridSegments[j + i].x1 = gridSegments[j + i].x2
3906 = lineGap / 2 + (j * (squareSize + lineGap));
3910 static void MenuBarSelect(w, addr, index)
3915 XtActionProc proc = (XtActionProc) addr;
3917 (proc)(NULL, NULL, NULL, NULL);
3920 void CreateMenuBarPopup(parent, name, mb)
3930 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3933 XtSetArg(args[j], XtNleftMargin, 20); j++;
3934 XtSetArg(args[j], XtNrightMargin, 20); j++;
3936 while (mi->string != NULL) {
3937 if (strcmp(mi->string, "----") == 0) {
3938 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3941 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3942 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3944 XtAddCallback(entry, XtNcallback,
3945 (XtCallbackProc) MenuBarSelect,
3946 (caddr_t) mi->proc);
3952 Widget CreateMenuBar(mb)
3956 Widget anchor, menuBar;
3958 char menuName[MSG_SIZ];
3961 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3962 XtSetArg(args[j], XtNvSpace, 0); j++;
3963 XtSetArg(args[j], XtNborderWidth, 0); j++;
3964 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3965 formWidget, args, j);
3967 while (mb->name != NULL) {
3968 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3969 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3971 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3974 shortName[0] = mb->name[0];
3975 shortName[1] = NULLCHAR;
3976 XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3979 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3982 XtSetArg(args[j], XtNborderWidth, 0); j++;
3983 anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3985 CreateMenuBarPopup(menuBar, menuName, mb);
3991 Widget CreateButtonBar(mi)
3995 Widget button, buttonBar;
3999 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4001 XtSetArg(args[j], XtNhSpace, 0); j++;
4003 XtSetArg(args[j], XtNborderWidth, 0); j++;
4004 XtSetArg(args[j], XtNvSpace, 0); j++;
4005 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4006 formWidget, args, j);
4008 while (mi->string != NULL) {
4011 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4012 XtSetArg(args[j], XtNborderWidth, 0); j++;
4014 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4015 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4016 buttonBar, args, j);
4017 XtAddCallback(button, XtNcallback,
4018 (XtCallbackProc) MenuBarSelect,
4019 (caddr_t) mi->proc);
4026 CreatePieceMenu(name, color)
4033 ChessSquare selection;
4035 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4036 boardWidget, args, 0);
4038 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4039 String item = pieceMenuStrings[color][i];
4041 if (strcmp(item, "----") == 0) {
4042 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4045 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4046 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4048 selection = pieceMenuTranslation[color][i];
4049 XtAddCallback(entry, XtNcallback,
4050 (XtCallbackProc) PieceMenuSelect,
4051 (caddr_t) selection);
4052 if (selection == WhitePawn || selection == BlackPawn) {
4053 XtSetArg(args[0], XtNpopupOnEntry, entry);
4054 XtSetValues(menu, args, 1);
4067 ChessSquare selection;
4069 whitePieceMenu = CreatePieceMenu("menuW", 0);
4070 blackPieceMenu = CreatePieceMenu("menuB", 1);
4072 XtRegisterGrabAction(PieceMenuPopup, True,
4073 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4074 GrabModeAsync, GrabModeAsync);
4076 XtSetArg(args[0], XtNlabel, _("Drop"));
4077 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4078 boardWidget, args, 1);
4079 for (i = 0; i < DROP_MENU_SIZE; i++) {
4080 String item = dropMenuStrings[i];
4082 if (strcmp(item, "----") == 0) {
4083 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4086 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4087 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4089 selection = dropMenuTranslation[i];
4090 XtAddCallback(entry, XtNcallback,
4091 (XtCallbackProc) DropMenuSelect,
4092 (caddr_t) selection);
4097 void SetupDropMenu()
4105 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4106 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4107 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4108 dmEnables[i].piece);
4109 XtSetSensitive(entry, p != NULL || !appData.testLegality
4110 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4111 && !appData.icsActive));
4113 while (p && *p++ == dmEnables[i].piece) count++;
4114 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4116 XtSetArg(args[j], XtNlabel, label); j++;
4117 XtSetValues(entry, args, j);
4121 void PieceMenuPopup(w, event, params, num_params)
4125 Cardinal *num_params;
4127 String whichMenu; int menuNr = -2;
4128 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4129 if (event->type == ButtonRelease)
4130 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4131 else if (event->type == ButtonPress)
4132 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4134 case 0: whichMenu = params[0]; break;
4135 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4137 case -1: if (errorUp) ErrorPopDown();
4140 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4143 static void PieceMenuSelect(w, piece, junk)
4148 if (pmFromX < 0 || pmFromY < 0) return;
4149 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4152 static void DropMenuSelect(w, piece, junk)
4157 if (pmFromX < 0 || pmFromY < 0) return;
4158 DropMenuEvent(piece, pmFromX, pmFromY);
4161 void WhiteClock(w, event, prms, nprms)
4167 shiftKey = prms[0][0] & 1;
4171 void BlackClock(w, event, prms, nprms)
4177 shiftKey = prms[0][0] & 1;
4183 * If the user selects on a border boundary, return -1; if off the board,
4184 * return -2. Otherwise map the event coordinate to the square.
4186 int EventToSquare(x, limit)
4194 if ((x % (squareSize + lineGap)) >= squareSize)
4196 x /= (squareSize + lineGap);
4202 static void do_flash_delay(msec)
4208 static void drawHighlight(file, rank, gc)
4214 if (lineGap == 0) return;
4217 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4218 (squareSize + lineGap);
4219 y = lineGap/2 + rank * (squareSize + lineGap);
4221 x = lineGap/2 + file * (squareSize + lineGap);
4222 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4223 (squareSize + lineGap);
4226 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4227 squareSize+lineGap, squareSize+lineGap);
4230 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4231 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4234 SetHighlights(fromX, fromY, toX, toY)
4235 int fromX, fromY, toX, toY;
4237 if (hi1X != fromX || hi1Y != fromY) {
4238 if (hi1X >= 0 && hi1Y >= 0) {
4239 drawHighlight(hi1X, hi1Y, lineGC);
4241 } // [HGM] first erase both, then draw new!
4242 if (hi2X != toX || hi2Y != toY) {
4243 if (hi2X >= 0 && hi2Y >= 0) {
4244 drawHighlight(hi2X, hi2Y, lineGC);
4247 if (hi1X != fromX || hi1Y != fromY) {
4248 if (fromX >= 0 && fromY >= 0) {
4249 drawHighlight(fromX, fromY, highlineGC);
4252 if (hi2X != toX || hi2Y != toY) {
4253 if (toX >= 0 && toY >= 0) {
4254 drawHighlight(toX, toY, highlineGC);
4266 SetHighlights(-1, -1, -1, -1);
4271 SetPremoveHighlights(fromX, fromY, toX, toY)
4272 int fromX, fromY, toX, toY;
4274 if (pm1X != fromX || pm1Y != fromY) {
4275 if (pm1X >= 0 && pm1Y >= 0) {
4276 drawHighlight(pm1X, pm1Y, lineGC);
4278 if (fromX >= 0 && fromY >= 0) {
4279 drawHighlight(fromX, fromY, prelineGC);
4282 if (pm2X != toX || pm2Y != toY) {
4283 if (pm2X >= 0 && pm2Y >= 0) {
4284 drawHighlight(pm2X, pm2Y, lineGC);
4286 if (toX >= 0 && toY >= 0) {
4287 drawHighlight(toX, toY, prelineGC);
4297 ClearPremoveHighlights()
4299 SetPremoveHighlights(-1, -1, -1, -1);
4302 static int CutOutSquare(x, y, x0, y0, kind)
4303 int x, y, *x0, *y0, kind;
4305 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4306 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4308 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4309 if(textureW[kind] < W*squareSize)
4310 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4312 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4313 if(textureH[kind] < H*squareSize)
4314 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4316 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4320 static void BlankSquare(x, y, color, piece, dest, fac)
4321 int x, y, color, fac;
4324 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4326 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4327 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4328 squareSize, squareSize, x*fac, y*fac);
4330 if (useImages && useImageSqs) {
4334 pm = xpmLightSquare;
4339 case 2: /* neutral */
4344 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4345 squareSize, squareSize, x*fac, y*fac);
4355 case 2: /* neutral */
4360 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4365 I split out the routines to draw a piece so that I could
4366 make a generic flash routine.
4368 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4370 int square_color, x, y;
4373 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4374 switch (square_color) {
4376 case 2: /* neutral */
4378 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4379 ? *pieceToOutline(piece)
4380 : *pieceToSolid(piece),
4381 dest, bwPieceGC, 0, 0,
4382 squareSize, squareSize, x, y);
4385 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4386 ? *pieceToSolid(piece)
4387 : *pieceToOutline(piece),
4388 dest, wbPieceGC, 0, 0,
4389 squareSize, squareSize, x, y);
4394 static void monoDrawPiece(piece, square_color, x, y, dest)
4396 int square_color, x, y;
4399 switch (square_color) {
4401 case 2: /* neutral */
4403 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4404 ? *pieceToOutline(piece)
4405 : *pieceToSolid(piece),
4406 dest, bwPieceGC, 0, 0,
4407 squareSize, squareSize, x, y, 1);
4410 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4411 ? *pieceToSolid(piece)
4412 : *pieceToOutline(piece),
4413 dest, wbPieceGC, 0, 0,
4414 squareSize, squareSize, x, y, 1);
4419 static void colorDrawPiece(piece, square_color, x, y, dest)
4421 int square_color, x, y;
4424 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4425 switch (square_color) {
4427 XCopyPlane(xDisplay, *pieceToSolid(piece),
4428 dest, (int) piece < (int) BlackPawn
4429 ? wlPieceGC : blPieceGC, 0, 0,
4430 squareSize, squareSize, x, y, 1);
4433 XCopyPlane(xDisplay, *pieceToSolid(piece),
4434 dest, (int) piece < (int) BlackPawn
4435 ? wdPieceGC : bdPieceGC, 0, 0,
4436 squareSize, squareSize, x, y, 1);
4438 case 2: /* neutral */
4440 XCopyPlane(xDisplay, *pieceToSolid(piece),
4441 dest, (int) piece < (int) BlackPawn
4442 ? wjPieceGC : bjPieceGC, 0, 0,
4443 squareSize, squareSize, x, y, 1);
4448 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4450 int square_color, x, y;
4453 int kind, p = piece;
4455 switch (square_color) {
4457 case 2: /* neutral */
4459 if ((int)piece < (int) BlackPawn) {
4467 if ((int)piece < (int) BlackPawn) {
4475 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4476 if(useTexture & square_color+1) {
4477 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4478 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4479 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4480 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4481 XSetClipMask(xDisplay, wlPieceGC, None);
4482 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4484 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4485 dest, wlPieceGC, 0, 0,
4486 squareSize, squareSize, x, y);
4489 typedef void (*DrawFunc)();
4491 DrawFunc ChooseDrawFunc()
4493 if (appData.monoMode) {
4494 if (DefaultDepth(xDisplay, xScreen) == 1) {
4495 return monoDrawPiece_1bit;
4497 return monoDrawPiece;
4501 return colorDrawPieceImage;
4503 return colorDrawPiece;
4507 /* [HR] determine square color depending on chess variant. */
4508 static int SquareColor(row, column)
4513 if (gameInfo.variant == VariantXiangqi) {
4514 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4516 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4518 } else if (row <= 4) {
4524 square_color = ((column + row) % 2) == 1;
4527 /* [hgm] holdings: next line makes all holdings squares light */
4528 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4530 return square_color;
4533 void DrawSquare(row, column, piece, do_flash)
4534 int row, column, do_flash;
4537 int square_color, x, y, direction, font_ascent, font_descent;
4540 XCharStruct overall;
4544 /* Calculate delay in milliseconds (2-delays per complete flash) */
4545 flash_delay = 500 / appData.flashRate;
4548 x = lineGap + ((BOARD_WIDTH-1)-column) *
4549 (squareSize + lineGap);
4550 y = lineGap + row * (squareSize + lineGap);
4552 x = lineGap + column * (squareSize + lineGap);
4553 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4554 (squareSize + lineGap);
4557 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4559 square_color = SquareColor(row, column);
4561 if ( // [HGM] holdings: blank out area between board and holdings
4562 column == BOARD_LEFT-1 || column == BOARD_RGHT
4563 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4564 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4565 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4567 // [HGM] print piece counts next to holdings
4568 string[1] = NULLCHAR;
4569 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4570 string[0] = '0' + piece;
4571 XTextExtents(countFontStruct, string, 1, &direction,
4572 &font_ascent, &font_descent, &overall);
4573 if (appData.monoMode) {
4574 XDrawImageString(xDisplay, xBoardWindow, countGC,
4575 x + squareSize - overall.width - 2,
4576 y + font_ascent + 1, string, 1);
4578 XDrawString(xDisplay, xBoardWindow, countGC,
4579 x + squareSize - overall.width - 2,
4580 y + font_ascent + 1, string, 1);
4583 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4584 string[0] = '0' + piece;
4585 XTextExtents(countFontStruct, string, 1, &direction,
4586 &font_ascent, &font_descent, &overall);
4587 if (appData.monoMode) {
4588 XDrawImageString(xDisplay, xBoardWindow, countGC,
4589 x + 2, y + font_ascent + 1, string, 1);
4591 XDrawString(xDisplay, xBoardWindow, countGC,
4592 x + 2, y + font_ascent + 1, string, 1);
4596 if (piece == EmptySquare || appData.blindfold) {
4597 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4599 drawfunc = ChooseDrawFunc();
4601 if (do_flash && appData.flashCount > 0) {
4602 for (i=0; i<appData.flashCount; ++i) {
4603 drawfunc(piece, square_color, x, y, xBoardWindow);
4604 XSync(xDisplay, False);
4605 do_flash_delay(flash_delay);
4607 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4608 XSync(xDisplay, False);
4609 do_flash_delay(flash_delay);
4612 drawfunc(piece, square_color, x, y, xBoardWindow);
4616 string[1] = NULLCHAR;
4617 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4618 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4619 string[0] = 'a' + column - BOARD_LEFT;
4620 XTextExtents(coordFontStruct, string, 1, &direction,
4621 &font_ascent, &font_descent, &overall);
4622 if (appData.monoMode) {
4623 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4624 x + squareSize - overall.width - 2,
4625 y + squareSize - font_descent - 1, string, 1);
4627 XDrawString(xDisplay, xBoardWindow, coordGC,
4628 x + squareSize - overall.width - 2,
4629 y + squareSize - font_descent - 1, string, 1);
4632 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4633 string[0] = ONE + row;
4634 XTextExtents(coordFontStruct, string, 1, &direction,
4635 &font_ascent, &font_descent, &overall);
4636 if (appData.monoMode) {
4637 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4638 x + 2, y + font_ascent + 1, string, 1);
4640 XDrawString(xDisplay, xBoardWindow, coordGC,
4641 x + 2, y + font_ascent + 1, string, 1);
4644 if(!partnerUp && marker[row][column]) {
4645 if(appData.monoMode) {
4646 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4647 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4648 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4649 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4651 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4652 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4657 /* Why is this needed on some versions of X? */
4658 void EventProc(widget, unused, event)
4663 if (!XtIsRealized(widget))
4666 switch (event->type) {
4668 if (event->xexpose.count > 0) return; /* no clipping is done */
4669 XDrawPosition(widget, True, NULL);
4670 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4671 flipView = !flipView; partnerUp = !partnerUp;
4672 XDrawPosition(widget, True, NULL);
4673 flipView = !flipView; partnerUp = !partnerUp;
4677 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4684 void DrawPosition(fullRedraw, board)
4685 /*Boolean*/int fullRedraw;
4688 XDrawPosition(boardWidget, fullRedraw, board);
4691 /* Returns 1 if there are "too many" differences between b1 and b2
4692 (i.e. more than 1 move was made) */
4693 static int too_many_diffs(b1, b2)
4699 for (i=0; i<BOARD_HEIGHT; ++i) {
4700 for (j=0; j<BOARD_WIDTH; ++j) {
4701 if (b1[i][j] != b2[i][j]) {
4702 if (++c > 4) /* Castling causes 4 diffs */
4710 /* Matrix describing castling maneuvers */
4711 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4712 static int castling_matrix[4][5] = {
4713 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4714 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4715 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4716 { 7, 7, 4, 5, 6 } /* 0-0, black */
4719 /* Checks whether castling occurred. If it did, *rrow and *rcol
4720 are set to the destination (row,col) of the rook that moved.
4722 Returns 1 if castling occurred, 0 if not.
4724 Note: Only handles a max of 1 castling move, so be sure
4725 to call too_many_diffs() first.
4727 static int check_castle_draw(newb, oldb, rrow, rcol)
4734 /* For each type of castling... */
4735 for (i=0; i<4; ++i) {
4736 r = castling_matrix[i];
4738 /* Check the 4 squares involved in the castling move */
4740 for (j=1; j<=4; ++j) {
4741 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4748 /* All 4 changed, so it must be a castling move */
4757 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4758 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4760 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4763 void DrawSeekBackground( int left, int top, int right, int bottom )
4765 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4768 void DrawSeekText(char *buf, int x, int y)
4770 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4773 void DrawSeekDot(int x, int y, int colorNr)
4775 int square = colorNr & 0x80;
4778 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4780 XFillRectangle(xDisplay, xBoardWindow, color,
4781 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4783 XFillArc(xDisplay, xBoardWindow, color,
4784 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4787 static int damage[2][BOARD_RANKS][BOARD_FILES];
4790 * event handler for redrawing the board
4792 void XDrawPosition(w, repaint, board)
4794 /*Boolean*/int repaint;
4798 static int lastFlipView = 0;
4799 static int lastBoardValid[2] = {0, 0};
4800 static Board lastBoard[2];
4803 int nr = twoBoards*partnerUp;
4805 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4807 if (board == NULL) {
4808 if (!lastBoardValid[nr]) return;
4809 board = lastBoard[nr];
4811 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4812 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4813 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4818 * It would be simpler to clear the window with XClearWindow()
4819 * but this causes a very distracting flicker.
4822 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4824 if ( lineGap && IsDrawArrowEnabled())
4825 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4826 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4828 /* If too much changes (begin observing new game, etc.), don't
4830 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4832 /* Special check for castling so we don't flash both the king
4833 and the rook (just flash the king). */
4835 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4836 /* Draw rook with NO flashing. King will be drawn flashing later */
4837 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4838 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4842 /* First pass -- Draw (newly) empty squares and repair damage.
4843 This prevents you from having a piece show up twice while it
4844 is flashing on its new square */
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] && board[i][j] == EmptySquare)
4848 || damage[nr][i][j]) {
4849 DrawSquare(i, j, board[i][j], 0);
4850 damage[nr][i][j] = False;
4853 /* Second pass -- Draw piece(s) in new position and flash them */
4854 for (i = 0; i < BOARD_HEIGHT; i++)
4855 for (j = 0; j < BOARD_WIDTH; j++)
4856 if (board[i][j] != lastBoard[nr][i][j]) {
4857 DrawSquare(i, j, board[i][j], do_flash);
4861 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4862 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4863 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4865 for (i = 0; i < BOARD_HEIGHT; i++)
4866 for (j = 0; j < BOARD_WIDTH; j++) {
4867 DrawSquare(i, j, board[i][j], 0);
4868 damage[nr][i][j] = False;
4872 CopyBoard(lastBoard[nr], board);
4873 lastBoardValid[nr] = 1;
4874 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4875 lastFlipView = flipView;
4877 /* Draw highlights */
4878 if (pm1X >= 0 && pm1Y >= 0) {
4879 drawHighlight(pm1X, pm1Y, prelineGC);
4881 if (pm2X >= 0 && pm2Y >= 0) {
4882 drawHighlight(pm2X, pm2Y, prelineGC);
4884 if (hi1X >= 0 && hi1Y >= 0) {
4885 drawHighlight(hi1X, hi1Y, highlineGC);
4887 if (hi2X >= 0 && hi2Y >= 0) {
4888 drawHighlight(hi2X, hi2Y, highlineGC);
4890 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4892 /* If piece being dragged around board, must redraw that too */
4895 XSync(xDisplay, False);
4900 * event handler for redrawing the board
4902 void DrawPositionProc(w, event, prms, nprms)
4908 XDrawPosition(w, True, NULL);
4913 * event handler for parsing user moves
4915 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4916 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4917 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4918 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4919 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4920 // and at the end FinishMove() to perform the move after optional promotion popups.
4921 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4922 void HandleUserMove(w, event, prms, nprms)
4928 if (w != boardWidget || errorExitStatus != -1) return;
4929 if(nprms) shiftKey = !strcmp(prms[0], "1");
4932 if (event->type == ButtonPress) {
4933 XtPopdown(promotionShell);
4934 XtDestroyWidget(promotionShell);
4935 promotionUp = False;
4943 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4944 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4945 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4948 void AnimateUserMove (Widget w, XEvent * event,
4949 String * params, Cardinal * nParams)
4951 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4952 DragPieceMove(event->xmotion.x, event->xmotion.y);
4955 void HandlePV (Widget w, XEvent * event,
4956 String * params, Cardinal * nParams)
4957 { // [HGM] pv: walk PV
4958 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4961 static int savedIndex; /* gross that this is global */
4963 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4966 XawTextPosition index, dummy;
4969 XawTextGetSelectionPos(w, &index, &dummy);
4970 XtSetArg(arg, XtNstring, &val);
4971 XtGetValues(w, &arg, 1);
4972 ReplaceComment(savedIndex, val);
4973 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4974 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4977 void EditCommentPopUp(index, title, text)
4982 if (text == NULL) text = "";
4983 NewCommentPopup(title, text, index);
4986 void ICSInputBoxPopUp()
4991 extern Option boxOptions[];
4993 void ICSInputSendText()
5000 edit = boxOptions[0].handle;
5002 XtSetArg(args[j], XtNstring, &val); j++;
5003 XtGetValues(edit, args, j);
5005 SendMultiLineToICS(val);
5006 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5007 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5010 void ICSInputBoxPopDown()
5015 void CommentPopUp(title, text)
5018 savedIndex = currentMove; // [HGM] vari
5019 NewCommentPopup(title, text, currentMove);
5022 void CommentPopDown()
5027 static char *openName;
5032 (void) (*fileProc)(openFP, 0, openName);
5035 void FileNamePopUp(label, def, filter, proc, openMode)
5042 fileProc = proc; /* I can't see a way not */
5043 fileOpenMode = openMode; /* to use globals here */
5044 { // [HGM] use file-selector dialog stolen from Ghostview
5045 int index; // this is not supported yet
5046 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5047 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5048 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5049 ScheduleDelayedEvent(&DelayedLoad, 50);
5053 void FileNamePopDown()
5055 if (!filenameUp) return;
5056 XtPopdown(fileNameShell);
5057 XtDestroyWidget(fileNameShell);
5062 void FileNameCallback(w, client_data, call_data)
5064 XtPointer client_data, call_data;
5069 XtSetArg(args[0], XtNlabel, &name);
5070 XtGetValues(w, args, 1);
5072 if (strcmp(name, _("cancel")) == 0) {
5077 FileNameAction(w, NULL, NULL, NULL);
5080 void FileNameAction(w, event, prms, nprms)
5092 name = XawDialogGetValueString(w = XtParent(w));
5094 if ((name != NULL) && (*name != NULLCHAR)) {
5095 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5096 XtPopdown(w = XtParent(XtParent(w)));
5100 p = strrchr(buf, ' ');
5107 fullname = ExpandPathName(buf);
5109 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5112 f = fopen(fullname, fileOpenMode);
5114 DisplayError(_("Failed to open file"), errno);
5116 (void) (*fileProc)(f, index, buf);
5123 XtPopdown(w = XtParent(XtParent(w)));
5129 void PromotionPopUp()
5132 Widget dialog, layout;
5134 Dimension bw_width, pw_width;
5136 char *PromoChars = "wglcqrbnkac+=\0";
5139 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5140 XtGetValues(boardWidget, args, j);
5143 XtSetArg(args[j], XtNresizable, True); j++;
5144 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5146 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5147 shellWidget, args, j);
5149 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5150 layoutArgs, XtNumber(layoutArgs));
5153 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5154 XtSetArg(args[j], XtNborderWidth, 0); j++;
5155 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5158 if(gameInfo.variant != VariantShogi) {
5159 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5160 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5161 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5162 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5163 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5165 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5166 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5167 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5168 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5170 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5171 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5172 gameInfo.variant == VariantGiveaway) {
5173 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5175 if(gameInfo.variant == VariantCapablanca ||
5176 gameInfo.variant == VariantGothic ||
5177 gameInfo.variant == VariantCapaRandom) {
5178 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5179 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5181 } else // [HGM] shogi
5183 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5184 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5186 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5188 XtRealizeWidget(promotionShell);
5189 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5192 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5193 XtGetValues(promotionShell, args, j);
5195 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5196 lineGap + squareSize/3 +
5197 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5198 0 : 6*(squareSize + lineGap)), &x, &y);
5201 XtSetArg(args[j], XtNx, x); j++;
5202 XtSetArg(args[j], XtNy, y); j++;
5203 XtSetValues(promotionShell, args, j);
5205 XtPopup(promotionShell, XtGrabNone);
5210 void PromotionPopDown()
5212 if (!promotionUp) return;
5213 XtPopdown(promotionShell);
5214 XtDestroyWidget(promotionShell);
5215 promotionUp = False;
5218 void PromotionCallback(w, client_data, call_data)
5220 XtPointer client_data, call_data;
5222 int promoChar = * (const char *) client_data;
5226 if (fromX == -1) return;
5233 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5235 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5236 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5241 void ErrorCallback(w, client_data, call_data)
5243 XtPointer client_data, call_data;
5246 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5248 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5254 if (!errorUp) return;
5256 XtPopdown(errorShell);
5257 XtDestroyWidget(errorShell);
5258 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5261 void ErrorPopUp(title, label, modal)
5262 char *title, *label;
5266 Widget dialog, layout;
5270 Dimension bw_width, pw_width;
5271 Dimension pw_height;
5275 XtSetArg(args[i], XtNresizable, True); i++;
5276 XtSetArg(args[i], XtNtitle, title); i++;
5278 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5279 shellWidget, args, i);
5281 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5282 layoutArgs, XtNumber(layoutArgs));
5285 XtSetArg(args[i], XtNlabel, label); i++;
5286 XtSetArg(args[i], XtNborderWidth, 0); i++;
5287 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5290 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5292 XtRealizeWidget(errorShell);
5293 CatchDeleteWindow(errorShell, "ErrorPopDown");
5296 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5297 XtGetValues(boardWidget, args, i);
5299 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5300 XtSetArg(args[i], XtNheight, &pw_height); i++;
5301 XtGetValues(errorShell, args, i);
5304 /* This code seems to tickle an X bug if it is executed too soon
5305 after xboard starts up. The coordinates get transformed as if
5306 the main window was positioned at (0, 0).
5308 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5309 0 - pw_height + squareSize / 3, &x, &y);
5311 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5312 RootWindowOfScreen(XtScreen(boardWidget)),
5313 (bw_width - pw_width) / 2,
5314 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5318 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5321 XtSetArg(args[i], XtNx, x); i++;
5322 XtSetArg(args[i], XtNy, y); i++;
5323 XtSetValues(errorShell, args, i);
5326 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5329 /* Disable all user input other than deleting the window */
5330 static int frozen = 0;
5334 /* Grab by a widget that doesn't accept input */
5335 XtAddGrab(messageWidget, TRUE, FALSE);
5339 /* Undo a FreezeUI */
5342 if (!frozen) return;
5343 XtRemoveGrab(messageWidget);
5347 char *ModeToWidgetName(mode)
5351 case BeginningOfGame:
5352 if (appData.icsActive)
5353 return "menuMode.ICS Client";
5354 else if (appData.noChessProgram ||
5355 *appData.cmailGameName != NULLCHAR)
5356 return "menuMode.Edit Game";
5358 return "menuMode.Machine Black";
5359 case MachinePlaysBlack:
5360 return "menuMode.Machine Black";
5361 case MachinePlaysWhite:
5362 return "menuMode.Machine White";
5364 return "menuMode.Analysis Mode";
5366 return "menuMode.Analyze File";
5367 case TwoMachinesPlay:
5368 return "menuMode.Two Machines";
5370 return "menuMode.Edit Game";
5371 case PlayFromGameFile:
5372 return "menuFile.Load Game";
5374 return "menuMode.Edit Position";
5376 return "menuMode.Training";
5377 case IcsPlayingWhite:
5378 case IcsPlayingBlack:
5382 return "menuMode.ICS Client";
5389 void ModeHighlight()
5392 static int oldPausing = FALSE;
5393 static GameMode oldmode = (GameMode) -1;
5396 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5398 if (pausing != oldPausing) {
5399 oldPausing = pausing;
5401 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5403 XtSetArg(args[0], XtNleftBitmap, None);
5405 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5408 if (appData.showButtonBar) {
5409 /* Always toggle, don't set. Previous code messes up when
5410 invoked while the button is pressed, as releasing it
5411 toggles the state again. */
5414 XtSetArg(args[0], XtNbackground, &oldbg);
5415 XtSetArg(args[1], XtNforeground, &oldfg);
5416 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5418 XtSetArg(args[0], XtNbackground, oldfg);
5419 XtSetArg(args[1], XtNforeground, oldbg);
5421 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5425 wname = ModeToWidgetName(oldmode);
5426 if (wname != NULL) {
5427 XtSetArg(args[0], XtNleftBitmap, None);
5428 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5430 wname = ModeToWidgetName(gameMode);
5431 if (wname != NULL) {
5432 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5433 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5436 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5437 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5439 /* Maybe all the enables should be handled here, not just this one */
5440 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5441 gameMode == Training || gameMode == PlayFromGameFile);
5446 * Button/menu procedures
5448 void ResetProc(w, event, prms, nprms)
5457 int LoadGamePopUp(f, gameNumber, title)
5462 cmailMsgLoaded = FALSE;
5463 if (gameNumber == 0) {
5464 int error = GameListBuild(f);
5466 DisplayError(_("Cannot build game list"), error);
5467 } else if (!ListEmpty(&gameList) &&
5468 ((ListGame *) gameList.tailPred)->number > 1) {
5469 GameListPopUp(f, title);
5475 return LoadGame(f, gameNumber, title, FALSE);
5478 void LoadGameProc(w, event, prms, nprms)
5484 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5487 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5490 void LoadNextGameProc(w, event, prms, nprms)
5499 void LoadPrevGameProc(w, event, prms, nprms)
5508 void ReloadGameProc(w, event, prms, nprms)
5517 void LoadNextPositionProc(w, event, prms, nprms)
5526 void LoadPrevPositionProc(w, event, prms, nprms)
5535 void ReloadPositionProc(w, event, prms, nprms)
5544 void LoadPositionProc(w, event, prms, nprms)
5550 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5553 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5556 void SaveGameProc(w, event, prms, nprms)
5562 FileNamePopUp(_("Save game file name?"),
5563 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5564 appData.oldSaveStyle ? ".game" : ".pgn",
5568 void SavePositionProc(w, event, prms, nprms)
5574 FileNamePopUp(_("Save position file name?"),
5575 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5576 appData.oldSaveStyle ? ".pos" : ".fen",
5580 void ReloadCmailMsgProc(w, event, prms, nprms)
5586 ReloadCmailMsgEvent(FALSE);
5589 void MailMoveProc(w, event, prms, nprms)
5598 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5599 char *selected_fen_position=NULL;
5602 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5603 Atom *type_return, XtPointer *value_return,
5604 unsigned long *length_return, int *format_return)
5606 char *selection_tmp;
5608 if (!selected_fen_position) return False; /* should never happen */
5609 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5610 /* note: since no XtSelectionDoneProc was registered, Xt will
5611 * automatically call XtFree on the value returned. So have to
5612 * make a copy of it allocated with XtMalloc */
5613 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5614 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5616 *value_return=selection_tmp;
5617 *length_return=strlen(selection_tmp);
5618 *type_return=*target;
5619 *format_return = 8; /* bits per byte */
5621 } else if (*target == XA_TARGETS(xDisplay)) {
5622 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5623 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5624 targets_tmp[1] = XA_STRING;
5625 *value_return = targets_tmp;
5626 *type_return = XA_ATOM;
5629 // This code leads to a read of value_return out of bounds on 64-bit systems.
5630 // Other code which I have seen always sets *format_return to 32 independent of
5631 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5632 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5633 *format_return = 8 * sizeof(Atom);
5634 if (*format_return > 32) {
5635 *length_return *= *format_return / 32;
5636 *format_return = 32;
5639 *format_return = 32;
5647 /* note: when called from menu all parameters are NULL, so no clue what the
5648 * Widget which was clicked on was, or what the click event was
5650 void CopyPositionProc(w, event, prms, nprms)
5657 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5658 * have a notion of a position that is selected but not copied.
5659 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5661 if(gameMode == EditPosition) EditPositionDone(TRUE);
5662 if (selected_fen_position) free(selected_fen_position);
5663 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5664 if (!selected_fen_position) return;
5665 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5667 SendPositionSelection,
5668 NULL/* lose_ownership_proc */ ,
5669 NULL/* transfer_done_proc */);
5670 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5672 SendPositionSelection,
5673 NULL/* lose_ownership_proc */ ,
5674 NULL/* transfer_done_proc */);
5677 /* function called when the data to Paste is ready */
5679 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5680 Atom *type, XtPointer value, unsigned long *len, int *format)
5683 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5684 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5685 EditPositionPasteFEN(fenstr);
5689 /* called when Paste Position button is pressed,
5690 * all parameters will be NULL */
5691 void PastePositionProc(w, event, prms, nprms)
5697 XtGetSelectionValue(menuBarWidget,
5698 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5699 /* (XtSelectionCallbackProc) */ PastePositionCB,
5700 NULL, /* client_data passed to PastePositionCB */
5702 /* better to use the time field from the event that triggered the
5703 * call to this function, but that isn't trivial to get
5711 SendGameSelection(Widget w, Atom *selection, Atom *target,
5712 Atom *type_return, XtPointer *value_return,
5713 unsigned long *length_return, int *format_return)
5715 char *selection_tmp;
5717 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5718 FILE* f = fopen(gameCopyFilename, "r");
5721 if (f == NULL) return False;
5725 selection_tmp = XtMalloc(len + 1);
5726 count = fread(selection_tmp, 1, len, f);
5729 XtFree(selection_tmp);
5732 selection_tmp[len] = NULLCHAR;
5733 *value_return = selection_tmp;
5734 *length_return = len;
5735 *type_return = *target;
5736 *format_return = 8; /* bits per byte */
5738 } else if (*target == XA_TARGETS(xDisplay)) {
5739 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5740 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5741 targets_tmp[1] = XA_STRING;
5742 *value_return = targets_tmp;
5743 *type_return = XA_ATOM;
5746 // This code leads to a read of value_return out of bounds on 64-bit systems.
5747 // Other code which I have seen always sets *format_return to 32 independent of
5748 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5749 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5750 *format_return = 8 * sizeof(Atom);
5751 if (*format_return > 32) {
5752 *length_return *= *format_return / 32;
5753 *format_return = 32;
5756 *format_return = 32;
5764 void CopySomething()
5767 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5768 * have a notion of a game that is selected but not copied.
5769 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5771 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5774 NULL/* lose_ownership_proc */ ,
5775 NULL/* transfer_done_proc */);
5776 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5779 NULL/* lose_ownership_proc */ ,
5780 NULL/* transfer_done_proc */);
5783 /* note: when called from menu all parameters are NULL, so no clue what the
5784 * Widget which was clicked on was, or what the click event was
5786 void CopyGameProc(w, event, prms, nprms)
5794 ret = SaveGameToFile(gameCopyFilename, FALSE);
5800 void CopyGameListProc(w, event, prms, nprms)
5806 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5810 /* function called when the data to Paste is ready */
5812 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5813 Atom *type, XtPointer value, unsigned long *len, int *format)
5816 if (value == NULL || *len == 0) {
5817 return; /* nothing had been selected to copy */
5819 f = fopen(gamePasteFilename, "w");
5821 DisplayError(_("Can't open temp file"), errno);
5824 fwrite(value, 1, *len, f);
5827 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5830 /* called when Paste Game button is pressed,
5831 * all parameters will be NULL */
5832 void PasteGameProc(w, event, prms, nprms)
5838 XtGetSelectionValue(menuBarWidget,
5839 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5840 /* (XtSelectionCallbackProc) */ PasteGameCB,
5841 NULL, /* client_data passed to PasteGameCB */
5843 /* better to use the time field from the event that triggered the
5844 * call to this function, but that isn't trivial to get
5854 SaveGameProc(NULL, NULL, NULL, NULL);
5858 void QuitProc(w, event, prms, nprms)
5867 void PauseProc(w, event, prms, nprms)
5877 void MachineBlackProc(w, event, prms, nprms)
5883 MachineBlackEvent();
5886 void MachineWhiteProc(w, event, prms, nprms)
5892 MachineWhiteEvent();
5895 void AnalyzeModeProc(w, event, prms, nprms)
5903 if (!first.analysisSupport) {
5904 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5905 DisplayError(buf, 0);
5908 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5909 if (appData.icsActive) {
5910 if (gameMode != IcsObserving) {
5911 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5912 DisplayError(buf, 0);
5914 if (appData.icsEngineAnalyze) {
5915 if (appData.debugMode)
5916 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5922 /* if enable, use want disable icsEngineAnalyze */
5923 if (appData.icsEngineAnalyze) {
5928 appData.icsEngineAnalyze = TRUE;
5929 if (appData.debugMode)
5930 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5932 #ifndef OPTIONSDIALOG
5933 if (!appData.showThinking)
5934 ShowThinkingProc(w,event,prms,nprms);
5940 void AnalyzeFileProc(w, event, prms, nprms)
5946 if (!first.analysisSupport) {
5948 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5949 DisplayError(buf, 0);
5952 // Reset(FALSE, TRUE);
5953 #ifndef OPTIONSDIALOG
5954 if (!appData.showThinking)
5955 ShowThinkingProc(w,event,prms,nprms);
5958 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5959 AnalysisPeriodicEvent(1);
5962 void TwoMachinesProc(w, event, prms, nprms)
5971 void MatchProc(w, event, prms, nprms)
5980 void IcsClientProc(w, event, prms, nprms)
5989 void EditGameProc(w, event, prms, nprms)
5998 void EditPositionProc(w, event, prms, nprms)
6004 EditPositionEvent();
6007 void TrainingProc(w, event, prms, nprms)
6016 void EditCommentProc(w, event, prms, nprms)
6024 if (PopDown(1)) { // popdown succesful
6026 XtSetArg(args[j], XtNleftBitmap, None); j++;
6027 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6028 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6029 } else // was not up
6033 void IcsInputBoxProc(w, event, prms, nprms)
6039 if (!PopDown(4)) ICSInputBoxPopUp();
6042 void AcceptProc(w, event, prms, nprms)
6051 void DeclineProc(w, event, prms, nprms)
6060 void RematchProc(w, event, prms, nprms)
6069 void CallFlagProc(w, event, prms, nprms)
6078 void DrawProc(w, event, prms, nprms)
6087 void AbortProc(w, event, prms, nprms)
6096 void AdjournProc(w, event, prms, nprms)
6105 void ResignProc(w, event, prms, nprms)
6114 void AdjuWhiteProc(w, event, prms, nprms)
6120 UserAdjudicationEvent(+1);
6123 void AdjuBlackProc(w, event, prms, nprms)
6129 UserAdjudicationEvent(-1);
6132 void AdjuDrawProc(w, event, prms, nprms)
6138 UserAdjudicationEvent(0);
6141 void EnterKeyProc(w, event, prms, nprms)
6147 if (shellUp[4] == True)
6151 void UpKeyProc(w, event, prms, nprms)
6156 { // [HGM] input: let up-arrow recall previous line from history
6163 if (!shellUp[4]) return;
6164 edit = boxOptions[0].handle;
6166 XtSetArg(args[j], XtNstring, &val); j++;
6167 XtGetValues(edit, args, j);
6168 val = PrevInHistory(val);
6169 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6170 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6172 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6173 XawTextReplace(edit, 0, 0, &t);
6174 XawTextSetInsertionPoint(edit, 9999);
6178 void DownKeyProc(w, event, prms, nprms)
6183 { // [HGM] input: let down-arrow recall next line from history
6188 if (!shellUp[4]) return;
6189 edit = boxOptions[0].handle;
6190 val = NextInHistory();
6191 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6192 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6194 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6195 XawTextReplace(edit, 0, 0, &t);
6196 XawTextSetInsertionPoint(edit, 9999);
6200 void StopObservingProc(w, event, prms, nprms)
6206 StopObservingEvent();
6209 void StopExaminingProc(w, event, prms, nprms)
6215 StopExaminingEvent();
6218 void UploadProc(w, event, prms, nprms)
6228 void ForwardProc(w, event, prms, nprms)
6238 void BackwardProc(w, event, prms, nprms)
6247 void TempBackwardProc(w, event, prms, nprms)
6253 if (!TempBackwardActive) {
6254 TempBackwardActive = True;
6259 void TempForwardProc(w, event, prms, nprms)
6265 /* Check to see if triggered by a key release event for a repeating key.
6266 * If so the next queued event will be a key press of the same key at the same time */
6267 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6269 XPeekEvent(xDisplay, &next);
6270 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6271 next.xkey.keycode == event->xkey.keycode)
6275 TempBackwardActive = False;
6278 void ToStartProc(w, event, prms, nprms)
6287 void ToEndProc(w, event, prms, nprms)
6296 void RevertProc(w, event, prms, nprms)
6305 void AnnotateProc(w, event, prms, nprms)
6314 void TruncateGameProc(w, event, prms, nprms)
6320 TruncateGameEvent();
6322 void RetractMoveProc(w, event, prms, nprms)
6331 void MoveNowProc(w, event, prms, nprms)
6340 void FlipViewProc(w, event, prms, nprms)
6346 flipView = !flipView;
6347 DrawPosition(True, NULL);
6350 void PonderNextMoveProc(w, event, prms, nprms)
6358 PonderNextMoveEvent(!appData.ponderNextMove);
6359 #ifndef OPTIONSDIALOG
6360 if (appData.ponderNextMove) {
6361 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6363 XtSetArg(args[0], XtNleftBitmap, None);
6365 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6370 #ifndef OPTIONSDIALOG
6371 void AlwaysQueenProc(w, event, prms, nprms)
6379 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6381 if (appData.alwaysPromoteToQueen) {
6382 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6384 XtSetArg(args[0], XtNleftBitmap, None);
6386 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6390 void AnimateDraggingProc(w, event, prms, nprms)
6398 appData.animateDragging = !appData.animateDragging;
6400 if (appData.animateDragging) {
6401 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6404 XtSetArg(args[0], XtNleftBitmap, None);
6406 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6410 void AnimateMovingProc(w, event, prms, nprms)
6418 appData.animate = !appData.animate;
6420 if (appData.animate) {
6421 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6424 XtSetArg(args[0], XtNleftBitmap, None);
6426 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6430 void AutoflagProc(w, event, prms, nprms)
6438 appData.autoCallFlag = !appData.autoCallFlag;
6440 if (appData.autoCallFlag) {
6441 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6443 XtSetArg(args[0], XtNleftBitmap, None);
6445 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6449 void AutoflipProc(w, event, prms, nprms)
6457 appData.autoFlipView = !appData.autoFlipView;
6459 if (appData.autoFlipView) {
6460 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6462 XtSetArg(args[0], XtNleftBitmap, None);
6464 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6468 void BlindfoldProc(w, event, prms, nprms)
6476 appData.blindfold = !appData.blindfold;
6478 if (appData.blindfold) {
6479 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6481 XtSetArg(args[0], XtNleftBitmap, None);
6483 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6486 DrawPosition(True, NULL);
6489 void TestLegalityProc(w, event, prms, nprms)
6497 appData.testLegality = !appData.testLegality;
6499 if (appData.testLegality) {
6500 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6502 XtSetArg(args[0], XtNleftBitmap, None);
6504 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6509 void FlashMovesProc(w, event, prms, nprms)
6517 if (appData.flashCount == 0) {
6518 appData.flashCount = 3;
6520 appData.flashCount = -appData.flashCount;
6523 if (appData.flashCount > 0) {
6524 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6526 XtSetArg(args[0], XtNleftBitmap, None);
6528 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6533 void HighlightDraggingProc(w, event, prms, nprms)
6541 appData.highlightDragging = !appData.highlightDragging;
6543 if (appData.highlightDragging) {
6544 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6546 XtSetArg(args[0], XtNleftBitmap, None);
6548 XtSetValues(XtNameToWidget(menuBarWidget,
6549 "menuOptions.Highlight Dragging"), args, 1);
6553 void HighlightLastMoveProc(w, event, prms, nprms)
6561 appData.highlightLastMove = !appData.highlightLastMove;
6563 if (appData.highlightLastMove) {
6564 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6566 XtSetArg(args[0], XtNleftBitmap, None);
6568 XtSetValues(XtNameToWidget(menuBarWidget,
6569 "menuOptions.Highlight Last Move"), args, 1);
6572 void HighlightArrowProc(w, event, prms, nprms)
6580 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6582 if (appData.highlightMoveWithArrow) {
6583 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6585 XtSetArg(args[0], XtNleftBitmap, None);
6587 XtSetValues(XtNameToWidget(menuBarWidget,
6588 "menuOptions.Arrow"), args, 1);
6592 void IcsAlarmProc(w, event, prms, nprms)
6600 appData.icsAlarm = !appData.icsAlarm;
6602 if (appData.icsAlarm) {
6603 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6605 XtSetArg(args[0], XtNleftBitmap, None);
6607 XtSetValues(XtNameToWidget(menuBarWidget,
6608 "menuOptions.ICS Alarm"), args, 1);
6612 void MoveSoundProc(w, event, prms, nprms)
6620 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6622 if (appData.ringBellAfterMoves) {
6623 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6625 XtSetArg(args[0], XtNleftBitmap, None);
6627 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6631 void OneClickProc(w, event, prms, nprms)
6639 appData.oneClick = !appData.oneClick;
6641 if (appData.oneClick) {
6642 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6644 XtSetArg(args[0], XtNleftBitmap, None);
6646 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6650 void PeriodicUpdatesProc(w, event, prms, nprms)
6658 PeriodicUpdatesEvent(!appData.periodicUpdates);
6660 if (appData.periodicUpdates) {
6661 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6663 XtSetArg(args[0], XtNleftBitmap, None);
6665 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6669 void PopupExitMessageProc(w, event, prms, nprms)
6677 appData.popupExitMessage = !appData.popupExitMessage;
6679 if (appData.popupExitMessage) {
6680 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6682 XtSetArg(args[0], XtNleftBitmap, None);
6684 XtSetValues(XtNameToWidget(menuBarWidget,
6685 "menuOptions.Popup Exit Message"), args, 1);
6688 void PopupMoveErrorsProc(w, event, prms, nprms)
6696 appData.popupMoveErrors = !appData.popupMoveErrors;
6698 if (appData.popupMoveErrors) {
6699 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6701 XtSetArg(args[0], XtNleftBitmap, None);
6703 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6708 void PremoveProc(w, event, prms, nprms)
6716 appData.premove = !appData.premove;
6718 if (appData.premove) {
6719 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6721 XtSetArg(args[0], XtNleftBitmap, None);
6723 XtSetValues(XtNameToWidget(menuBarWidget,
6724 "menuOptions.Premove"), args, 1);
6728 void ShowCoordsProc(w, event, prms, nprms)
6736 appData.showCoords = !appData.showCoords;
6738 if (appData.showCoords) {
6739 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6741 XtSetArg(args[0], XtNleftBitmap, None);
6743 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6746 DrawPosition(True, NULL);
6749 void ShowThinkingProc(w, event, prms, nprms)
6755 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6756 ShowThinkingEvent();
6759 void HideThinkingProc(w, event, prms, nprms)
6767 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6768 ShowThinkingEvent();
6770 if (appData.hideThinkingFromHuman) {
6771 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6773 XtSetArg(args[0], XtNleftBitmap, None);
6775 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6780 void SaveOnExitProc(w, event, prms, nprms)
6788 saveSettingsOnExit = !saveSettingsOnExit;
6790 if (saveSettingsOnExit) {
6791 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6793 XtSetArg(args[0], XtNleftBitmap, None);
6795 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6799 void SaveSettingsProc(w, event, prms, nprms)
6805 SaveSettings(settingsFileName);
6808 void InfoProc(w, event, prms, nprms)
6815 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6820 void ManProc(w, event, prms, nprms)
6828 if (nprms && *nprms > 0)
6832 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6836 void HintProc(w, event, prms, nprms)
6845 void BookProc(w, event, prms, nprms)
6854 void AboutProc(w, event, prms, nprms)
6862 char *zippy = _(" (with Zippy code)");
6866 snprintf(buf, sizeof(buf),
6868 "Copyright 1991 Digital Equipment Corporation\n"
6869 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6870 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6871 "%s is free software and carries NO WARRANTY;"
6872 "see the file COPYING for more information."),
6873 programVersion, zippy, PACKAGE);
6874 ErrorPopUp(_("About XBoard"), buf, FALSE);
6877 void DebugProc(w, event, prms, nprms)
6883 appData.debugMode = !appData.debugMode;
6886 void AboutGameProc(w, event, prms, nprms)
6895 void NothingProc(w, event, prms, nprms)
6904 void DisplayMessage(message, extMessage)
6905 char *message, *extMessage;
6907 /* display a message in the message widget */
6916 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6921 message = extMessage;
6925 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6927 /* need to test if messageWidget already exists, since this function
6928 can also be called during the startup, if for example a Xresource
6929 is not set up correctly */
6932 XtSetArg(arg, XtNlabel, message);
6933 XtSetValues(messageWidget, &arg, 1);
6939 void DisplayTitle(text)
6944 char title[MSG_SIZ];
6947 if (text == NULL) text = "";
6949 if (appData.titleInWindow) {
6951 XtSetArg(args[i], XtNlabel, text); i++;
6952 XtSetValues(titleWidget, args, i);
6955 if (*text != NULLCHAR) {
6956 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6957 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6958 } else if (appData.icsActive) {
6959 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6960 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6961 } else if (appData.cmailGameName[0] != NULLCHAR) {
6962 snprintf(icon, sizeof(icon), "%s", "CMail");
6963 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6965 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6966 } else if (gameInfo.variant == VariantGothic) {
6967 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6968 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6971 } else if (gameInfo.variant == VariantFalcon) {
6972 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6973 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6975 } else if (appData.noChessProgram) {
6976 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6977 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6979 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6980 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6983 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6984 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6985 XtSetValues(shellWidget, args, i);
6986 XSync(xDisplay, False);
6991 DisplayError(message, error)
6998 if (appData.debugMode || appData.matchMode) {
6999 fprintf(stderr, "%s: %s\n", programName, message);
7002 if (appData.debugMode || appData.matchMode) {
7003 fprintf(stderr, "%s: %s: %s\n",
7004 programName, message, strerror(error));
7006 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7009 ErrorPopUp(_("Error"), message, FALSE);
7013 void DisplayMoveError(message)
7018 DrawPosition(FALSE, NULL);
7019 if (appData.debugMode || appData.matchMode) {
7020 fprintf(stderr, "%s: %s\n", programName, message);
7022 if (appData.popupMoveErrors) {
7023 ErrorPopUp(_("Error"), message, FALSE);
7025 DisplayMessage(message, "");
7030 void DisplayFatalError(message, error, status)
7036 errorExitStatus = status;
7038 fprintf(stderr, "%s: %s\n", programName, message);
7040 fprintf(stderr, "%s: %s: %s\n",
7041 programName, message, strerror(error));
7042 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7045 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7046 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7052 void DisplayInformation(message)
7056 ErrorPopUp(_("Information"), message, TRUE);
7059 void DisplayNote(message)
7063 ErrorPopUp(_("Note"), message, FALSE);
7067 NullXErrorCheck(dpy, error_event)
7069 XErrorEvent *error_event;
7074 void DisplayIcsInteractionTitle(message)
7077 if (oldICSInteractionTitle == NULL) {
7078 /* Magic to find the old window title, adapted from vim */
7079 char *wina = getenv("WINDOWID");
7081 Window win = (Window) atoi(wina);
7082 Window root, parent, *children;
7083 unsigned int nchildren;
7084 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7086 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7087 if (!XQueryTree(xDisplay, win, &root, &parent,
7088 &children, &nchildren)) break;
7089 if (children) XFree((void *)children);
7090 if (parent == root || parent == 0) break;
7093 XSetErrorHandler(oldHandler);
7095 if (oldICSInteractionTitle == NULL) {
7096 oldICSInteractionTitle = "xterm";
7099 printf("\033]0;%s\007", message);
7103 char pendingReplyPrefix[MSG_SIZ];
7104 ProcRef pendingReplyPR;
7106 void AskQuestionProc(w, event, prms, nprms)
7113 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7117 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7120 void AskQuestionPopDown()
7122 if (!askQuestionUp) return;
7123 XtPopdown(askQuestionShell);
7124 XtDestroyWidget(askQuestionShell);
7125 askQuestionUp = False;
7128 void AskQuestionReplyAction(w, event, prms, nprms)
7138 reply = XawDialogGetValueString(w = XtParent(w));
7139 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7140 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7141 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7142 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
7143 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7144 AskQuestionPopDown();
7146 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7149 void AskQuestionCallback(w, client_data, call_data)
7151 XtPointer client_data, call_data;
7156 XtSetArg(args[0], XtNlabel, &name);
7157 XtGetValues(w, args, 1);
7159 if (strcmp(name, _("cancel")) == 0) {
7160 AskQuestionPopDown();
7162 AskQuestionReplyAction(w, NULL, NULL, NULL);
7166 void AskQuestion(title, question, replyPrefix, pr)
7167 char *title, *question, *replyPrefix;
7171 Widget popup, layout, dialog, edit;
7177 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7178 pendingReplyPR = pr;
7181 XtSetArg(args[i], XtNresizable, True); i++;
7182 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7183 askQuestionShell = popup =
7184 XtCreatePopupShell(title, transientShellWidgetClass,
7185 shellWidget, args, i);
7188 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7189 layoutArgs, XtNumber(layoutArgs));
7192 XtSetArg(args[i], XtNlabel, question); i++;
7193 XtSetArg(args[i], XtNvalue, ""); i++;
7194 XtSetArg(args[i], XtNborderWidth, 0); i++;
7195 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7198 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7199 (XtPointer) dialog);
7200 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7201 (XtPointer) dialog);
7203 XtRealizeWidget(popup);
7204 CatchDeleteWindow(popup, "AskQuestionPopDown");
7206 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7207 &x, &y, &win_x, &win_y, &mask);
7209 XtSetArg(args[0], XtNx, x - 10);
7210 XtSetArg(args[1], XtNy, y - 30);
7211 XtSetValues(popup, args, 2);
7213 XtPopup(popup, XtGrabExclusive);
7214 askQuestionUp = True;
7216 edit = XtNameToWidget(dialog, "*value");
7217 XtSetKeyboardFocus(popup, edit);
7225 if (*name == NULLCHAR) {
7227 } else if (strcmp(name, "$") == 0) {
7228 putc(BELLCHAR, stderr);
7231 char *prefix = "", *sep = "";
7232 if(appData.soundProgram[0] == NULLCHAR) return;
7233 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7234 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7242 PlaySound(appData.soundMove);
7248 PlaySound(appData.soundIcsWin);
7254 PlaySound(appData.soundIcsLoss);
7260 PlaySound(appData.soundIcsDraw);
7264 PlayIcsUnfinishedSound()
7266 PlaySound(appData.soundIcsUnfinished);
7272 PlaySound(appData.soundIcsAlarm);
7278 PlaySound(appData.soundTell);
7284 system("stty echo");
7291 system("stty -echo");
7296 RunCommand(char *buf)
7302 Colorize(cc, continuation)
7307 int count, outCount, error;
7309 if (textColors[(int)cc].bg > 0) {
7310 if (textColors[(int)cc].fg > 0) {
7311 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7312 textColors[(int)cc].fg, textColors[(int)cc].bg);
7314 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7315 textColors[(int)cc].bg);
7318 if (textColors[(int)cc].fg > 0) {
7319 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7320 textColors[(int)cc].fg);
7322 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7325 count = strlen(buf);
7326 outCount = OutputToProcess(NoProc, buf, count, &error);
7327 if (outCount < count) {
7328 DisplayFatalError(_("Error writing to display"), error, 1);
7331 if (continuation) return;
7334 PlaySound(appData.soundShout);
7337 PlaySound(appData.soundSShout);
7340 PlaySound(appData.soundChannel1);
7343 PlaySound(appData.soundChannel);
7346 PlaySound(appData.soundKibitz);
7349 PlaySound(appData.soundTell);
7351 case ColorChallenge:
7352 PlaySound(appData.soundChallenge);
7355 PlaySound(appData.soundRequest);
7358 PlaySound(appData.soundSeek);
7369 return getpwuid(getuid())->pw_name;
7373 ExpandPathName(path)
7376 static char static_buf[4*MSG_SIZ];
7377 char *d, *s, buf[4*MSG_SIZ];
7383 while (*s && isspace(*s))
7392 if (*(s+1) == '/') {
7393 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7397 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7398 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7399 pwd = getpwnam(buf);
7402 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7406 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7407 strcat(d, strchr(s+1, '/'));
7411 safeStrCpy(d, s, 4*MSG_SIZ );
7418 static char host_name[MSG_SIZ];
7420 #if HAVE_GETHOSTNAME
7421 gethostname(host_name, MSG_SIZ);
7423 #else /* not HAVE_GETHOSTNAME */
7424 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7425 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7427 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7429 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7430 #endif /* not HAVE_GETHOSTNAME */
7433 XtIntervalId delayedEventTimerXID = 0;
7434 DelayedEventCallback delayedEventCallback = 0;
7439 delayedEventTimerXID = 0;
7440 delayedEventCallback();
7444 ScheduleDelayedEvent(cb, millisec)
7445 DelayedEventCallback cb; long millisec;
7447 if(delayedEventTimerXID && delayedEventCallback == cb)
7448 // [HGM] alive: replace, rather than add or flush identical event
7449 XtRemoveTimeOut(delayedEventTimerXID);
7450 delayedEventCallback = cb;
7451 delayedEventTimerXID =
7452 XtAppAddTimeOut(appContext, millisec,
7453 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7456 DelayedEventCallback
7459 if (delayedEventTimerXID) {
7460 return delayedEventCallback;
7467 CancelDelayedEvent()
7469 if (delayedEventTimerXID) {
7470 XtRemoveTimeOut(delayedEventTimerXID);
7471 delayedEventTimerXID = 0;
7475 XtIntervalId loadGameTimerXID = 0;
7477 int LoadGameTimerRunning()
7479 return loadGameTimerXID != 0;
7482 int StopLoadGameTimer()
7484 if (loadGameTimerXID != 0) {
7485 XtRemoveTimeOut(loadGameTimerXID);
7486 loadGameTimerXID = 0;
7494 LoadGameTimerCallback(arg, id)
7498 loadGameTimerXID = 0;
7503 StartLoadGameTimer(millisec)
7507 XtAppAddTimeOut(appContext, millisec,
7508 (XtTimerCallbackProc) LoadGameTimerCallback,
7512 XtIntervalId analysisClockXID = 0;
7515 AnalysisClockCallback(arg, id)
7519 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7520 || appData.icsEngineAnalyze) { // [DM]
7521 AnalysisPeriodicEvent(0);
7522 StartAnalysisClock();
7527 StartAnalysisClock()
7530 XtAppAddTimeOut(appContext, 2000,
7531 (XtTimerCallbackProc) AnalysisClockCallback,
7535 XtIntervalId clockTimerXID = 0;
7537 int ClockTimerRunning()
7539 return clockTimerXID != 0;
7542 int StopClockTimer()
7544 if (clockTimerXID != 0) {
7545 XtRemoveTimeOut(clockTimerXID);
7554 ClockTimerCallback(arg, id)
7563 StartClockTimer(millisec)
7567 XtAppAddTimeOut(appContext, millisec,
7568 (XtTimerCallbackProc) ClockTimerCallback,
7573 DisplayTimerLabel(w, color, timer, highlight)
7582 /* check for low time warning */
7583 Pixel foregroundOrWarningColor = timerForegroundPixel;
7586 appData.lowTimeWarning &&
7587 (timer / 1000) < appData.icsAlarmTime)
7588 foregroundOrWarningColor = lowTimeWarningColor;
7590 if (appData.clockMode) {
7591 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7592 XtSetArg(args[0], XtNlabel, buf);
7594 snprintf(buf, MSG_SIZ, "%s ", color);
7595 XtSetArg(args[0], XtNlabel, buf);
7600 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7601 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7603 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7604 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7607 XtSetValues(w, args, 3);
7611 DisplayWhiteClock(timeRemaining, highlight)
7617 if(appData.noGUI) return;
7618 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7619 if (highlight && iconPixmap == bIconPixmap) {
7620 iconPixmap = wIconPixmap;
7621 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7622 XtSetValues(shellWidget, args, 1);
7627 DisplayBlackClock(timeRemaining, highlight)
7633 if(appData.noGUI) return;
7634 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7635 if (highlight && iconPixmap == wIconPixmap) {
7636 iconPixmap = bIconPixmap;
7637 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7638 XtSetValues(shellWidget, args, 1);
7656 int StartChildProcess(cmdLine, dir, pr)
7663 int to_prog[2], from_prog[2];
7667 if (appData.debugMode) {
7668 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7671 /* We do NOT feed the cmdLine to the shell; we just
7672 parse it into blank-separated arguments in the
7673 most simple-minded way possible.
7676 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7679 while(*p == ' ') p++;
7681 if(*p == '"' || *p == '\'')
7682 p = strchr(++argv[i-1], *p);
7683 else p = strchr(p, ' ');
7684 if (p == NULL) break;
7689 SetUpChildIO(to_prog, from_prog);
7691 if ((pid = fork()) == 0) {
7693 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7694 close(to_prog[1]); // first close the unused pipe ends
7695 close(from_prog[0]);
7696 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7697 dup2(from_prog[1], 1);
7698 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7699 close(from_prog[1]); // and closing again loses one of the pipes!
7700 if(fileno(stderr) >= 2) // better safe than sorry...
7701 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7703 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7708 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7710 execvp(argv[0], argv);
7712 /* If we get here, exec failed */
7717 /* Parent process */
7719 close(from_prog[1]);
7721 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7724 cp->fdFrom = from_prog[0];
7725 cp->fdTo = to_prog[1];
7730 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7731 static RETSIGTYPE AlarmCallBack(int n)
7737 DestroyChildProcess(pr, signalType)
7741 ChildProc *cp = (ChildProc *) pr;
7743 if (cp->kind != CPReal) return;
7745 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7746 signal(SIGALRM, AlarmCallBack);
7748 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7749 kill(cp->pid, SIGKILL); // kill it forcefully
7750 wait((int *) 0); // and wait again
7754 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7756 /* Process is exiting either because of the kill or because of
7757 a quit command sent by the backend; either way, wait for it to die.
7766 InterruptChildProcess(pr)
7769 ChildProc *cp = (ChildProc *) pr;
7771 if (cp->kind != CPReal) return;
7772 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7775 int OpenTelnet(host, port, pr)
7780 char cmdLine[MSG_SIZ];
7782 if (port[0] == NULLCHAR) {
7783 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7785 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7787 return StartChildProcess(cmdLine, "", pr);
7790 int OpenTCP(host, port, pr)
7796 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7797 #else /* !OMIT_SOCKETS */
7798 struct addrinfo hints;
7799 struct addrinfo *ais, *ai;
7804 memset(&hints, 0, sizeof(hints));
7805 hints.ai_family = AF_UNSPEC;
7806 hints.ai_socktype = SOCK_STREAM;
7808 error = getaddrinfo(host, port, &hints, &ais);
7810 /* a getaddrinfo error is not an errno, so can't return it */
7811 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7812 host, port, gai_strerror(error));
7816 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7817 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7821 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7834 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7840 #endif /* !OMIT_SOCKETS */
7845 int OpenCommPort(name, pr)
7852 fd = open(name, 2, 0);
7853 if (fd < 0) return errno;
7855 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7865 int OpenLoopback(pr)
7871 SetUpChildIO(to, from);
7873 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7876 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7883 int OpenRcmd(host, user, cmd, pr)
7884 char *host, *user, *cmd;
7887 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7891 #define INPUT_SOURCE_BUF_SIZE 8192
7900 char buf[INPUT_SOURCE_BUF_SIZE];
7905 DoInputCallback(closure, source, xid)
7910 InputSource *is = (InputSource *) closure;
7915 if (is->lineByLine) {
7916 count = read(is->fd, is->unused,
7917 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7919 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7922 is->unused += count;
7924 while (p < is->unused) {
7925 q = memchr(p, '\n', is->unused - p);
7926 if (q == NULL) break;
7928 (is->func)(is, is->closure, p, q - p, 0);
7932 while (p < is->unused) {
7937 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7942 (is->func)(is, is->closure, is->buf, count, error);
7946 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7953 ChildProc *cp = (ChildProc *) pr;
7955 is = (InputSource *) calloc(1, sizeof(InputSource));
7956 is->lineByLine = lineByLine;
7960 is->fd = fileno(stdin);
7962 is->kind = cp->kind;
7963 is->fd = cp->fdFrom;
7966 is->unused = is->buf;
7969 is->xid = XtAppAddInput(appContext, is->fd,
7970 (XtPointer) (XtInputReadMask),
7971 (XtInputCallbackProc) DoInputCallback,
7973 is->closure = closure;
7974 return (InputSourceRef) is;
7978 RemoveInputSource(isr)
7981 InputSource *is = (InputSource *) isr;
7983 if (is->xid == 0) return;
7984 XtRemoveInput(is->xid);
7988 int OutputToProcess(pr, message, count, outError)
7994 static int line = 0;
7995 ChildProc *cp = (ChildProc *) pr;
8000 if (appData.noJoin || !appData.useInternalWrap)
8001 outCount = fwrite(message, 1, count, stdout);
8004 int width = get_term_width();
8005 int len = wrap(NULL, message, count, width, &line);
8006 char *msg = malloc(len);
8010 outCount = fwrite(message, 1, count, stdout);
8013 dbgchk = wrap(msg, message, count, width, &line);
8014 if (dbgchk != len && appData.debugMode)
8015 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8016 outCount = fwrite(msg, 1, dbgchk, stdout);
8022 outCount = write(cp->fdTo, message, count);
8032 /* Output message to process, with "ms" milliseconds of delay
8033 between each character. This is needed when sending the logon
8034 script to ICC, which for some reason doesn't like the
8035 instantaneous send. */
8036 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8043 ChildProc *cp = (ChildProc *) pr;
8048 r = write(cp->fdTo, message++, 1);
8061 /**** Animation code by Hugh Fisher, DCS, ANU.
8063 Known problem: if a window overlapping the board is
8064 moved away while a piece is being animated underneath,
8065 the newly exposed area won't be updated properly.
8066 I can live with this.
8068 Known problem: if you look carefully at the animation
8069 of pieces in mono mode, they are being drawn as solid
8070 shapes without interior detail while moving. Fixing
8071 this would be a major complication for minimal return.
8074 /* Masks for XPM pieces. Black and white pieces can have
8075 different shapes, but in the interest of retaining my
8076 sanity pieces must have the same outline on both light
8077 and dark squares, and all pieces must use the same
8078 background square colors/images. */
8080 static int xpmDone = 0;
8083 CreateAnimMasks (pieceDepth)
8090 unsigned long plane;
8093 /* Need a bitmap just to get a GC with right depth */
8094 buf = XCreatePixmap(xDisplay, xBoardWindow,
8096 values.foreground = 1;
8097 values.background = 0;
8098 /* Don't use XtGetGC, not read only */
8099 maskGC = XCreateGC(xDisplay, buf,
8100 GCForeground | GCBackground, &values);
8101 XFreePixmap(xDisplay, buf);
8103 buf = XCreatePixmap(xDisplay, xBoardWindow,
8104 squareSize, squareSize, pieceDepth);
8105 values.foreground = XBlackPixel(xDisplay, xScreen);
8106 values.background = XWhitePixel(xDisplay, xScreen);
8107 bufGC = XCreateGC(xDisplay, buf,
8108 GCForeground | GCBackground, &values);
8110 for (piece = WhitePawn; piece <= BlackKing; piece++) {
8111 /* Begin with empty mask */
8112 if(!xpmDone) // [HGM] pieces: keep using existing
8113 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8114 squareSize, squareSize, 1);
8115 XSetFunction(xDisplay, maskGC, GXclear);
8116 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8117 0, 0, squareSize, squareSize);
8119 /* Take a copy of the piece */
8124 XSetFunction(xDisplay, bufGC, GXcopy);
8125 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8127 0, 0, squareSize, squareSize, 0, 0);
8129 /* XOR the background (light) over the piece */
8130 XSetFunction(xDisplay, bufGC, GXxor);
8132 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8133 0, 0, squareSize, squareSize, 0, 0);
8135 XSetForeground(xDisplay, bufGC, lightSquareColor);
8136 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8139 /* We now have an inverted piece image with the background
8140 erased. Construct mask by just selecting all the non-zero
8141 pixels - no need to reconstruct the original image. */
8142 XSetFunction(xDisplay, maskGC, GXor);
8144 /* Might be quicker to download an XImage and create bitmap
8145 data from it rather than this N copies per piece, but it
8146 only takes a fraction of a second and there is a much
8147 longer delay for loading the pieces. */
8148 for (n = 0; n < pieceDepth; n ++) {
8149 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8150 0, 0, squareSize, squareSize,
8156 XFreePixmap(xDisplay, buf);
8157 XFreeGC(xDisplay, bufGC);
8158 XFreeGC(xDisplay, maskGC);
8162 InitAnimState (anim, info)
8164 XWindowAttributes * info;
8169 /* Each buffer is square size, same depth as window */
8170 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8171 squareSize, squareSize, info->depth);
8172 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8173 squareSize, squareSize, info->depth);
8175 /* Create a plain GC for blitting */
8176 mask = GCForeground | GCBackground | GCFunction |
8177 GCPlaneMask | GCGraphicsExposures;
8178 values.foreground = XBlackPixel(xDisplay, xScreen);
8179 values.background = XWhitePixel(xDisplay, xScreen);
8180 values.function = GXcopy;
8181 values.plane_mask = AllPlanes;
8182 values.graphics_exposures = False;
8183 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8185 /* Piece will be copied from an existing context at
8186 the start of each new animation/drag. */
8187 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8189 /* Outline will be a read-only copy of an existing */
8190 anim->outlineGC = None;
8196 XWindowAttributes info;
8198 if (xpmDone && gameInfo.variant == oldVariant) return;
8199 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8200 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8202 InitAnimState(&game, &info);
8203 InitAnimState(&player, &info);
8205 /* For XPM pieces, we need bitmaps to use as masks. */
8207 CreateAnimMasks(info.depth), xpmDone = 1;
8212 static Boolean frameWaiting;
8214 static RETSIGTYPE FrameAlarm (sig)
8217 frameWaiting = False;
8218 /* In case System-V style signals. Needed?? */
8219 signal(SIGALRM, FrameAlarm);
8226 struct itimerval delay;
8228 XSync(xDisplay, False);
8231 frameWaiting = True;
8232 signal(SIGALRM, FrameAlarm);
8233 delay.it_interval.tv_sec =
8234 delay.it_value.tv_sec = time / 1000;
8235 delay.it_interval.tv_usec =
8236 delay.it_value.tv_usec = (time % 1000) * 1000;
8237 setitimer(ITIMER_REAL, &delay, NULL);
8238 while (frameWaiting) pause();
8239 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8240 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8241 setitimer(ITIMER_REAL, &delay, NULL);
8251 XSync(xDisplay, False);
8253 usleep(time * 1000);
8264 /* Convert board position to corner of screen rect and color */
8267 ScreenSquare(column, row, pt, color)
8268 int column; int row; XPoint * pt; int * color;
8271 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8272 pt->y = lineGap + row * (squareSize + lineGap);
8274 pt->x = lineGap + column * (squareSize + lineGap);
8275 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8277 *color = SquareColor(row, column);
8280 /* Convert window coords to square */
8283 BoardSquare(x, y, column, row)
8284 int x; int y; int * column; int * row;
8286 *column = EventToSquare(x, BOARD_WIDTH);
8287 if (flipView && *column >= 0)
8288 *column = BOARD_WIDTH - 1 - *column;
8289 *row = EventToSquare(y, BOARD_HEIGHT);
8290 if (!flipView && *row >= 0)
8291 *row = BOARD_HEIGHT - 1 - *row;
8296 #undef Max /* just in case */
8298 #define Max(a, b) ((a) > (b) ? (a) : (b))
8299 #define Min(a, b) ((a) < (b) ? (a) : (b))
8302 SetRect(rect, x, y, width, height)
8303 XRectangle * rect; int x; int y; int width; int height;
8307 rect->width = width;
8308 rect->height = height;
8311 /* Test if two frames overlap. If they do, return
8312 intersection rect within old and location of
8313 that rect within new. */
8316 Intersect(old, new, size, area, pt)
8317 XPoint * old; XPoint * new;
8318 int size; XRectangle * area; XPoint * pt;
8320 if (old->x > new->x + size || new->x > old->x + size ||
8321 old->y > new->y + size || new->y > old->y + size) {
8324 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8325 size - abs(old->x - new->x), size - abs(old->y - new->y));
8326 pt->x = Max(old->x - new->x, 0);
8327 pt->y = Max(old->y - new->y, 0);
8332 /* For two overlapping frames, return the rect(s)
8333 in the old that do not intersect with the new. */
8336 CalcUpdateRects(old, new, size, update, nUpdates)
8337 XPoint * old; XPoint * new; int size;
8338 XRectangle update[]; int * nUpdates;
8342 /* If old = new (shouldn't happen) then nothing to draw */
8343 if (old->x == new->x && old->y == new->y) {
8347 /* Work out what bits overlap. Since we know the rects
8348 are the same size we don't need a full intersect calc. */
8350 /* Top or bottom edge? */
8351 if (new->y > old->y) {
8352 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8354 } else if (old->y > new->y) {
8355 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8356 size, old->y - new->y);
8359 /* Left or right edge - don't overlap any update calculated above. */
8360 if (new->x > old->x) {
8361 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8362 new->x - old->x, size - abs(new->y - old->y));
8364 } else if (old->x > new->x) {
8365 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8366 old->x - new->x, size - abs(new->y - old->y));
8373 /* Generate a series of frame coords from start->mid->finish.
8374 The movement rate doubles until the half way point is
8375 reached, then halves back down to the final destination,
8376 which gives a nice slow in/out effect. The algorithmn
8377 may seem to generate too many intermediates for short
8378 moves, but remember that the purpose is to attract the
8379 viewers attention to the piece about to be moved and
8380 then to where it ends up. Too few frames would be less
8384 Tween(start, mid, finish, factor, frames, nFrames)
8385 XPoint * start; XPoint * mid;
8386 XPoint * finish; int factor;
8387 XPoint frames[]; int * nFrames;
8389 int fraction, n, count;
8393 /* Slow in, stepping 1/16th, then 1/8th, ... */
8395 for (n = 0; n < factor; n++)
8397 for (n = 0; n < factor; n++) {
8398 frames[count].x = start->x + (mid->x - start->x) / fraction;
8399 frames[count].y = start->y + (mid->y - start->y) / fraction;
8401 fraction = fraction / 2;
8405 frames[count] = *mid;
8408 /* Slow out, stepping 1/2, then 1/4, ... */
8410 for (n = 0; n < factor; n++) {
8411 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8412 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8414 fraction = fraction * 2;
8419 /* Draw a piece on the screen without disturbing what's there */
8422 SelectGCMask(piece, clip, outline, mask)
8423 ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8427 /* Bitmap for piece being moved. */
8428 if (appData.monoMode) {
8429 *mask = *pieceToSolid(piece);
8430 } else if (useImages) {
8432 *mask = xpmMask[piece];
8434 *mask = ximMaskPm[piece];
8437 *mask = *pieceToSolid(piece);
8440 /* GC for piece being moved. Square color doesn't matter, but
8441 since it gets modified we make a copy of the original. */
8443 if (appData.monoMode)
8448 if (appData.monoMode)
8453 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8455 /* Outline only used in mono mode and is not modified */
8457 *outline = bwPieceGC;
8459 *outline = wbPieceGC;
8463 OverlayPiece(piece, clip, outline, dest)
8464 ChessSquare piece; GC clip; GC outline; Drawable dest;
8469 /* Draw solid rectangle which will be clipped to shape of piece */
8470 XFillRectangle(xDisplay, dest, clip,
8471 0, 0, squareSize, squareSize);
8472 if (appData.monoMode)
8473 /* Also draw outline in contrasting color for black
8474 on black / white on white cases */
8475 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8476 0, 0, squareSize, squareSize, 0, 0, 1);
8478 /* Copy the piece */
8483 if(appData.upsideDown && flipView) kind ^= 2;
8484 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8486 0, 0, squareSize, squareSize,
8491 /* Animate the movement of a single piece */
8494 BeginAnimation(anim, piece, startColor, start)
8502 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8503 /* The old buffer is initialised with the start square (empty) */
8504 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8505 anim->prevFrame = *start;
8507 /* The piece will be drawn using its own bitmap as a matte */
8508 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8509 XSetClipMask(xDisplay, anim->pieceGC, mask);
8513 AnimationFrame(anim, frame, piece)
8518 XRectangle updates[4];
8523 /* Save what we are about to draw into the new buffer */
8524 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8525 frame->x, frame->y, squareSize, squareSize,
8528 /* Erase bits of the previous frame */
8529 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8530 /* Where the new frame overlapped the previous,
8531 the contents in newBuf are wrong. */
8532 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8533 overlap.x, overlap.y,
8534 overlap.width, overlap.height,
8536 /* Repaint the areas in the old that don't overlap new */
8537 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8538 for (i = 0; i < count; i++)
8539 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8540 updates[i].x - anim->prevFrame.x,
8541 updates[i].y - anim->prevFrame.y,
8542 updates[i].width, updates[i].height,
8543 updates[i].x, updates[i].y);
8545 /* Easy when no overlap */
8546 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8547 0, 0, squareSize, squareSize,
8548 anim->prevFrame.x, anim->prevFrame.y);
8551 /* Save this frame for next time round */
8552 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8553 0, 0, squareSize, squareSize,
8555 anim->prevFrame = *frame;
8557 /* Draw piece over original screen contents, not current,
8558 and copy entire rect. Wipes out overlapping piece images. */
8559 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8560 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8561 0, 0, squareSize, squareSize,
8562 frame->x, frame->y);
8566 EndAnimation (anim, finish)
8570 XRectangle updates[4];
8575 /* The main code will redraw the final square, so we
8576 only need to erase the bits that don't overlap. */
8577 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8578 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8579 for (i = 0; i < count; i++)
8580 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8581 updates[i].x - anim->prevFrame.x,
8582 updates[i].y - anim->prevFrame.y,
8583 updates[i].width, updates[i].height,
8584 updates[i].x, updates[i].y);
8586 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8587 0, 0, squareSize, squareSize,
8588 anim->prevFrame.x, anim->prevFrame.y);
8593 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8595 ChessSquare piece; int startColor;
8596 XPoint * start; XPoint * finish;
8597 XPoint frames[]; int nFrames;
8601 BeginAnimation(anim, piece, startColor, start);
8602 for (n = 0; n < nFrames; n++) {
8603 AnimationFrame(anim, &(frames[n]), piece);
8604 FrameDelay(appData.animSpeed);
8606 EndAnimation(anim, finish);
8610 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8613 ChessSquare piece = board[fromY][toY];
8614 board[fromY][toY] = EmptySquare;
8615 DrawPosition(FALSE, board);
8617 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8618 y = lineGap + toY * (squareSize + lineGap);
8620 x = lineGap + toX * (squareSize + lineGap);
8621 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8623 for(i=1; i<4*kFactor; i++) {
8624 int r = squareSize * 9 * i/(20*kFactor - 5);
8625 XFillArc(xDisplay, xBoardWindow, highlineGC,
8626 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8627 FrameDelay(appData.animSpeed);
8629 board[fromY][toY] = piece;
8632 /* Main control logic for deciding what to animate and how */
8635 AnimateMove(board, fromX, fromY, toX, toY)
8644 XPoint start, finish, mid;
8645 XPoint frames[kFactor * 2 + 1];
8646 int nFrames, startColor, endColor;
8648 /* Are we animating? */
8649 if (!appData.animate || appData.blindfold)
8652 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8653 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8654 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8656 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8657 piece = board[fromY][fromX];
8658 if (piece >= EmptySquare) return;
8663 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8666 if (appData.debugMode) {
8667 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8668 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8669 piece, fromX, fromY, toX, toY); }
8671 ScreenSquare(fromX, fromY, &start, &startColor);
8672 ScreenSquare(toX, toY, &finish, &endColor);
8675 /* Knight: make straight movement then diagonal */
8676 if (abs(toY - fromY) < abs(toX - fromX)) {
8677 mid.x = start.x + (finish.x - start.x) / 2;
8681 mid.y = start.y + (finish.y - start.y) / 2;
8684 mid.x = start.x + (finish.x - start.x) / 2;
8685 mid.y = start.y + (finish.y - start.y) / 2;
8688 /* Don't use as many frames for very short moves */
8689 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8690 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8692 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8693 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8694 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8696 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8697 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8700 /* Be sure end square is redrawn */
8701 damage[0][toY][toX] = True;
8705 DragPieceBegin(x, y, instantly)
8706 int x; int y; Boolean instantly;
8708 int boardX, boardY, color;
8711 /* Are we animating? */
8712 if (!appData.animateDragging || appData.blindfold)
8715 /* Figure out which square we start in and the
8716 mouse position relative to top left corner. */
8717 BoardSquare(x, y, &boardX, &boardY);
8718 player.startBoardX = boardX;
8719 player.startBoardY = boardY;
8720 ScreenSquare(boardX, boardY, &corner, &color);
8721 player.startSquare = corner;
8722 player.startColor = color;
8723 /* As soon as we start dragging, the piece will jump slightly to
8724 be centered over the mouse pointer. */
8725 player.mouseDelta.x = squareSize/2;
8726 player.mouseDelta.y = squareSize/2;
8727 /* Initialise animation */
8728 player.dragPiece = PieceForSquare(boardX, boardY);
8730 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8731 player.dragActive = True;
8732 BeginAnimation(&player, player.dragPiece, color, &corner);
8733 /* Mark this square as needing to be redrawn. Note that
8734 we don't remove the piece though, since logically (ie
8735 as seen by opponent) the move hasn't been made yet. */
8736 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8737 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8738 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8739 corner.x, corner.y, squareSize, squareSize,
8740 0, 0); // [HGM] zh: unstack in stead of grab
8741 if(gatingPiece != EmptySquare) {
8742 /* Kludge alert: When gating we want the introduced
8743 piece to appear on the from square. To generate an
8744 image of it, we draw it on the board, copy the image,
8745 and draw the original piece again. */
8746 ChessSquare piece = boards[currentMove][boardY][boardX];
8747 DrawSquare(boardY, boardX, gatingPiece, 0);
8748 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8749 corner.x, corner.y, squareSize, squareSize, 0, 0);
8750 DrawSquare(boardY, boardX, piece, 0);
8752 damage[0][boardY][boardX] = True;
8754 player.dragActive = False;
8759 ChangeDragPiece(ChessSquare piece)
8762 player.dragPiece = piece;
8763 /* The piece will be drawn using its own bitmap as a matte */
8764 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8765 XSetClipMask(xDisplay, player.pieceGC, mask);
8774 /* Are we animating? */
8775 if (!appData.animateDragging || appData.blindfold)
8779 if (! player.dragActive)
8781 /* Move piece, maintaining same relative position
8782 of mouse within square */
8783 corner.x = x - player.mouseDelta.x;
8784 corner.y = y - player.mouseDelta.y;
8785 AnimationFrame(&player, &corner, player.dragPiece);
8787 if (appData.highlightDragging) {
8789 BoardSquare(x, y, &boardX, &boardY);
8790 SetHighlights(fromX, fromY, boardX, boardY);
8799 int boardX, boardY, color;
8802 /* Are we animating? */
8803 if (!appData.animateDragging || appData.blindfold)
8807 if (! player.dragActive)
8809 /* Last frame in sequence is square piece is
8810 placed on, which may not match mouse exactly. */
8811 BoardSquare(x, y, &boardX, &boardY);
8812 ScreenSquare(boardX, boardY, &corner, &color);
8813 EndAnimation(&player, &corner);
8815 /* Be sure end square is redrawn */
8816 damage[0][boardY][boardX] = True;
8818 /* This prevents weird things happening with fast successive
8819 clicks which on my Sun at least can cause motion events
8820 without corresponding press/release. */
8821 player.dragActive = False;
8824 /* Handle expose event while piece being dragged */
8829 if (!player.dragActive || appData.blindfold)
8832 /* What we're doing: logically, the move hasn't been made yet,
8833 so the piece is still in it's original square. But visually
8834 it's being dragged around the board. So we erase the square
8835 that the piece is on and draw it at the last known drag point. */
8836 BlankSquare(player.startSquare.x, player.startSquare.y,
8837 player.startColor, EmptySquare, xBoardWindow, 1);
8838 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8839 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8842 #include <sys/ioctl.h>
8843 int get_term_width()
8845 int fd, default_width;
8848 default_width = 79; // this is FICS default anyway...
8850 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8852 if (!ioctl(fd, TIOCGSIZE, &win))
8853 default_width = win.ts_cols;
8854 #elif defined(TIOCGWINSZ)
8856 if (!ioctl(fd, TIOCGWINSZ, &win))
8857 default_width = win.ws_col;
8859 return default_width;
8865 static int old_width = 0;
8866 int new_width = get_term_width();
8868 if (old_width != new_width)
8869 ics_printf("set width %d\n", new_width);
8870 old_width = new_width;
8873 void NotifyFrontendLogin()
8878 /* [AS] Arrow highlighting support */
8880 static double A_WIDTH = 5; /* Width of arrow body */
8882 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8883 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8885 static double Sqr( double x )
8890 static int Round( double x )
8892 return (int) (x + 0.5);
8895 void SquareToPos(int rank, int file, int *x, int *y)
8898 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8899 *y = lineGap + rank * (squareSize + lineGap);
8901 *x = lineGap + file * (squareSize + lineGap);
8902 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8906 /* Draw an arrow between two points using current settings */
8907 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8910 double dx, dy, j, k, x, y;
8913 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8915 arrow[0].x = s_x + A_WIDTH + 0.5;
8918 arrow[1].x = s_x + A_WIDTH + 0.5;
8919 arrow[1].y = d_y - h;
8921 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8922 arrow[2].y = d_y - h;
8927 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8928 arrow[5].y = d_y - h;
8930 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8931 arrow[4].y = d_y - h;
8933 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8936 else if( d_y == s_y ) {
8937 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8940 arrow[0].y = s_y + A_WIDTH + 0.5;
8942 arrow[1].x = d_x - w;
8943 arrow[1].y = s_y + A_WIDTH + 0.5;
8945 arrow[2].x = d_x - w;
8946 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8951 arrow[5].x = d_x - w;
8952 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8954 arrow[4].x = d_x - w;
8955 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8958 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8961 /* [AS] Needed a lot of paper for this! :-) */
8962 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8963 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8965 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8967 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8972 arrow[0].x = Round(x - j);
8973 arrow[0].y = Round(y + j*dx);
8975 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8976 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8979 x = (double) d_x - k;
8980 y = (double) d_y - k*dy;
8983 x = (double) d_x + k;
8984 y = (double) d_y + k*dy;
8987 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8989 arrow[6].x = Round(x - j);
8990 arrow[6].y = Round(y + j*dx);
8992 arrow[2].x = Round(arrow[6].x + 2*j);
8993 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8995 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8996 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
9001 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9002 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9005 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9006 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
9007 // Polygon( hdc, arrow, 7 );
9010 /* [AS] Draw an arrow between two squares */
9011 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9013 int s_x, s_y, d_x, d_y, hor, vert, i;
9015 if( s_col == d_col && s_row == d_row ) {
9019 /* Get source and destination points */
9020 SquareToPos( s_row, s_col, &s_x, &s_y);
9021 SquareToPos( d_row, d_col, &d_x, &d_y);
9024 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9026 else if( d_y < s_y ) {
9027 d_y += squareSize / 2 + squareSize / 4;
9030 d_y += squareSize / 2;
9034 d_x += squareSize / 2 - squareSize / 4;
9036 else if( d_x < s_x ) {
9037 d_x += squareSize / 2 + squareSize / 4;
9040 d_x += squareSize / 2;
9043 s_x += squareSize / 2;
9044 s_y += squareSize / 2;
9047 A_WIDTH = squareSize / 14.; //[HGM] make float
9049 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9051 hor = 64*s_col + 32; vert = 64*s_row + 32;
9052 for(i=0; i<= 64; i++) {
9053 damage[0][vert+6>>6][hor+6>>6] = True;
9054 damage[0][vert-6>>6][hor+6>>6] = True;
9055 damage[0][vert+6>>6][hor-6>>6] = True;
9056 damage[0][vert-6>>6][hor-6>>6] = True;
9057 hor += d_col - s_col; vert += d_row - s_row;
9061 Boolean IsDrawArrowEnabled()
9063 return appData.highlightMoveWithArrow && squareSize >= 32;
9066 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9068 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9069 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9072 void UpdateLogos(int displ)
9074 return; // no logos in XBoard yet