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, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
245 int main P((int argc, char **argv));
246 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
247 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
248 RETSIGTYPE CmailSigHandler P((int sig));
249 RETSIGTYPE IntSigHandler P((int sig));
250 RETSIGTYPE TermSizeSigHandler P((int sig));
251 void CreateGCs P((int redo));
252 void CreateAnyPieces P((void));
253 void CreateXIMPieces P((void));
254 void CreateXPMPieces P((void));
255 void CreateXPMBoard P((char *s, int n));
256 void CreatePieces P((void));
257 void CreatePieceMenus P((void));
258 Widget CreateMenuBar P((Menu *mb, int boardWidth));
259 Widget CreateButtonBar P ((MenuItem *mi));
261 char *InsertPxlSize P((char *pattern, int targetPxlSize));
262 XFontSet CreateFontSet P((char *base_fnt_lst));
264 char *FindFont P((char *pattern, int targetPxlSize));
266 void PieceMenuPopup P((Widget w, XEvent *event,
267 String *params, Cardinal *num_params));
268 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
269 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
270 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
271 u_int wreq, u_int hreq));
272 void CreateGrid P((void));
273 int EventToSquare P((int x, int limit));
274 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
275 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
276 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
277 void HandleUserMove P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AnimateUserMove P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void HandlePV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void SelectPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void StopPV P((Widget w, XEvent * event,
286 String * params, Cardinal * nParams));
287 void WhiteClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void BlackClock P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void DrawPositionProc P((Widget w, XEvent *event,
292 String *prms, Cardinal *nprms));
293 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
295 void CommentClick P((Widget w, XEvent * event,
296 String * params, Cardinal * nParams));
297 void CommentPopUp P((char *title, char *label));
298 void CommentPopDown P((void));
299 void ICSInputBoxPopUp P((void));
300 void ICSInputBoxPopDown P((void));
301 void FileNamePopUp P((char *label, char *def, char *filter,
302 FileProc proc, char *openMode));
303 void FileNamePopDown P((void));
304 void FileNameCallback P((Widget w, XtPointer client_data,
305 XtPointer call_data));
306 void FileNameAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionReplyAction P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionProc P((Widget w, XEvent *event,
311 String *prms, Cardinal *nprms));
312 void AskQuestionPopDown P((void));
313 void PromotionPopDown P((void));
314 void PromotionCallback P((Widget w, XtPointer client_data,
315 XtPointer call_data));
316 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
317 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
318 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
321 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
323 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
325 void LoadPositionProc P((Widget w, XEvent *event,
326 String *prms, Cardinal *nprms));
327 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
329 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
331 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
333 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
335 void PastePositionProc P((Widget w, XEvent *event, String *prms,
337 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
340 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
341 void SavePositionProc P((Widget w, XEvent *event,
342 String *prms, Cardinal *nprms));
343 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
344 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
346 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
347 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
350 void MachineWhiteProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeModeProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void AnalyzeFileProc P((Widget w, XEvent *event,
355 String *prms, Cardinal *nprms));
356 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
358 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void IcsClientProc P((Widget w, XEvent *event, String *prms,
362 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void EditPositionProc P((Widget w, XEvent *event,
364 String *prms, Cardinal *nprms));
365 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void EditCommentProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void IcsInputBoxProc P((Widget w, XEvent *event,
369 String *prms, Cardinal *nprms));
370 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void StopObservingProc P((Widget w, XEvent *event, String *prms,
387 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
389 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 Boolean TempBackwardActive = False;
395 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
396 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
401 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
403 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
404 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
406 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
408 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
410 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
412 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
413 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
415 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
416 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
418 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
420 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
422 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
424 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
425 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
427 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
429 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
431 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
433 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
436 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
438 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
440 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
442 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void DisplayMove P((int moveNumber));
453 void DisplayTitle P((char *title));
454 void ICSInitScript P((void));
455 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
456 void ErrorPopUp P((char *title, char *text, int modal));
457 void ErrorPopDown P((void));
458 static char *ExpandPathName P((char *path));
459 static void CreateAnimVars P((void));
460 static void DragPieceMove P((int x, int y));
461 static void DrawDragPiece P((void));
462 char *ModeToWidgetName P((GameMode mode));
463 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
477 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
478 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
479 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
480 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
481 void GameListOptionsPopDown P(());
482 void GenericPopDown P(());
483 void update_ics_width P(());
484 int get_term_width P(());
485 int CopyMemoProc P(());
486 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
487 Boolean IsDrawArrowEnabled P(());
490 * XBoard depends on Xt R4 or higher
492 int xtVersion = XtSpecificationRelease;
497 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
498 jailSquareColor, highlightSquareColor, premoveHighlightColor;
499 Pixel lowTimeWarningColor;
500 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
501 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
502 wjPieceGC, bjPieceGC, prelineGC, countGC;
503 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
504 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
505 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
506 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
507 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
508 ICSInputShell, fileNameShell, askQuestionShell;
509 Widget historyShell, evalGraphShell, gameListShell;
510 int hOffset; // [HGM] dual
511 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
512 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
513 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
515 XFontSet fontSet, clockFontSet;
518 XFontStruct *clockFontStruct;
520 Font coordFontID, countFontID;
521 XFontStruct *coordFontStruct, *countFontStruct;
522 XtAppContext appContext;
524 char *oldICSInteractionTitle;
528 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
530 Position commentX = -1, commentY = -1;
531 Dimension commentW, commentH;
532 typedef unsigned int BoardSize;
534 Boolean chessProgram;
536 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
537 int squareSize, smallLayout = 0, tinyLayout = 0,
538 marginW, marginH, // [HGM] for run-time resizing
539 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
540 ICSInputBoxUp = False, askQuestionUp = False,
541 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
542 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
543 Pixel timerForegroundPixel, timerBackgroundPixel;
544 Pixel buttonForegroundPixel, buttonBackgroundPixel;
545 char *chessDir, *programName, *programVersion,
546 *gameCopyFilename, *gamePasteFilename;
547 Boolean alwaysOnTop = False;
548 Boolean saveSettingsOnExit;
549 char *settingsFileName;
550 char *icsTextMenuString;
552 char *firstChessProgramNames;
553 char *secondChessProgramNames;
555 WindowPlacement wpMain;
556 WindowPlacement wpConsole;
557 WindowPlacement wpComment;
558 WindowPlacement wpMoveHistory;
559 WindowPlacement wpEvalGraph;
560 WindowPlacement wpEngineOutput;
561 WindowPlacement wpGameList;
562 WindowPlacement wpTags;
564 extern Widget shells[];
565 extern Boolean shellUp[];
569 Pixmap pieceBitmap[2][(int)BlackPawn];
570 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
571 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
572 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
573 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
574 Pixmap xpmBoardBitmap[2];
575 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
576 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
577 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
578 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
579 XImage *ximLightSquare, *ximDarkSquare;
582 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
583 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
585 #define White(piece) ((int)(piece) < (int)BlackPawn)
587 /* Variables for doing smooth animation. This whole thing
588 would be much easier if the board was double-buffered,
589 but that would require a fairly major rewrite. */
594 GC blitGC, pieceGC, outlineGC;
595 XPoint startSquare, prevFrame, mouseDelta;
599 int startBoardX, startBoardY;
602 /* There can be two pieces being animated at once: a player
603 can begin dragging a piece before the remote opponent has moved. */
605 static AnimState game, player;
607 /* Bitmaps for use as masks when drawing XPM pieces.
608 Need one for each black and white piece. */
609 static Pixmap xpmMask[BlackKing + 1];
611 /* This magic number is the number of intermediate frames used
612 in each half of the animation. For short moves it's reduced
613 by 1. The total number of frames will be factor * 2 + 1. */
616 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
618 MenuItem fileMenu[] = {
619 {N_("New Game Ctrl+N"), "New Game", ResetProc},
620 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
621 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
622 {"----", NULL, NothingProc},
623 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
624 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
625 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
626 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
627 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
628 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
629 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
630 {"----", NULL, NothingProc},
631 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
632 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
633 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
634 {"----", NULL, NothingProc},
635 {N_("Mail Move"), "Mail Move", MailMoveProc},
636 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
637 {"----", NULL, NothingProc},
638 {N_("Quit Ctr+Q"), "Exit", QuitProc},
642 MenuItem editMenu[] = {
643 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
644 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
645 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
646 {"----", NULL, NothingProc},
647 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
648 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
649 {"----", NULL, NothingProc},
650 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
651 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
652 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
653 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
654 {N_("Edit Book"), "Edit Book", EditBookProc},
655 {"----", NULL, NothingProc},
656 {N_("Revert Home"), "Revert", RevertProc},
657 {N_("Annotate"), "Annotate", AnnotateProc},
658 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
659 {"----", NULL, NothingProc},
660 {N_("Backward Alt+Left"), "Backward", BackwardProc},
661 {N_("Forward Alt+Right"), "Forward", ForwardProc},
662 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
663 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
667 MenuItem viewMenu[] = {
668 {N_("Flip View F2"), "Flip View", FlipViewProc},
669 {"----", NULL, NothingProc},
670 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
671 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
672 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
673 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
674 {N_("ICS text menu"), "ICStex", IcsTextProc},
675 {"----", NULL, NothingProc},
676 {N_("Tags"), "Show Tags", EditTagsProc},
677 {N_("Comments"), "Show Comments", EditCommentProc},
678 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
679 {"----", NULL, NothingProc},
680 {N_("Board..."), "Board Options", BoardOptionsProc},
681 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
685 MenuItem modeMenu[] = {
686 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
687 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
688 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
689 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
690 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
691 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
692 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
693 {N_("Training"), "Training", TrainingProc},
694 {N_("ICS Client"), "ICS Client", IcsClientProc},
695 {"----", NULL, NothingProc},
696 {N_("Machine Match"), "Machine Match", MatchProc},
697 {N_("Pause Pause"), "Pause", PauseProc},
701 MenuItem actionMenu[] = {
702 {N_("Accept F3"), "Accept", AcceptProc},
703 {N_("Decline F4"), "Decline", DeclineProc},
704 {N_("Rematch F12"), "Rematch", RematchProc},
705 {"----", NULL, NothingProc},
706 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
707 {N_("Draw F6"), "Draw", DrawProc},
708 {N_("Adjourn F7"), "Adjourn", AdjournProc},
709 {N_("Abort F8"),"Abort", AbortProc},
710 {N_("Resign F9"), "Resign", ResignProc},
711 {"----", NULL, NothingProc},
712 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
713 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
714 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
715 {"----", NULL, NothingProc},
716 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
717 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
718 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
722 MenuItem engineMenu[] = {
723 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
724 {"----", NULL, NothingProc},
725 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
726 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
727 {"----", NULL, NothingProc},
728 {N_("Hint"), "Hint", HintProc},
729 {N_("Book"), "Book", BookProc},
730 {"----", NULL, NothingProc},
731 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
732 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
736 MenuItem optionsMenu[] = {
737 #define OPTIONSDIALOG
739 {N_("General ..."), "General", OptionsProc},
741 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
742 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
743 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
744 {N_("ICS ..."), "ICS", IcsOptionsProc},
745 {N_("Match ..."), "Match", MatchOptionsProc},
746 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
747 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
748 // {N_(" ..."), "", OptionsProc},
749 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
750 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
751 {"----", NULL, NothingProc},
752 #ifndef OPTIONSDIALOG
753 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
754 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
755 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
756 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
757 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
758 {N_("Blindfold"), "Blindfold", BlindfoldProc},
759 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
761 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
763 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
764 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
765 {N_("Move Sound"), "Move Sound", MoveSoundProc},
766 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
767 {N_("One-Click Moving"), "OneClick", OneClickProc},
768 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
769 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
770 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
771 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
772 // {N_("Premove"), "Premove", PremoveProc},
773 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
774 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
775 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
776 {"----", NULL, NothingProc},
778 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
779 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
783 MenuItem helpMenu[] = {
784 {N_("Info XBoard"), "Info XBoard", InfoProc},
785 {N_("Man XBoard F1"), "Man XBoard", ManProc},
786 {"----", NULL, NothingProc},
787 {N_("About XBoard"), "About XBoard", AboutProc},
792 {N_("File"), "File", fileMenu},
793 {N_("Edit"), "Edit", editMenu},
794 {N_("View"), "View", viewMenu},
795 {N_("Mode"), "Mode", modeMenu},
796 {N_("Action"), "Action", actionMenu},
797 {N_("Engine"), "Engine", engineMenu},
798 {N_("Options"), "Options", optionsMenu},
799 {N_("Help"), "Help", helpMenu},
803 #define PAUSE_BUTTON "P"
804 MenuItem buttonBar[] = {
805 {"<<", "<<", ToStartProc},
806 {"<", "<", BackwardProc},
807 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
808 {">", ">", ForwardProc},
809 {">>", ">>", ToEndProc},
813 #define PIECE_MENU_SIZE 18
814 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
815 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
816 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
817 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
818 N_("Empty square"), N_("Clear board") },
819 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
820 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
821 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
822 N_("Empty square"), N_("Clear board") }
824 /* must be in same order as pieceMenuStrings! */
825 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
826 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
827 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
828 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
829 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
830 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
831 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
832 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
833 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
836 #define DROP_MENU_SIZE 6
837 String dropMenuStrings[DROP_MENU_SIZE] = {
838 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
840 /* must be in same order as dropMenuStrings! */
841 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
842 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
843 WhiteRook, WhiteQueen
851 DropMenuEnables dmEnables[] = {
869 { XtNborderWidth, 0 },
870 { XtNdefaultDistance, 0 },
874 { XtNborderWidth, 0 },
875 { XtNresizable, (XtArgVal) True },
879 { XtNborderWidth, 0 },
885 { XtNjustify, (XtArgVal) XtJustifyRight },
886 { XtNlabel, (XtArgVal) "..." },
887 { XtNresizable, (XtArgVal) True },
888 { XtNresize, (XtArgVal) False }
891 Arg messageArgs[] = {
892 { XtNjustify, (XtArgVal) XtJustifyLeft },
893 { XtNlabel, (XtArgVal) "..." },
894 { XtNresizable, (XtArgVal) True },
895 { XtNresize, (XtArgVal) False }
899 { XtNborderWidth, 0 },
900 { XtNjustify, (XtArgVal) XtJustifyLeft }
903 XtResource clientResources[] = {
904 { "flashCount", "flashCount", XtRInt, sizeof(int),
905 XtOffset(AppDataPtr, flashCount), XtRImmediate,
906 (XtPointer) FLASH_COUNT },
909 XrmOptionDescRec shellOptions[] = {
910 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
911 { "-flash", "flashCount", XrmoptionNoArg, "3" },
912 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
915 XtActionsRec boardActions[] = {
916 { "DrawPosition", DrawPositionProc },
917 { "HandleUserMove", HandleUserMove },
918 { "AnimateUserMove", AnimateUserMove },
919 { "HandlePV", HandlePV },
920 { "SelectPV", SelectPV },
921 { "StopPV", StopPV },
922 { "FileNameAction", FileNameAction },
923 { "AskQuestionProc", AskQuestionProc },
924 { "AskQuestionReplyAction", AskQuestionReplyAction },
925 { "PieceMenuPopup", PieceMenuPopup },
926 { "WhiteClock", WhiteClock },
927 { "BlackClock", BlackClock },
928 { "ResetProc", ResetProc },
929 { "NewVariantProc", NewVariantProc },
930 { "LoadGameProc", LoadGameProc },
931 { "LoadNextGameProc", LoadNextGameProc },
932 { "LoadPrevGameProc", LoadPrevGameProc },
933 { "LoadSelectedProc", LoadSelectedProc },
934 { "SetFilterProc", SetFilterProc },
935 { "ReloadGameProc", ReloadGameProc },
936 { "LoadPositionProc", LoadPositionProc },
937 { "LoadNextPositionProc", LoadNextPositionProc },
938 { "LoadPrevPositionProc", LoadPrevPositionProc },
939 { "ReloadPositionProc", ReloadPositionProc },
940 { "CopyPositionProc", CopyPositionProc },
941 { "PastePositionProc", PastePositionProc },
942 { "CopyGameProc", CopyGameProc },
943 { "CopyGameListProc", CopyGameListProc },
944 { "PasteGameProc", PasteGameProc },
945 { "SaveGameProc", SaveGameProc },
946 { "SavePositionProc", SavePositionProc },
947 { "MailMoveProc", MailMoveProc },
948 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
949 { "QuitProc", QuitProc },
950 { "MachineWhiteProc", MachineWhiteProc },
951 { "MachineBlackProc", MachineBlackProc },
952 { "AnalysisModeProc", AnalyzeModeProc },
953 { "AnalyzeFileProc", AnalyzeFileProc },
954 { "TwoMachinesProc", TwoMachinesProc },
955 { "IcsClientProc", IcsClientProc },
956 { "EditGameProc", EditGameProc },
957 { "EditPositionProc", EditPositionProc },
958 { "TrainingProc", EditPositionProc },
959 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
960 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
961 { "ShowGameListProc", ShowGameListProc },
962 { "ShowMoveListProc", HistoryShowProc},
963 { "EditTagsProc", EditCommentProc },
964 { "EditBookProc", EditBookProc },
965 { "EditCommentProc", EditCommentProc },
966 { "IcsInputBoxProc", IcsInputBoxProc },
967 { "PauseProc", PauseProc },
968 { "AcceptProc", AcceptProc },
969 { "DeclineProc", DeclineProc },
970 { "RematchProc", RematchProc },
971 { "CallFlagProc", CallFlagProc },
972 { "DrawProc", DrawProc },
973 { "AdjournProc", AdjournProc },
974 { "AbortProc", AbortProc },
975 { "ResignProc", ResignProc },
976 { "AdjuWhiteProc", AdjuWhiteProc },
977 { "AdjuBlackProc", AdjuBlackProc },
978 { "AdjuDrawProc", AdjuDrawProc },
979 { "TypeInProc", TypeInProc },
980 { "EnterKeyProc", EnterKeyProc },
981 { "UpKeyProc", UpKeyProc },
982 { "DownKeyProc", DownKeyProc },
983 { "StopObservingProc", StopObservingProc },
984 { "StopExaminingProc", StopExaminingProc },
985 { "UploadProc", UploadProc },
986 { "BackwardProc", BackwardProc },
987 { "ForwardProc", ForwardProc },
988 { "TempBackwardProc", TempBackwardProc },
989 { "TempForwardProc", TempForwardProc },
990 { "ToStartProc", ToStartProc },
991 { "ToEndProc", ToEndProc },
992 { "RevertProc", RevertProc },
993 { "AnnotateProc", AnnotateProc },
994 { "TruncateGameProc", TruncateGameProc },
995 { "MoveNowProc", MoveNowProc },
996 { "RetractMoveProc", RetractMoveProc },
997 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
998 { "UciMenuProc", (XtActionProc) UciMenuProc },
999 { "TimeControlProc", (XtActionProc) TimeControlProc },
1000 { "FlipViewProc", FlipViewProc },
1001 { "PonderNextMoveProc", PonderNextMoveProc },
1002 #ifndef OPTIONSDIALOG
1003 { "AlwaysQueenProc", AlwaysQueenProc },
1004 { "AnimateDraggingProc", AnimateDraggingProc },
1005 { "AnimateMovingProc", AnimateMovingProc },
1006 { "AutoflagProc", AutoflagProc },
1007 { "AutoflipProc", AutoflipProc },
1008 { "BlindfoldProc", BlindfoldProc },
1009 { "FlashMovesProc", FlashMovesProc },
1011 { "HighlightDraggingProc", HighlightDraggingProc },
1013 { "HighlightLastMoveProc", HighlightLastMoveProc },
1014 // { "IcsAlarmProc", IcsAlarmProc },
1015 { "MoveSoundProc", MoveSoundProc },
1016 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1017 { "PopupExitMessageProc", PopupExitMessageProc },
1018 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1019 // { "PremoveProc", PremoveProc },
1020 { "ShowCoordsProc", ShowCoordsProc },
1021 { "ShowThinkingProc", ShowThinkingProc },
1022 { "HideThinkingProc", HideThinkingProc },
1023 { "TestLegalityProc", TestLegalityProc },
1025 { "SaveSettingsProc", SaveSettingsProc },
1026 { "SaveOnExitProc", SaveOnExitProc },
1027 { "InfoProc", InfoProc },
1028 { "ManProc", ManProc },
1029 { "HintProc", HintProc },
1030 { "BookProc", BookProc },
1031 { "AboutGameProc", AboutGameProc },
1032 { "AboutProc", AboutProc },
1033 { "DebugProc", DebugProc },
1034 { "NothingProc", NothingProc },
1035 { "CommentClick", (XtActionProc) CommentClick },
1036 { "CommentPopDown", (XtActionProc) CommentPopDown },
1037 { "TagsPopDown", (XtActionProc) TagsPopDown },
1038 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1039 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1040 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1041 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1042 { "GameListPopDown", (XtActionProc) GameListPopDown },
1043 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1044 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1045 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1046 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1047 { "GenericPopDown", (XtActionProc) GenericPopDown },
1048 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1049 { "SelectMove", (XtActionProc) SelectMove },
1052 char globalTranslations[] =
1053 ":<Key>F9: ResignProc() \n \
1054 :Ctrl<Key>n: ResetProc() \n \
1055 :Meta<Key>V: NewVariantProc() \n \
1056 :Ctrl<Key>o: LoadGameProc() \n \
1057 :Meta<Key>Next: LoadNextGameProc() \n \
1058 :Meta<Key>Prior: LoadPrevGameProc() \n \
1059 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1060 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1061 :Ctrl<Key>s: SaveGameProc() \n \
1062 :Ctrl<Key>c: CopyGameProc() \n \
1063 :Ctrl<Key>v: PasteGameProc() \n \
1064 :Ctrl<Key>O: LoadPositionProc() \n \
1065 :Shift<Key>Next: LoadNextPositionProc() \n \
1066 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1067 :Ctrl<Key>S: SavePositionProc() \n \
1068 :Ctrl<Key>C: CopyPositionProc() \n \
1069 :Ctrl<Key>V: PastePositionProc() \n \
1070 :Ctrl<Key>q: QuitProc() \n \
1071 :Ctrl<Key>w: MachineWhiteProc() \n \
1072 :Ctrl<Key>b: MachineBlackProc() \n \
1073 :Ctrl<Key>t: TwoMachinesProc() \n \
1074 :Ctrl<Key>a: AnalysisModeProc() \n \
1075 :Ctrl<Key>g: AnalyzeFileProc() \n \
1076 :Ctrl<Key>e: EditGameProc() \n \
1077 :Ctrl<Key>E: EditPositionProc() \n \
1078 :Meta<Key>O: EngineOutputProc() \n \
1079 :Meta<Key>E: EvalGraphProc() \n \
1080 :Meta<Key>G: ShowGameListProc() \n \
1081 :Meta<Key>H: ShowMoveListProc() \n \
1082 :<Key>Pause: PauseProc() \n \
1083 :<Key>F3: AcceptProc() \n \
1084 :<Key>F4: DeclineProc() \n \
1085 :<Key>F12: RematchProc() \n \
1086 :<Key>F5: CallFlagProc() \n \
1087 :<Key>F6: DrawProc() \n \
1088 :<Key>F7: AdjournProc() \n \
1089 :<Key>F8: AbortProc() \n \
1090 :<Key>F10: StopObservingProc() \n \
1091 :<Key>F11: StopExaminingProc() \n \
1092 :Meta Ctrl<Key>F12: DebugProc() \n \
1093 :Meta<Key>End: ToEndProc() \n \
1094 :Meta<Key>Right: ForwardProc() \n \
1095 :Meta<Key>Home: ToStartProc() \n \
1096 :Meta<Key>Left: BackwardProc() \n \
1097 :<Key>Left: BackwardProc() \n \
1098 :<Key>Right: ForwardProc() \n \
1099 :<Key>Home: RevertProc() \n \
1100 :<Key>End: TruncateGameProc() \n \
1101 :Ctrl<Key>m: MoveNowProc() \n \
1102 :Ctrl<Key>x: RetractMoveProc() \n \
1103 :Meta<Key>J: EngineMenuProc() \n \
1104 :Meta<Key>U: UciMenuProc() \n \
1105 :Meta<Key>T: TimeControlProc() \n \
1106 :Ctrl<Key>P: PonderNextMoveProc() \n "
1107 #ifndef OPTIONSDIALOG
1109 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1110 :Ctrl<Key>F: AutoflagProc() \n \
1111 :Ctrl<Key>A: AnimateMovingProc() \n \
1112 :Ctrl<Key>L: TestLegalityProc() \n \
1113 :Ctrl<Key>H: HideThinkingProc() \n "
1116 :<Key>F1: ManProc() \n \
1117 :<Key>F2: FlipViewProc() \n \
1118 :<KeyDown>Return: TempBackwardProc() \n \
1119 :<KeyUp>Return: TempForwardProc() \n";
1121 char boardTranslations[] =
1122 "<Btn1Down>: HandleUserMove(0) \n \
1123 Shift<Btn1Up>: HandleUserMove(1) \n \
1124 <Btn1Up>: HandleUserMove(0) \n \
1125 <Btn1Motion>: AnimateUserMove() \n \
1126 <Btn3Motion>: HandlePV() \n \
1127 <Btn3Up>: PieceMenuPopup(menuB) \n \
1128 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1129 PieceMenuPopup(menuB) \n \
1130 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1131 PieceMenuPopup(menuW) \n \
1132 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1133 PieceMenuPopup(menuW) \n \
1134 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1135 PieceMenuPopup(menuB) \n";
1137 char whiteTranslations[] =
1138 "Shift<BtnDown>: WhiteClock(1)\n \
1139 <BtnDown>: WhiteClock(0)\n";
1140 char blackTranslations[] =
1141 "Shift<BtnDown>: BlackClock(1)\n \
1142 <BtnDown>: BlackClock(0)\n";
1144 char ICSInputTranslations[] =
1145 "<Key>Up: UpKeyProc() \n "
1146 "<Key>Down: DownKeyProc() \n "
1147 "<Key>Return: EnterKeyProc() \n";
1149 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1150 // as the widget is destroyed before the up-click can call extend-end
1151 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1153 String xboardResources[] = {
1154 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1155 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1156 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1161 /* Max possible square size */
1162 #define MAXSQSIZE 256
1164 static int xpm_avail[MAXSQSIZE];
1166 #ifdef HAVE_DIR_STRUCT
1168 /* Extract piece size from filename */
1170 xpm_getsize (char *name, int len, char *ext)
1178 if ((p=strchr(name, '.')) == NULL ||
1179 StrCaseCmp(p+1, ext) != 0)
1185 while (*p && isdigit(*p))
1192 /* Setup xpm_avail */
1194 xpm_getavail (char *dirname, char *ext)
1200 for (i=0; i<MAXSQSIZE; ++i)
1203 if (appData.debugMode)
1204 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1206 dir = opendir(dirname);
1209 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1210 programName, dirname);
1214 while ((ent=readdir(dir)) != NULL) {
1215 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1216 if (i > 0 && i < MAXSQSIZE)
1226 xpm_print_avail (FILE *fp, char *ext)
1230 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1231 for (i=1; i<MAXSQSIZE; ++i) {
1237 /* Return XPM piecesize closest to size */
1239 xpm_closest_to (char *dirname, int size, char *ext)
1242 int sm_diff = MAXSQSIZE;
1246 xpm_getavail(dirname, ext);
1248 if (appData.debugMode)
1249 xpm_print_avail(stderr, ext);
1251 for (i=1; i<MAXSQSIZE; ++i) {
1254 diff = (diff<0) ? -diff : diff;
1255 if (diff < sm_diff) {
1263 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1269 #else /* !HAVE_DIR_STRUCT */
1270 /* If we are on a system without a DIR struct, we can't
1271 read the directory, so we can't collect a list of
1272 filenames, etc., so we can't do any size-fitting. */
1274 xpm_closest_to (char *dirname, int size, char *ext)
1276 fprintf(stderr, _("\
1277 Warning: No DIR structure found on this system --\n\
1278 Unable to autosize for XPM/XIM pieces.\n\
1279 Please report this error to %s.\n\
1280 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1283 #endif /* HAVE_DIR_STRUCT */
1285 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1286 "magenta", "cyan", "white" };
1290 TextColors textColors[(int)NColorClasses];
1292 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1294 parse_color (char *str, int which)
1296 char *p, buf[100], *d;
1299 if (strlen(str) > 99) /* watch bounds on buf */
1304 for (i=0; i<which; ++i) {
1311 /* Could be looking at something like:
1313 .. in which case we want to stop on a comma also */
1314 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1318 return -1; /* Use default for empty field */
1321 if (which == 2 || isdigit(*p))
1324 while (*p && isalpha(*p))
1329 for (i=0; i<8; ++i) {
1330 if (!StrCaseCmp(buf, cnames[i]))
1331 return which? (i+40) : (i+30);
1333 if (!StrCaseCmp(buf, "default")) return -1;
1335 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1340 parse_cpair (ColorClass cc, char *str)
1342 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1343 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1348 /* bg and attr are optional */
1349 textColors[(int)cc].bg = parse_color(str, 1);
1350 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1351 textColors[(int)cc].attr = 0;
1357 /* Arrange to catch delete-window events */
1358 Atom wm_delete_window;
1360 CatchDeleteWindow (Widget w, String procname)
1363 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1364 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1365 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1372 XtSetArg(args[0], XtNiconic, False);
1373 XtSetValues(shellWidget, args, 1);
1375 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1378 //---------------------------------------------------------------------------------------------------------
1379 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1382 #define CW_USEDEFAULT (1<<31)
1383 #define ICS_TEXT_MENU_SIZE 90
1384 #define DEBUG_FILE "xboard.debug"
1385 #define SetCurrentDirectory chdir
1386 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1390 // these two must some day move to frontend.h, when they are implemented
1391 Boolean GameListIsUp();
1393 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1396 // front-end part of option handling
1398 // [HGM] This platform-dependent table provides the location for storing the color info
1399 extern char *crWhite, * crBlack;
1403 &appData.whitePieceColor,
1404 &appData.blackPieceColor,
1405 &appData.lightSquareColor,
1406 &appData.darkSquareColor,
1407 &appData.highlightSquareColor,
1408 &appData.premoveHighlightColor,
1409 &appData.lowTimeWarningColor,
1420 // [HGM] font: keep a font for each square size, even non-stndard ones
1421 #define NUM_SIZES 18
1422 #define MAX_SIZE 130
1423 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1424 char *fontTable[NUM_FONTS][MAX_SIZE];
1427 ParseFont (char *name, int number)
1428 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1430 if(sscanf(name, "size%d:", &size)) {
1431 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1432 // defer processing it until we know if it matches our board size
1433 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1434 fontTable[number][size] = strdup(strchr(name, ':')+1);
1435 fontValid[number][size] = True;
1440 case 0: // CLOCK_FONT
1441 appData.clockFont = strdup(name);
1443 case 1: // MESSAGE_FONT
1444 appData.font = strdup(name);
1446 case 2: // COORD_FONT
1447 appData.coordFont = strdup(name);
1452 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1457 { // only 2 fonts currently
1458 appData.clockFont = CLOCK_FONT_NAME;
1459 appData.coordFont = COORD_FONT_NAME;
1460 appData.font = DEFAULT_FONT_NAME;
1465 { // no-op, until we identify the code for this already in XBoard and move it here
1469 ParseColor (int n, char *name)
1470 { // in XBoard, just copy the color-name string
1471 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1475 ParseTextAttribs (ColorClass cc, char *s)
1477 (&appData.colorShout)[cc] = strdup(s);
1481 ParseBoardSize (void *addr, char *name)
1483 appData.boardSize = strdup(name);
1488 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1492 SetCommPortDefaults ()
1493 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1496 // [HGM] args: these three cases taken out to stay in front-end
1498 SaveFontArg (FILE *f, ArgDescriptor *ad)
1501 int i, n = (int)(intptr_t)ad->argLoc;
1503 case 0: // CLOCK_FONT
1504 name = appData.clockFont;
1506 case 1: // MESSAGE_FONT
1507 name = appData.font;
1509 case 2: // COORD_FONT
1510 name = appData.coordFont;
1515 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1516 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1517 fontTable[n][squareSize] = strdup(name);
1518 fontValid[n][squareSize] = True;
1521 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1522 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1527 { // nothing to do, as the sounds are at all times represented by their text-string names already
1531 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1532 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1533 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1537 SaveColor (FILE *f, ArgDescriptor *ad)
1538 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1539 if(colorVariable[(int)(intptr_t)ad->argLoc])
1540 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1544 SaveBoardSize (FILE *f, char *name, void *addr)
1545 { // wrapper to shield back-end from BoardSize & sizeInfo
1546 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1550 ParseCommPortSettings (char *s)
1551 { // no such option in XBoard (yet)
1554 extern Widget engineOutputShell;
1557 GetActualPlacement (Widget wg, WindowPlacement *wp)
1567 XtSetArg(args[i], XtNx, &x); i++;
1568 XtSetArg(args[i], XtNy, &y); i++;
1569 XtSetArg(args[i], XtNwidth, &w); i++;
1570 XtSetArg(args[i], XtNheight, &h); i++;
1571 XtGetValues(wg, args, i);
1580 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1581 // In XBoard this will have to wait until awareness of window parameters is implemented
1582 GetActualPlacement(shellWidget, &wpMain);
1583 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1584 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1585 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1586 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1587 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1588 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1592 PrintCommPortSettings (FILE *f, char *name)
1593 { // This option does not exist in XBoard
1597 MySearchPath (char *installDir, char *name, char *fullname)
1598 { // just append installDir and name. Perhaps ExpandPath should be used here?
1599 name = ExpandPathName(name);
1600 if(name && name[0] == '/')
1601 safeStrCpy(fullname, name, MSG_SIZ );
1603 sprintf(fullname, "%s%c%s", installDir, '/', name);
1609 MyGetFullPathName (char *name, char *fullname)
1610 { // should use ExpandPath?
1611 name = ExpandPathName(name);
1612 safeStrCpy(fullname, name, MSG_SIZ );
1617 EnsureOnScreen (int *x, int *y, int minX, int minY)
1624 { // [HGM] args: allows testing if main window is realized from back-end
1625 return xBoardWindow != 0;
1629 PopUpStartupDialog ()
1630 { // start menu not implemented in XBoard
1634 ConvertToLine (int argc, char **argv)
1636 static char line[128*1024], buf[1024];
1640 for(i=1; i<argc; i++)
1642 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1643 && argv[i][0] != '{' )
1644 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1646 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1647 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1650 line[strlen(line)-1] = NULLCHAR;
1654 //--------------------------------------------------------------------------------------------
1656 extern Boolean twoBoards, partnerUp;
1659 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1661 #define BoardSize int
1663 InitDrawingSizes (BoardSize boardSize, int flags)
1664 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1665 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1667 XtGeometryResult gres;
1669 static Dimension oldWidth, oldHeight;
1670 static VariantClass oldVariant;
1671 static int oldDual = -1, oldMono = -1;
1673 if(!formWidget) return;
1675 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1676 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1677 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1679 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1681 * Enable shell resizing.
1683 shellArgs[0].value = (XtArgVal) &w;
1684 shellArgs[1].value = (XtArgVal) &h;
1685 XtGetValues(shellWidget, shellArgs, 2);
1687 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1688 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1689 XtSetValues(shellWidget, &shellArgs[2], 4);
1691 XtSetArg(args[0], XtNdefaultDistance, &sep);
1692 XtGetValues(formWidget, args, 1);
1694 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1696 hOffset = boardWidth + 10;
1697 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1698 secondSegments[i] = gridSegments[i];
1699 secondSegments[i].x1 += hOffset;
1700 secondSegments[i].x2 += hOffset;
1703 XtSetArg(args[0], XtNwidth, boardWidth);
1704 XtSetArg(args[1], XtNheight, boardHeight);
1705 XtSetValues(boardWidget, args, 2);
1707 timerWidth = (boardWidth - sep) / 2;
1708 XtSetArg(args[0], XtNwidth, timerWidth);
1709 XtSetValues(whiteTimerWidget, args, 1);
1710 XtSetValues(blackTimerWidget, args, 1);
1712 XawFormDoLayout(formWidget, False);
1714 if (appData.titleInWindow) {
1716 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1717 XtSetArg(args[i], XtNheight, &h); i++;
1718 XtGetValues(titleWidget, args, i);
1720 w = boardWidth - 2*bor;
1722 XtSetArg(args[0], XtNwidth, &w);
1723 XtGetValues(menuBarWidget, args, 1);
1724 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1727 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1728 if (gres != XtGeometryYes && appData.debugMode) {
1730 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1731 programName, gres, w, h, wr, hr);
1735 XawFormDoLayout(formWidget, True);
1738 * Inhibit shell resizing.
1740 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1741 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1742 shellArgs[4].value = shellArgs[2].value = w;
1743 shellArgs[5].value = shellArgs[3].value = h;
1744 XtSetValues(shellWidget, &shellArgs[0], 6);
1747 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1750 if(gameInfo.variant != oldVariant) { // and only if variant changed
1753 for(i=0; i<4; i++) {
1755 for(p=0; p<=(int)WhiteKing; p++)
1756 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1757 if(gameInfo.variant == VariantShogi) {
1758 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1759 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1760 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1761 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1762 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1765 if(gameInfo.variant == VariantGothic) {
1766 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1769 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1770 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1771 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1774 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1775 for(p=0; p<=(int)WhiteKing; p++)
1776 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1777 if(gameInfo.variant == VariantShogi) {
1778 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1779 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1780 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1781 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1782 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1785 if(gameInfo.variant == VariantGothic) {
1786 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1789 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1790 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1791 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1796 for(i=0; i<2; i++) {
1798 for(p=0; p<=(int)WhiteKing; p++)
1799 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1800 if(gameInfo.variant == VariantShogi) {
1801 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1802 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1803 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1804 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1805 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1808 if(gameInfo.variant == VariantGothic) {
1809 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1812 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1813 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1814 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1820 if(appData.monoMode == oldMono)
1823 oldMono = appData.monoMode;
1828 ParseIcsTextColors ()
1829 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1830 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1831 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1832 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1833 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1834 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1835 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1836 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1837 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1838 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1839 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1841 if (appData.colorize) {
1843 _("%s: can't parse color names; disabling colorization\n"),
1846 appData.colorize = FALSE;
1852 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1853 XrmValue vFrom, vTo;
1854 int forceMono = False;
1856 if (!appData.monoMode) {
1857 vFrom.addr = (caddr_t) appData.lightSquareColor;
1858 vFrom.size = strlen(appData.lightSquareColor);
1859 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1860 if (vTo.addr == NULL) {
1861 appData.monoMode = True;
1864 lightSquareColor = *(Pixel *) vTo.addr;
1867 if (!appData.monoMode) {
1868 vFrom.addr = (caddr_t) appData.darkSquareColor;
1869 vFrom.size = strlen(appData.darkSquareColor);
1870 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1871 if (vTo.addr == NULL) {
1872 appData.monoMode = True;
1875 darkSquareColor = *(Pixel *) vTo.addr;
1878 if (!appData.monoMode) {
1879 vFrom.addr = (caddr_t) appData.whitePieceColor;
1880 vFrom.size = strlen(appData.whitePieceColor);
1881 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1882 if (vTo.addr == NULL) {
1883 appData.monoMode = True;
1886 whitePieceColor = *(Pixel *) vTo.addr;
1889 if (!appData.monoMode) {
1890 vFrom.addr = (caddr_t) appData.blackPieceColor;
1891 vFrom.size = strlen(appData.blackPieceColor);
1892 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1893 if (vTo.addr == NULL) {
1894 appData.monoMode = True;
1897 blackPieceColor = *(Pixel *) vTo.addr;
1901 if (!appData.monoMode) {
1902 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1903 vFrom.size = strlen(appData.highlightSquareColor);
1904 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1905 if (vTo.addr == NULL) {
1906 appData.monoMode = True;
1909 highlightSquareColor = *(Pixel *) vTo.addr;
1913 if (!appData.monoMode) {
1914 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1915 vFrom.size = strlen(appData.premoveHighlightColor);
1916 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1917 if (vTo.addr == NULL) {
1918 appData.monoMode = True;
1921 premoveHighlightColor = *(Pixel *) vTo.addr;
1929 { // [HGM] taken out of main
1931 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1932 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1933 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1935 if (appData.bitmapDirectory[0] != NULLCHAR) {
1939 CreateXPMBoard(appData.liteBackTextureFile, 1);
1940 CreateXPMBoard(appData.darkBackTextureFile, 0);
1944 /* Create regular pieces */
1945 if (!useImages) CreatePieces();
1950 main (int argc, char **argv)
1952 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1953 XSetWindowAttributes window_attributes;
1955 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1956 XrmValue vFrom, vTo;
1957 XtGeometryResult gres;
1960 int forceMono = False;
1962 srandom(time(0)); // [HGM] book: make random truly random
1964 setbuf(stdout, NULL);
1965 setbuf(stderr, NULL);
1968 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1969 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1973 programName = strrchr(argv[0], '/');
1974 if (programName == NULL)
1975 programName = argv[0];
1980 XtSetLanguageProc(NULL, NULL, NULL);
1981 bindtextdomain(PACKAGE, LOCALEDIR);
1982 textdomain(PACKAGE);
1986 XtAppInitialize(&appContext, "XBoard", shellOptions,
1987 XtNumber(shellOptions),
1988 &argc, argv, xboardResources, NULL, 0);
1989 appData.boardSize = "";
1990 InitAppData(ConvertToLine(argc, argv));
1992 if (p == NULL) p = "/tmp";
1993 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1994 gameCopyFilename = (char*) malloc(i);
1995 gamePasteFilename = (char*) malloc(i);
1996 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1997 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1999 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2000 clientResources, XtNumber(clientResources),
2003 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2004 static char buf[MSG_SIZ];
2005 EscapeExpand(buf, appData.firstInitString);
2006 appData.firstInitString = strdup(buf);
2007 EscapeExpand(buf, appData.secondInitString);
2008 appData.secondInitString = strdup(buf);
2009 EscapeExpand(buf, appData.firstComputerString);
2010 appData.firstComputerString = strdup(buf);
2011 EscapeExpand(buf, appData.secondComputerString);
2012 appData.secondComputerString = strdup(buf);
2015 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2018 if (chdir(chessDir) != 0) {
2019 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2025 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2026 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2027 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2028 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2031 setbuf(debugFP, NULL);
2035 if (appData.debugMode) {
2036 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2040 /* [HGM,HR] make sure board size is acceptable */
2041 if(appData.NrFiles > BOARD_FILES ||
2042 appData.NrRanks > BOARD_RANKS )
2043 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2046 /* This feature does not work; animation needs a rewrite */
2047 appData.highlightDragging = FALSE;
2051 xDisplay = XtDisplay(shellWidget);
2052 xScreen = DefaultScreen(xDisplay);
2053 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2055 gameInfo.variant = StringToVariant(appData.variant);
2056 InitPosition(FALSE);
2059 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2061 if (isdigit(appData.boardSize[0])) {
2062 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2063 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2064 &fontPxlSize, &smallLayout, &tinyLayout);
2066 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2067 programName, appData.boardSize);
2071 /* Find some defaults; use the nearest known size */
2072 SizeDefaults *szd, *nearest;
2073 int distance = 99999;
2074 nearest = szd = sizeDefaults;
2075 while (szd->name != NULL) {
2076 if (abs(szd->squareSize - squareSize) < distance) {
2078 distance = abs(szd->squareSize - squareSize);
2079 if (distance == 0) break;
2083 if (i < 2) lineGap = nearest->lineGap;
2084 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2085 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2086 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2087 if (i < 6) smallLayout = nearest->smallLayout;
2088 if (i < 7) tinyLayout = nearest->tinyLayout;
2091 SizeDefaults *szd = sizeDefaults;
2092 if (*appData.boardSize == NULLCHAR) {
2093 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2094 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2097 if (szd->name == NULL) szd--;
2098 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2100 while (szd->name != NULL &&
2101 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2102 if (szd->name == NULL) {
2103 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2104 programName, appData.boardSize);
2108 squareSize = szd->squareSize;
2109 lineGap = szd->lineGap;
2110 clockFontPxlSize = szd->clockFontPxlSize;
2111 coordFontPxlSize = szd->coordFontPxlSize;
2112 fontPxlSize = szd->fontPxlSize;
2113 smallLayout = szd->smallLayout;
2114 tinyLayout = szd->tinyLayout;
2115 // [HGM] font: use defaults from settings file if available and not overruled
2117 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2118 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2119 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2120 appData.font = fontTable[MESSAGE_FONT][squareSize];
2121 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2122 appData.coordFont = fontTable[COORD_FONT][squareSize];
2124 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2125 if (strlen(appData.pixmapDirectory) > 0) {
2126 p = ExpandPathName(appData.pixmapDirectory);
2128 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2129 appData.pixmapDirectory);
2132 if (appData.debugMode) {
2133 fprintf(stderr, _("\
2134 XBoard square size (hint): %d\n\
2135 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2137 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2138 if (appData.debugMode) {
2139 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2142 defaultLineGap = lineGap;
2143 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2145 /* [HR] height treated separately (hacked) */
2146 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2147 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2148 if (appData.showJail == 1) {
2149 /* Jail on top and bottom */
2150 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2151 XtSetArg(boardArgs[2], XtNheight,
2152 boardHeight + 2*(lineGap + squareSize));
2153 } else if (appData.showJail == 2) {
2155 XtSetArg(boardArgs[1], XtNwidth,
2156 boardWidth + 2*(lineGap + squareSize));
2157 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2160 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2161 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2165 * Determine what fonts to use.
2168 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2169 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2170 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2171 fontSet = CreateFontSet(appData.font);
2172 clockFontSet = CreateFontSet(appData.clockFont);
2174 /* For the coordFont, use the 0th font of the fontset. */
2175 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2176 XFontStruct **font_struct_list;
2177 char **font_name_list;
2178 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2179 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2180 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2183 appData.font = FindFont(appData.font, fontPxlSize);
2184 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2185 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2186 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2187 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2188 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2189 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2191 countFontID = coordFontID; // [HGM] holdings
2192 countFontStruct = coordFontStruct;
2194 xdb = XtDatabase(xDisplay);
2196 XrmPutLineResource(&xdb, "*international: True");
2197 vTo.size = sizeof(XFontSet);
2198 vTo.addr = (XtPointer) &fontSet;
2199 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2201 XrmPutStringResource(&xdb, "*font", appData.font);
2205 * Detect if there are not enough colors available and adapt.
2207 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2208 appData.monoMode = True;
2211 forceMono = MakeColors();
2214 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2216 appData.monoMode = True;
2219 if (appData.lowTimeWarning && !appData.monoMode) {
2220 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2221 vFrom.size = strlen(appData.lowTimeWarningColor);
2222 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2223 if (vTo.addr == NULL)
2224 appData.monoMode = True;
2226 lowTimeWarningColor = *(Pixel *) vTo.addr;
2229 if (appData.monoMode && appData.debugMode) {
2230 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2231 (unsigned long) XWhitePixel(xDisplay, xScreen),
2232 (unsigned long) XBlackPixel(xDisplay, xScreen));
2235 ParseIcsTextColors();
2236 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2237 textColors[ColorNone].attr = 0;
2239 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2245 layoutName = "tinyLayout";
2246 } else if (smallLayout) {
2247 layoutName = "smallLayout";
2249 layoutName = "normalLayout";
2251 /* Outer layoutWidget is there only to provide a name for use in
2252 resources that depend on the layout style */
2254 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2255 layoutArgs, XtNumber(layoutArgs));
2257 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2258 formArgs, XtNumber(formArgs));
2259 XtSetArg(args[0], XtNdefaultDistance, &sep);
2260 XtGetValues(formWidget, args, 1);
2263 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2264 XtSetArg(args[0], XtNtop, XtChainTop);
2265 XtSetArg(args[1], XtNbottom, XtChainTop);
2266 XtSetArg(args[2], XtNright, XtChainLeft);
2267 XtSetValues(menuBarWidget, args, 3);
2269 widgetList[j++] = whiteTimerWidget =
2270 XtCreateWidget("whiteTime", labelWidgetClass,
2271 formWidget, timerArgs, XtNumber(timerArgs));
2273 XtSetArg(args[0], XtNfontSet, clockFontSet);
2275 XtSetArg(args[0], XtNfont, clockFontStruct);
2277 XtSetArg(args[1], XtNtop, XtChainTop);
2278 XtSetArg(args[2], XtNbottom, XtChainTop);
2279 XtSetValues(whiteTimerWidget, args, 3);
2281 widgetList[j++] = blackTimerWidget =
2282 XtCreateWidget("blackTime", labelWidgetClass,
2283 formWidget, timerArgs, XtNumber(timerArgs));
2285 XtSetArg(args[0], XtNfontSet, clockFontSet);
2287 XtSetArg(args[0], XtNfont, clockFontStruct);
2289 XtSetArg(args[1], XtNtop, XtChainTop);
2290 XtSetArg(args[2], XtNbottom, XtChainTop);
2291 XtSetValues(blackTimerWidget, args, 3);
2293 if (appData.titleInWindow) {
2294 widgetList[j++] = titleWidget =
2295 XtCreateWidget("title", labelWidgetClass, formWidget,
2296 titleArgs, XtNumber(titleArgs));
2297 XtSetArg(args[0], XtNtop, XtChainTop);
2298 XtSetArg(args[1], XtNbottom, XtChainTop);
2299 XtSetValues(titleWidget, args, 2);
2302 if (appData.showButtonBar) {
2303 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2304 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2305 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2306 XtSetArg(args[2], XtNtop, XtChainTop);
2307 XtSetArg(args[3], XtNbottom, XtChainTop);
2308 XtSetValues(buttonBarWidget, args, 4);
2311 widgetList[j++] = messageWidget =
2312 XtCreateWidget("message", labelWidgetClass, formWidget,
2313 messageArgs, XtNumber(messageArgs));
2314 XtSetArg(args[0], XtNtop, XtChainTop);
2315 XtSetArg(args[1], XtNbottom, XtChainTop);
2316 XtSetValues(messageWidget, args, 2);
2318 widgetList[j++] = boardWidget =
2319 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2320 XtNumber(boardArgs));
2322 XtManageChildren(widgetList, j);
2324 timerWidth = (boardWidth - sep) / 2;
2325 XtSetArg(args[0], XtNwidth, timerWidth);
2326 XtSetValues(whiteTimerWidget, args, 1);
2327 XtSetValues(blackTimerWidget, args, 1);
2329 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2330 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2331 XtGetValues(whiteTimerWidget, args, 2);
2333 if (appData.showButtonBar) {
2334 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2335 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2336 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2340 * formWidget uses these constraints but they are stored
2344 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2345 XtSetValues(menuBarWidget, args, i);
2346 if (appData.titleInWindow) {
2349 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2350 XtSetValues(whiteTimerWidget, args, i);
2352 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2353 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2354 XtSetValues(blackTimerWidget, args, i);
2356 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2357 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2358 XtSetValues(titleWidget, args, i);
2360 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2361 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2362 XtSetValues(messageWidget, args, i);
2363 if (appData.showButtonBar) {
2365 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2366 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2367 XtSetValues(buttonBarWidget, args, i);
2371 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2372 XtSetValues(whiteTimerWidget, args, i);
2374 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2375 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2376 XtSetValues(blackTimerWidget, args, i);
2378 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2379 XtSetValues(titleWidget, args, i);
2381 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2382 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2383 XtSetValues(messageWidget, args, i);
2384 if (appData.showButtonBar) {
2386 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2387 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2388 XtSetValues(buttonBarWidget, args, i);
2393 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2394 XtSetValues(whiteTimerWidget, args, i);
2396 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2397 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2398 XtSetValues(blackTimerWidget, args, i);
2400 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2401 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2402 XtSetValues(messageWidget, args, i);
2403 if (appData.showButtonBar) {
2405 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2406 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2407 XtSetValues(buttonBarWidget, args, i);
2411 XtSetArg(args[0], XtNfromVert, messageWidget);
2412 XtSetArg(args[1], XtNtop, XtChainTop);
2413 XtSetArg(args[2], XtNbottom, XtChainBottom);
2414 XtSetArg(args[3], XtNleft, XtChainLeft);
2415 XtSetArg(args[4], XtNright, XtChainRight);
2416 XtSetValues(boardWidget, args, 5);
2418 XtRealizeWidget(shellWidget);
2421 XtSetArg(args[0], XtNx, wpMain.x);
2422 XtSetArg(args[1], XtNy, wpMain.y);
2423 XtSetValues(shellWidget, args, 2);
2427 * Correct the width of the message and title widgets.
2428 * It is not known why some systems need the extra fudge term.
2429 * The value "2" is probably larger than needed.
2431 XawFormDoLayout(formWidget, False);
2433 #define WIDTH_FUDGE 2
2435 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2436 XtSetArg(args[i], XtNheight, &h); i++;
2437 XtGetValues(messageWidget, args, i);
2438 if (appData.showButtonBar) {
2440 XtSetArg(args[i], XtNwidth, &w); i++;
2441 XtGetValues(buttonBarWidget, args, i);
2442 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2444 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2447 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2448 if (gres != XtGeometryYes && appData.debugMode) {
2449 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2450 programName, gres, w, h, wr, hr);
2453 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2454 /* The size used for the child widget in layout lags one resize behind
2455 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2457 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2458 if (gres != XtGeometryYes && appData.debugMode) {
2459 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2460 programName, gres, w, h, wr, hr);
2463 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2464 XtSetArg(args[1], XtNright, XtChainRight);
2465 XtSetValues(messageWidget, args, 2);
2467 if (appData.titleInWindow) {
2469 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2470 XtSetArg(args[i], XtNheight, &h); i++;
2471 XtGetValues(titleWidget, args, i);
2473 w = boardWidth - 2*bor;
2475 XtSetArg(args[0], XtNwidth, &w);
2476 XtGetValues(menuBarWidget, args, 1);
2477 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2480 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2481 if (gres != XtGeometryYes && appData.debugMode) {
2483 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2484 programName, gres, w, h, wr, hr);
2487 XawFormDoLayout(formWidget, True);
2489 xBoardWindow = XtWindow(boardWidget);
2491 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2492 // not need to go into InitDrawingSizes().
2496 * Create X checkmark bitmap and initialize option menu checks.
2498 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2499 checkmark_bits, checkmark_width, checkmark_height);
2500 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2501 #ifndef OPTIONSDIALOG
2502 if (appData.alwaysPromoteToQueen) {
2503 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2506 if (appData.animateDragging) {
2507 XtSetValues(XtNameToWidget(menuBarWidget,
2508 "menuOptions.Animate Dragging"),
2511 if (appData.animate) {
2512 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2515 if (appData.autoCallFlag) {
2516 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2519 if (appData.autoFlipView) {
2520 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2523 if (appData.blindfold) {
2524 XtSetValues(XtNameToWidget(menuBarWidget,
2525 "menuOptions.Blindfold"), args, 1);
2527 if (appData.flashCount > 0) {
2528 XtSetValues(XtNameToWidget(menuBarWidget,
2529 "menuOptions.Flash Moves"),
2533 if (appData.highlightDragging) {
2534 XtSetValues(XtNameToWidget(menuBarWidget,
2535 "menuOptions.Highlight Dragging"),
2539 if (appData.highlightLastMove) {
2540 XtSetValues(XtNameToWidget(menuBarWidget,
2541 "menuOptions.Highlight Last Move"),
2544 if (appData.highlightMoveWithArrow) {
2545 XtSetValues(XtNameToWidget(menuBarWidget,
2546 "menuOptions.Arrow"),
2549 // if (appData.icsAlarm) {
2550 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2553 if (appData.ringBellAfterMoves) {
2554 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2557 if (appData.oneClick) {
2558 XtSetValues(XtNameToWidget(menuBarWidget,
2559 "menuOptions.OneClick"), args, 1);
2561 if (appData.periodicUpdates) {
2562 XtSetValues(XtNameToWidget(menuBarWidget,
2563 "menuOptions.Periodic Updates"), args, 1);
2565 if (appData.ponderNextMove) {
2566 XtSetValues(XtNameToWidget(menuBarWidget,
2567 "menuOptions.Ponder Next Move"), args, 1);
2569 if (appData.popupExitMessage) {
2570 XtSetValues(XtNameToWidget(menuBarWidget,
2571 "menuOptions.Popup Exit Message"), args, 1);
2573 if (appData.popupMoveErrors) {
2574 XtSetValues(XtNameToWidget(menuBarWidget,
2575 "menuOptions.Popup Move Errors"), args, 1);
2577 // if (appData.premove) {
2578 // XtSetValues(XtNameToWidget(menuBarWidget,
2579 // "menuOptions.Premove"), args, 1);
2581 if (appData.showCoords) {
2582 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2585 if (appData.hideThinkingFromHuman) {
2586 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2589 if (appData.testLegality) {
2590 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2594 if (saveSettingsOnExit) {
2595 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2602 ReadBitmap(&wIconPixmap, "icon_white.bm",
2603 icon_white_bits, icon_white_width, icon_white_height);
2604 ReadBitmap(&bIconPixmap, "icon_black.bm",
2605 icon_black_bits, icon_black_width, icon_black_height);
2606 iconPixmap = wIconPixmap;
2608 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2609 XtSetValues(shellWidget, args, i);
2612 * Create a cursor for the board widget.
2614 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2615 XChangeWindowAttributes(xDisplay, xBoardWindow,
2616 CWCursor, &window_attributes);
2619 * Inhibit shell resizing.
2621 shellArgs[0].value = (XtArgVal) &w;
2622 shellArgs[1].value = (XtArgVal) &h;
2623 XtGetValues(shellWidget, shellArgs, 2);
2624 shellArgs[4].value = shellArgs[2].value = w;
2625 shellArgs[5].value = shellArgs[3].value = h;
2626 XtSetValues(shellWidget, &shellArgs[2], 4);
2627 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2628 marginH = h - boardHeight;
2630 CatchDeleteWindow(shellWidget, "QuitProc");
2638 if (appData.animate || appData.animateDragging)
2641 XtAugmentTranslations(formWidget,
2642 XtParseTranslationTable(globalTranslations));
2643 XtAugmentTranslations(boardWidget,
2644 XtParseTranslationTable(boardTranslations));
2645 XtAugmentTranslations(whiteTimerWidget,
2646 XtParseTranslationTable(whiteTranslations));
2647 XtAugmentTranslations(blackTimerWidget,
2648 XtParseTranslationTable(blackTranslations));
2650 /* Why is the following needed on some versions of X instead
2651 * of a translation? */
2652 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2653 (XtEventHandler) EventProc, NULL);
2655 XtAddEventHandler(formWidget, KeyPressMask, False,
2656 (XtEventHandler) MoveTypeInProc, NULL);
2658 /* [AS] Restore layout */
2659 if( wpMoveHistory.visible ) {
2663 if( wpEvalGraph.visible )
2668 if( wpEngineOutput.visible ) {
2669 EngineOutputPopUp();
2674 if (errorExitStatus == -1) {
2675 if (appData.icsActive) {
2676 /* We now wait until we see "login:" from the ICS before
2677 sending the logon script (problems with timestamp otherwise) */
2678 /*ICSInitScript();*/
2679 if (appData.icsInputBox) ICSInputBoxPopUp();
2683 signal(SIGWINCH, TermSizeSigHandler);
2685 signal(SIGINT, IntSigHandler);
2686 signal(SIGTERM, IntSigHandler);
2687 if (*appData.cmailGameName != NULLCHAR) {
2688 signal(SIGUSR1, CmailSigHandler);
2691 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2693 // XtSetKeyboardFocus(shellWidget, formWidget);
2694 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2696 XtAppMainLoop(appContext);
2697 if (appData.debugMode) fclose(debugFP); // [DM] debug
2701 static Boolean noEcho;
2706 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2707 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2709 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2710 unlink(gameCopyFilename);
2711 unlink(gamePasteFilename);
2712 if(noEcho) EchoOn();
2716 TermSizeSigHandler (int sig)
2722 IntSigHandler (int sig)
2728 CmailSigHandler (int sig)
2733 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2735 /* Activate call-back function CmailSigHandlerCallBack() */
2736 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2738 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2742 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2745 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2747 /**** end signal code ****/
2753 /* try to open the icsLogon script, either in the location given
2754 * or in the users HOME directory
2761 f = fopen(appData.icsLogon, "r");
2764 homedir = getenv("HOME");
2765 if (homedir != NULL)
2767 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2768 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2769 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2770 f = fopen(buf, "r");
2775 ProcessICSInitScript(f);
2777 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2796 GreyRevert (Boolean grey)
2799 if (!menuBarWidget) return;
2800 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2802 DisplayError("menuEdit.Revert", 0);
2804 XtSetSensitive(w, !grey);
2806 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2808 DisplayError("menuEdit.Annotate", 0);
2810 XtSetSensitive(w, !grey);
2815 SetMenuEnables (Enables *enab)
2818 if (!menuBarWidget) return;
2819 while (enab->name != NULL) {
2820 w = XtNameToWidget(menuBarWidget, enab->name);
2822 DisplayError(enab->name, 0);
2824 XtSetSensitive(w, enab->value);
2830 Enables icsEnables[] = {
2831 { "menuFile.Mail Move", False },
2832 { "menuFile.Reload CMail Message", False },
2833 { "menuMode.Machine Black", False },
2834 { "menuMode.Machine White", False },
2835 { "menuMode.Analysis Mode", False },
2836 { "menuMode.Analyze File", False },
2837 { "menuMode.Two Machines", False },
2838 { "menuMode.Machine Match", False },
2840 { "menuEngine.Hint", False },
2841 { "menuEngine.Book", False },
2842 { "menuEngine.Move Now", False },
2843 #ifndef OPTIONSDIALOG
2844 { "menuOptions.Periodic Updates", False },
2845 { "menuOptions.Hide Thinking", False },
2846 { "menuOptions.Ponder Next Move", False },
2849 { "menuEngine.Engine #1 Settings", False },
2850 { "menuEngine.Engine #2 Settings", False },
2851 { "menuEngine.Load Engine", False },
2852 { "menuEdit.Annotate", False },
2853 { "menuOptions.Match", False },
2857 Enables ncpEnables[] = {
2858 { "menuFile.Mail Move", False },
2859 { "menuFile.Reload CMail Message", False },
2860 { "menuMode.Machine White", False },
2861 { "menuMode.Machine Black", False },
2862 { "menuMode.Analysis Mode", False },
2863 { "menuMode.Analyze File", False },
2864 { "menuMode.Two Machines", False },
2865 { "menuMode.Machine Match", False },
2866 { "menuMode.ICS Client", False },
2867 { "menuView.ICStex", False },
2868 { "menuView.ICS Input Box", False },
2869 { "Action", False },
2870 { "menuEdit.Revert", False },
2871 { "menuEdit.Annotate", False },
2872 { "menuEngine.Engine #1 Settings", False },
2873 { "menuEngine.Engine #2 Settings", False },
2874 { "menuEngine.Move Now", False },
2875 { "menuEngine.Retract Move", False },
2876 { "menuOptions.ICS", False },
2877 #ifndef OPTIONSDIALOG
2878 { "menuOptions.Auto Flag", False },
2879 { "menuOptions.Auto Flip View", False },
2880 // { "menuOptions.ICS Alarm", False },
2881 { "menuOptions.Move Sound", False },
2882 { "menuOptions.Hide Thinking", False },
2883 { "menuOptions.Periodic Updates", False },
2884 { "menuOptions.Ponder Next Move", False },
2886 { "menuEngine.Hint", False },
2887 { "menuEngine.Book", False },
2891 Enables gnuEnables[] = {
2892 { "menuMode.ICS Client", False },
2893 { "menuView.ICStex", False },
2894 { "menuView.ICS Input Box", False },
2895 { "menuAction.Accept", False },
2896 { "menuAction.Decline", False },
2897 { "menuAction.Rematch", False },
2898 { "menuAction.Adjourn", False },
2899 { "menuAction.Stop Examining", False },
2900 { "menuAction.Stop Observing", False },
2901 { "menuAction.Upload to Examine", False },
2902 { "menuEdit.Revert", False },
2903 { "menuEdit.Annotate", False },
2904 { "menuOptions.ICS", False },
2906 /* The next two options rely on SetCmailMode being called *after* */
2907 /* SetGNUMode so that when GNU is being used to give hints these */
2908 /* menu options are still available */
2910 { "menuFile.Mail Move", False },
2911 { "menuFile.Reload CMail Message", False },
2912 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2913 { "menuMode.Machine White", True },
2914 { "menuMode.Machine Black", True },
2915 { "menuMode.Analysis Mode", True },
2916 { "menuMode.Analyze File", True },
2917 { "menuMode.Two Machines", True },
2918 { "menuMode.Machine Match", True },
2919 { "menuEngine.Engine #1 Settings", True },
2920 { "menuEngine.Engine #2 Settings", True },
2921 { "menuEngine.Hint", True },
2922 { "menuEngine.Book", True },
2923 { "menuEngine.Move Now", True },
2924 { "menuEngine.Retract Move", True },
2929 Enables cmailEnables[] = {
2931 { "menuAction.Call Flag", False },
2932 { "menuAction.Draw", True },
2933 { "menuAction.Adjourn", False },
2934 { "menuAction.Abort", False },
2935 { "menuAction.Stop Observing", False },
2936 { "menuAction.Stop Examining", False },
2937 { "menuFile.Mail Move", True },
2938 { "menuFile.Reload CMail Message", True },
2942 Enables trainingOnEnables[] = {
2943 { "menuMode.Edit Comment", False },
2944 { "menuMode.Pause", False },
2945 { "menuEdit.Forward", False },
2946 { "menuEdit.Backward", False },
2947 { "menuEdit.Forward to End", False },
2948 { "menuEdit.Back to Start", False },
2949 { "menuEngine.Move Now", False },
2950 { "menuEdit.Truncate Game", False },
2954 Enables trainingOffEnables[] = {
2955 { "menuMode.Edit Comment", True },
2956 { "menuMode.Pause", True },
2957 { "menuEdit.Forward", True },
2958 { "menuEdit.Backward", True },
2959 { "menuEdit.Forward to End", True },
2960 { "menuEdit.Back to Start", True },
2961 { "menuEngine.Move Now", True },
2962 { "menuEdit.Truncate Game", True },
2966 Enables machineThinkingEnables[] = {
2967 { "menuFile.Load Game", False },
2968 // { "menuFile.Load Next Game", False },
2969 // { "menuFile.Load Previous Game", False },
2970 // { "menuFile.Reload Same Game", False },
2971 { "menuEdit.Paste Game", False },
2972 { "menuFile.Load Position", False },
2973 // { "menuFile.Load Next Position", False },
2974 // { "menuFile.Load Previous Position", False },
2975 // { "menuFile.Reload Same Position", False },
2976 { "menuEdit.Paste Position", False },
2977 { "menuMode.Machine White", False },
2978 { "menuMode.Machine Black", False },
2979 { "menuMode.Two Machines", False },
2980 // { "menuMode.Machine Match", False },
2981 { "menuEngine.Retract Move", False },
2985 Enables userThinkingEnables[] = {
2986 { "menuFile.Load Game", True },
2987 // { "menuFile.Load Next Game", True },
2988 // { "menuFile.Load Previous Game", True },
2989 // { "menuFile.Reload Same Game", True },
2990 { "menuEdit.Paste Game", True },
2991 { "menuFile.Load Position", True },
2992 // { "menuFile.Load Next Position", True },
2993 // { "menuFile.Load Previous Position", True },
2994 // { "menuFile.Reload Same Position", True },
2995 { "menuEdit.Paste Position", True },
2996 { "menuMode.Machine White", True },
2997 { "menuMode.Machine Black", True },
2998 { "menuMode.Two Machines", True },
2999 // { "menuMode.Machine Match", True },
3000 { "menuEngine.Retract Move", True },
3007 SetMenuEnables(icsEnables);
3010 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3011 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3012 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3020 SetMenuEnables(ncpEnables);
3026 SetMenuEnables(gnuEnables);
3032 SetMenuEnables(cmailEnables);
3036 SetTrainingModeOn ()
3038 SetMenuEnables(trainingOnEnables);
3039 if (appData.showButtonBar) {
3040 XtSetSensitive(buttonBarWidget, False);
3046 SetTrainingModeOff ()
3048 SetMenuEnables(trainingOffEnables);
3049 if (appData.showButtonBar) {
3050 XtSetSensitive(buttonBarWidget, True);
3055 SetUserThinkingEnables ()
3057 if (appData.noChessProgram) return;
3058 SetMenuEnables(userThinkingEnables);
3062 SetMachineThinkingEnables ()
3064 if (appData.noChessProgram) return;
3065 SetMenuEnables(machineThinkingEnables);
3067 case MachinePlaysBlack:
3068 case MachinePlaysWhite:
3069 case TwoMachinesPlay:
3070 XtSetSensitive(XtNameToWidget(menuBarWidget,
3071 ModeToWidgetName(gameMode)), True);
3078 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3079 #define HISTORY_SIZE 64
3080 static char *history[HISTORY_SIZE];
3081 int histIn = 0, histP = 0;
3084 SaveInHistory (char *cmd)
3086 if (history[histIn] != NULL) {
3087 free(history[histIn]);
3088 history[histIn] = NULL;
3090 if (*cmd == NULLCHAR) return;
3091 history[histIn] = StrSave(cmd);
3092 histIn = (histIn + 1) % HISTORY_SIZE;
3093 if (history[histIn] != NULL) {
3094 free(history[histIn]);
3095 history[histIn] = NULL;
3101 PrevInHistory (char *cmd)
3104 if (histP == histIn) {
3105 if (history[histIn] != NULL) free(history[histIn]);
3106 history[histIn] = StrSave(cmd);
3108 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3109 if (newhp == histIn || history[newhp] == NULL) return NULL;
3111 return history[histP];
3117 if (histP == histIn) return NULL;
3118 histP = (histP + 1) % HISTORY_SIZE;
3119 return history[histP];
3121 // end of borrowed code
3123 #define Abs(n) ((n)<0 ? -(n) : (n))
3127 InsertPxlSize (char *pattern, int targetPxlSize)
3129 char *base_fnt_lst, strInt[12], *p, *q;
3130 int alternatives, i, len, strIntLen;
3133 * Replace the "*" (if present) in the pixel-size slot of each
3134 * alternative with the targetPxlSize.
3138 while ((p = strchr(p, ',')) != NULL) {
3142 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3143 strIntLen = strlen(strInt);
3144 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3148 while (alternatives--) {
3149 char *comma = strchr(p, ',');
3150 for (i=0; i<14; i++) {
3151 char *hyphen = strchr(p, '-');
3153 if (comma && hyphen > comma) break;
3154 len = hyphen + 1 - p;
3155 if (i == 7 && *p == '*' && len == 2) {
3157 memcpy(q, strInt, strIntLen);
3167 len = comma + 1 - p;
3174 return base_fnt_lst;
3178 CreateFontSet (char *base_fnt_lst)
3181 char **missing_list;
3185 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3186 &missing_list, &missing_count, &def_string);
3187 if (appData.debugMode) {
3189 XFontStruct **font_struct_list;
3190 char **font_name_list;
3191 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3193 fprintf(debugFP, " got list %s, locale %s\n",
3194 XBaseFontNameListOfFontSet(fntSet),
3195 XLocaleOfFontSet(fntSet));
3196 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3197 for (i = 0; i < count; i++) {
3198 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3201 for (i = 0; i < missing_count; i++) {
3202 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3205 if (fntSet == NULL) {
3206 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3211 #else // not ENABLE_NLS
3213 * Find a font that matches "pattern" that is as close as
3214 * possible to the targetPxlSize. Prefer fonts that are k
3215 * pixels smaller to fonts that are k pixels larger. The
3216 * pattern must be in the X Consortium standard format,
3217 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3218 * The return value should be freed with XtFree when no
3222 FindFont (char *pattern, int targetPxlSize)
3224 char **fonts, *p, *best, *scalable, *scalableTail;
3225 int i, j, nfonts, minerr, err, pxlSize;
3227 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3229 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3230 programName, pattern);
3237 for (i=0; i<nfonts; i++) {
3240 if (*p != '-') continue;
3242 if (*p == NULLCHAR) break;
3243 if (*p++ == '-') j++;
3245 if (j < 7) continue;
3248 scalable = fonts[i];
3251 err = pxlSize - targetPxlSize;
3252 if (Abs(err) < Abs(minerr) ||
3253 (minerr > 0 && err < 0 && -err == minerr)) {
3259 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3260 /* If the error is too big and there is a scalable font,
3261 use the scalable font. */
3262 int headlen = scalableTail - scalable;
3263 p = (char *) XtMalloc(strlen(scalable) + 10);
3264 while (isdigit(*scalableTail)) scalableTail++;
3265 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3267 p = (char *) XtMalloc(strlen(best) + 2);
3268 safeStrCpy(p, best, strlen(best)+1 );
3270 if (appData.debugMode) {
3271 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3272 pattern, targetPxlSize, p);
3274 XFreeFontNames(fonts);
3281 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3282 // must be called before all non-first callse to CreateGCs()
3283 XtReleaseGC(shellWidget, highlineGC);
3284 XtReleaseGC(shellWidget, lightSquareGC);
3285 XtReleaseGC(shellWidget, darkSquareGC);
3286 XtReleaseGC(shellWidget, lineGC);
3287 if (appData.monoMode) {
3288 if (DefaultDepth(xDisplay, xScreen) == 1) {
3289 XtReleaseGC(shellWidget, wbPieceGC);
3291 XtReleaseGC(shellWidget, bwPieceGC);
3294 XtReleaseGC(shellWidget, prelineGC);
3295 XtReleaseGC(shellWidget, jailSquareGC);
3296 XtReleaseGC(shellWidget, wdPieceGC);
3297 XtReleaseGC(shellWidget, wlPieceGC);
3298 XtReleaseGC(shellWidget, wjPieceGC);
3299 XtReleaseGC(shellWidget, bdPieceGC);
3300 XtReleaseGC(shellWidget, blPieceGC);
3301 XtReleaseGC(shellWidget, bjPieceGC);
3306 CreateGCs (int redo)
3308 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3309 | GCBackground | GCFunction | GCPlaneMask;
3310 XGCValues gc_values;
3313 gc_values.plane_mask = AllPlanes;
3314 gc_values.line_width = lineGap;
3315 gc_values.line_style = LineSolid;
3316 gc_values.function = GXcopy;
3319 DeleteGCs(); // called a second time; clean up old GCs first
3320 } else { // [HGM] grid and font GCs created on first call only
3321 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3322 gc_values.background = XWhitePixel(xDisplay, xScreen);
3323 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3324 XSetFont(xDisplay, coordGC, coordFontID);
3326 // [HGM] make font for holdings counts (white on black)
3327 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3328 gc_values.background = XBlackPixel(xDisplay, xScreen);
3329 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3330 XSetFont(xDisplay, countGC, countFontID);
3332 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3333 gc_values.background = XBlackPixel(xDisplay, xScreen);
3334 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3336 if (appData.monoMode) {
3337 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3338 gc_values.background = XWhitePixel(xDisplay, xScreen);
3339 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3341 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3342 gc_values.background = XBlackPixel(xDisplay, xScreen);
3343 lightSquareGC = wbPieceGC
3344 = XtGetGC(shellWidget, value_mask, &gc_values);
3346 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3347 gc_values.background = XWhitePixel(xDisplay, xScreen);
3348 darkSquareGC = bwPieceGC
3349 = XtGetGC(shellWidget, value_mask, &gc_values);
3351 if (DefaultDepth(xDisplay, xScreen) == 1) {
3352 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3353 gc_values.function = GXcopyInverted;
3354 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3355 gc_values.function = GXcopy;
3356 if (XBlackPixel(xDisplay, xScreen) == 1) {
3357 bwPieceGC = darkSquareGC;
3358 wbPieceGC = copyInvertedGC;
3360 bwPieceGC = copyInvertedGC;
3361 wbPieceGC = lightSquareGC;
3365 gc_values.foreground = highlightSquareColor;
3366 gc_values.background = highlightSquareColor;
3367 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3369 gc_values.foreground = premoveHighlightColor;
3370 gc_values.background = premoveHighlightColor;
3371 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3373 gc_values.foreground = lightSquareColor;
3374 gc_values.background = darkSquareColor;
3375 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3377 gc_values.foreground = darkSquareColor;
3378 gc_values.background = lightSquareColor;
3379 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3381 gc_values.foreground = jailSquareColor;
3382 gc_values.background = jailSquareColor;
3383 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3385 gc_values.foreground = whitePieceColor;
3386 gc_values.background = darkSquareColor;
3387 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3389 gc_values.foreground = whitePieceColor;
3390 gc_values.background = lightSquareColor;
3391 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3393 gc_values.foreground = whitePieceColor;
3394 gc_values.background = jailSquareColor;
3395 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3397 gc_values.foreground = blackPieceColor;
3398 gc_values.background = darkSquareColor;
3399 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3401 gc_values.foreground = blackPieceColor;
3402 gc_values.background = lightSquareColor;
3403 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3405 gc_values.foreground = blackPieceColor;
3406 gc_values.background = jailSquareColor;
3407 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3412 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3420 fp = fopen(filename, "rb");
3422 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3429 for (y=0; y<h; ++y) {
3430 for (x=0; x<h; ++x) {
3435 XPutPixel(xim, x, y, blackPieceColor);
3437 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3440 XPutPixel(xim, x, y, darkSquareColor);
3442 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3445 XPutPixel(xim, x, y, whitePieceColor);
3447 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3450 XPutPixel(xim, x, y, lightSquareColor);
3452 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3460 /* create Pixmap of piece */
3461 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3463 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3466 /* create Pixmap of clipmask
3467 Note: We assume the white/black pieces have the same
3468 outline, so we make only 6 masks. This is okay
3469 since the XPM clipmask routines do the same. */
3471 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3473 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3476 /* now create the 1-bit version */
3477 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3480 values.foreground = 1;
3481 values.background = 0;
3483 /* Don't use XtGetGC, not read only */
3484 maskGC = XCreateGC(xDisplay, *mask,
3485 GCForeground | GCBackground, &values);
3486 XCopyPlane(xDisplay, temp, *mask, maskGC,
3487 0, 0, squareSize, squareSize, 0, 0, 1);
3488 XFreePixmap(xDisplay, temp);
3493 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3501 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3506 /* The XSynchronize calls were copied from CreatePieces.
3507 Not sure if needed, but can't hurt */
3508 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3511 /* temp needed by loadXIM() */
3512 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3513 0, 0, ss, ss, AllPlanes, XYPixmap);
3515 if (strlen(appData.pixmapDirectory) == 0) {
3519 if (appData.monoMode) {
3520 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3524 fprintf(stderr, _("\nLoading XIMs...\n"));
3526 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3527 fprintf(stderr, "%d", piece+1);
3528 for (kind=0; kind<4; kind++) {
3529 fprintf(stderr, ".");
3530 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3531 ExpandPathName(appData.pixmapDirectory),
3532 piece <= (int) WhiteKing ? "" : "w",
3533 pieceBitmapNames[piece],
3535 ximPieceBitmap[kind][piece] =
3536 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3537 0, 0, ss, ss, AllPlanes, XYPixmap);
3538 if (appData.debugMode)
3539 fprintf(stderr, _("(File:%s:) "), buf);
3540 loadXIM(ximPieceBitmap[kind][piece],
3542 &(xpmPieceBitmap2[kind][piece]),
3543 &(ximMaskPm2[piece]));
3544 if(piece <= (int)WhiteKing)
3545 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3547 fprintf(stderr," ");
3549 /* Load light and dark squares */
3550 /* If the LSQ and DSQ pieces don't exist, we will
3551 draw them with solid squares. */
3552 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3553 if (access(buf, 0) != 0) {
3557 fprintf(stderr, _("light square "));
3559 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3560 0, 0, ss, ss, AllPlanes, XYPixmap);
3561 if (appData.debugMode)
3562 fprintf(stderr, _("(File:%s:) "), buf);
3564 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3565 fprintf(stderr, _("dark square "));
3566 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3567 ExpandPathName(appData.pixmapDirectory), ss);
3568 if (appData.debugMode)
3569 fprintf(stderr, _("(File:%s:) "), buf);
3571 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3572 0, 0, ss, ss, AllPlanes, XYPixmap);
3573 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3574 xpmJailSquare = xpmLightSquare;
3576 fprintf(stderr, _("Done.\n"));
3578 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3581 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3585 CreateXPMBoard (char *s, int kind)
3589 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3590 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3591 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3597 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3598 // thisroutine has to be called t free the old piece pixmaps
3600 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3601 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3603 XFreePixmap(xDisplay, xpmLightSquare);
3604 XFreePixmap(xDisplay, xpmDarkSquare);
3613 u_int ss = squareSize;
3615 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3616 XpmColorSymbol symbols[4];
3617 static int redo = False;
3619 if(redo) FreeXPMPieces(); else redo = 1;
3621 /* The XSynchronize calls were copied from CreatePieces.
3622 Not sure if needed, but can't hurt */
3623 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3625 /* Setup translations so piece colors match square colors */
3626 symbols[0].name = "light_piece";
3627 symbols[0].value = appData.whitePieceColor;
3628 symbols[1].name = "dark_piece";
3629 symbols[1].value = appData.blackPieceColor;
3630 symbols[2].name = "light_square";
3631 symbols[2].value = appData.lightSquareColor;
3632 symbols[3].name = "dark_square";
3633 symbols[3].value = appData.darkSquareColor;
3635 attr.valuemask = XpmColorSymbols;
3636 attr.colorsymbols = symbols;
3637 attr.numsymbols = 4;
3639 if (appData.monoMode) {
3640 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3644 if (strlen(appData.pixmapDirectory) == 0) {
3645 XpmPieces* pieces = builtInXpms;
3648 while (pieces->size != squareSize && pieces->size) pieces++;
3649 if (!pieces->size) {
3650 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3653 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3654 for (kind=0; kind<4; kind++) {
3656 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3657 pieces->xpm[piece][kind],
3658 &(xpmPieceBitmap2[kind][piece]),
3659 NULL, &attr)) != 0) {
3660 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3664 if(piece <= (int) WhiteKing)
3665 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3669 xpmJailSquare = xpmLightSquare;
3673 fprintf(stderr, _("\nLoading XPMs...\n"));
3676 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3677 fprintf(stderr, "%d ", piece+1);
3678 for (kind=0; kind<4; kind++) {
3679 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3680 ExpandPathName(appData.pixmapDirectory),
3681 piece > (int) WhiteKing ? "w" : "",
3682 pieceBitmapNames[piece],
3684 if (appData.debugMode) {
3685 fprintf(stderr, _("(File:%s:) "), buf);
3687 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3688 &(xpmPieceBitmap2[kind][piece]),
3689 NULL, &attr)) != 0) {
3690 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3691 // [HGM] missing: read of unorthodox piece failed; substitute King.
3692 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3693 ExpandPathName(appData.pixmapDirectory),
3695 if (appData.debugMode) {
3696 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3698 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3699 &(xpmPieceBitmap2[kind][piece]),
3703 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3708 if(piece <= (int) WhiteKing)
3709 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3712 /* Load light and dark squares */
3713 /* If the LSQ and DSQ pieces don't exist, we will
3714 draw them with solid squares. */
3715 fprintf(stderr, _("light square "));
3716 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3717 if (access(buf, 0) != 0) {
3721 if (appData.debugMode)
3722 fprintf(stderr, _("(File:%s:) "), buf);
3724 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3725 &xpmLightSquare, NULL, &attr)) != 0) {
3726 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3729 fprintf(stderr, _("dark square "));
3730 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3731 ExpandPathName(appData.pixmapDirectory), ss);
3732 if (appData.debugMode) {
3733 fprintf(stderr, _("(File:%s:) "), buf);
3735 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3736 &xpmDarkSquare, NULL, &attr)) != 0) {
3737 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3741 xpmJailSquare = xpmLightSquare;
3742 fprintf(stderr, _("Done.\n"));
3744 oldVariant = -1; // kludge to force re-makig of animation masks
3745 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3748 #endif /* HAVE_LIBXPM */
3751 /* No built-in bitmaps */
3756 u_int ss = squareSize;
3758 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3761 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3762 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3763 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3764 pieceBitmapNames[piece],
3765 ss, kind == SOLID ? 's' : 'o');
3766 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3767 if(piece <= (int)WhiteKing)
3768 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3772 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3776 /* With built-in bitmaps */
3780 BuiltInBits* bib = builtInBits;
3783 u_int ss = squareSize;
3785 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3788 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3790 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3791 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3792 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3793 pieceBitmapNames[piece],
3794 ss, kind == SOLID ? 's' : 'o');
3795 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3796 bib->bits[kind][piece], ss, ss);
3797 if(piece <= (int)WhiteKing)
3798 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3802 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3808 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3813 char msg[MSG_SIZ], fullname[MSG_SIZ];
3815 if (*appData.bitmapDirectory != NULLCHAR) {
3816 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3817 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3818 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3819 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3820 &w, &h, pm, &x_hot, &y_hot);
3821 fprintf(stderr, "load %s\n", name);
3822 if (errcode != BitmapSuccess) {
3824 case BitmapOpenFailed:
3825 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3827 case BitmapFileInvalid:
3828 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3830 case BitmapNoMemory:
3831 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3835 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3839 fprintf(stderr, _("%s: %s...using built-in\n"),
3841 } else if (w != wreq || h != hreq) {
3843 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3844 programName, fullname, w, h, wreq, hreq);
3850 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3860 if (lineGap == 0) return;
3862 /* [HR] Split this into 2 loops for non-square boards. */
3864 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3865 gridSegments[i].x1 = 0;
3866 gridSegments[i].x2 =
3867 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3868 gridSegments[i].y1 = gridSegments[i].y2
3869 = lineGap / 2 + (i * (squareSize + lineGap));
3872 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3873 gridSegments[j + i].y1 = 0;
3874 gridSegments[j + i].y2 =
3875 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3876 gridSegments[j + i].x1 = gridSegments[j + i].x2
3877 = lineGap / 2 + (j * (squareSize + lineGap));
3882 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3884 XtActionProc proc = (XtActionProc) addr;
3886 (proc)(NULL, NULL, NULL, NULL);
3890 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3897 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3900 XtSetArg(args[j], XtNleftMargin, 20); j++;
3901 XtSetArg(args[j], XtNrightMargin, 20); j++;
3903 while (mi->string != NULL) {
3904 if (strcmp(mi->string, "----") == 0) {
3905 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3908 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3909 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3911 XtAddCallback(entry, XtNcallback,
3912 (XtCallbackProc) MenuBarSelect,
3913 (caddr_t) mi->proc);
3920 CreateMenuBar (Menu *mb, int boardWidth)
3922 int i, j, nr = 0, wtot = 0, widths[10];
3925 char menuName[MSG_SIZ];
3930 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3931 XtSetArg(args[j], XtNvSpace, 0); j++;
3932 XtSetArg(args[j], XtNborderWidth, 0); j++;
3933 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3934 formWidget, args, j);
3936 while (mb->name != NULL) {
3937 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3938 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3940 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3941 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3942 XtSetArg(args[j], XtNborderWidth, 0); j++;
3943 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3945 CreateMenuBarPopup(menuBar, menuName, mb);
3947 XtSetArg(args[j], XtNwidth, &w); j++;
3948 XtGetValues(mb->subMenu, args, j);
3949 wtot += mb->textWidth = widths[nr++] = w;
3952 while(wtot > boardWidth - 40) {
3954 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3958 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3960 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3961 XtSetValues(ma[i].subMenu, args, j);
3967 CreateButtonBar (MenuItem *mi)
3970 Widget button, buttonBar;
3974 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3976 XtSetArg(args[j], XtNhSpace, 0); j++;
3978 XtSetArg(args[j], XtNborderWidth, 0); j++;
3979 XtSetArg(args[j], XtNvSpace, 0); j++;
3980 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3981 formWidget, args, j);
3983 while (mi->string != NULL) {
3986 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3987 XtSetArg(args[j], XtNborderWidth, 0); j++;
3989 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3990 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3991 buttonBar, args, j);
3992 XtAddCallback(button, XtNcallback,
3993 (XtCallbackProc) MenuBarSelect,
3994 (caddr_t) mi->proc);
4001 CreatePieceMenu (char *name, int color)
4006 ChessSquare selection;
4008 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4009 boardWidget, args, 0);
4011 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4012 String item = pieceMenuStrings[color][i];
4014 if (strcmp(item, "----") == 0) {
4015 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4018 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4019 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4021 selection = pieceMenuTranslation[color][i];
4022 XtAddCallback(entry, XtNcallback,
4023 (XtCallbackProc) PieceMenuSelect,
4024 (caddr_t) selection);
4025 if (selection == WhitePawn || selection == BlackPawn) {
4026 XtSetArg(args[0], XtNpopupOnEntry, entry);
4027 XtSetValues(menu, args, 1);
4040 ChessSquare selection;
4042 whitePieceMenu = CreatePieceMenu("menuW", 0);
4043 blackPieceMenu = CreatePieceMenu("menuB", 1);
4045 XtRegisterGrabAction(PieceMenuPopup, True,
4046 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4047 GrabModeAsync, GrabModeAsync);
4049 XtSetArg(args[0], XtNlabel, _("Drop"));
4050 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4051 boardWidget, args, 1);
4052 for (i = 0; i < DROP_MENU_SIZE; i++) {
4053 String item = dropMenuStrings[i];
4055 if (strcmp(item, "----") == 0) {
4056 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4059 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4060 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4062 selection = dropMenuTranslation[i];
4063 XtAddCallback(entry, XtNcallback,
4064 (XtCallbackProc) DropMenuSelect,
4065 (caddr_t) selection);
4079 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4080 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4081 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4082 dmEnables[i].piece);
4083 XtSetSensitive(entry, p != NULL || !appData.testLegality
4084 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4085 && !appData.icsActive));
4087 while (p && *p++ == dmEnables[i].piece) count++;
4088 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4090 XtSetArg(args[j], XtNlabel, label); j++;
4091 XtSetValues(entry, args, j);
4096 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4098 String whichMenu; int menuNr = -2;
4099 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4100 if (event->type == ButtonRelease)
4101 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4102 else if (event->type == ButtonPress)
4103 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4105 case 0: whichMenu = params[0]; break;
4106 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4108 case -1: if (errorUp) ErrorPopDown();
4111 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4115 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4117 if (pmFromX < 0 || pmFromY < 0) return;
4118 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4122 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4124 if (pmFromX < 0 || pmFromY < 0) return;
4125 DropMenuEvent(piece, pmFromX, pmFromY);
4129 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4131 shiftKey = prms[0][0] & 1;
4136 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4138 shiftKey = prms[0][0] & 1;
4144 * If the user selects on a border boundary, return -1; if off the board,
4145 * return -2. Otherwise map the event coordinate to the square.
4148 EventToSquare (int x, int limit)
4155 if ((x % (squareSize + lineGap)) >= squareSize)
4157 x /= (squareSize + lineGap);
4164 do_flash_delay (unsigned long msec)
4170 drawHighlight (int file, int rank, GC gc)
4174 if (lineGap == 0) return;
4177 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4178 (squareSize + lineGap);
4179 y = lineGap/2 + rank * (squareSize + lineGap);
4181 x = lineGap/2 + file * (squareSize + lineGap);
4182 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4183 (squareSize + lineGap);
4186 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4187 squareSize+lineGap, squareSize+lineGap);
4190 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4191 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4194 SetHighlights (int fromX, int fromY, int toX, int toY)
4196 if (hi1X != fromX || hi1Y != fromY) {
4197 if (hi1X >= 0 && hi1Y >= 0) {
4198 drawHighlight(hi1X, hi1Y, lineGC);
4200 } // [HGM] first erase both, then draw new!
4201 if (hi2X != toX || hi2Y != toY) {
4202 if (hi2X >= 0 && hi2Y >= 0) {
4203 drawHighlight(hi2X, hi2Y, lineGC);
4206 if (hi1X != fromX || hi1Y != fromY) {
4207 if (fromX >= 0 && fromY >= 0) {
4208 drawHighlight(fromX, fromY, highlineGC);
4211 if (hi2X != toX || hi2Y != toY) {
4212 if (toX >= 0 && toY >= 0) {
4213 drawHighlight(toX, toY, highlineGC);
4225 SetHighlights(-1, -1, -1, -1);
4230 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4232 if (pm1X != fromX || pm1Y != fromY) {
4233 if (pm1X >= 0 && pm1Y >= 0) {
4234 drawHighlight(pm1X, pm1Y, lineGC);
4236 if (fromX >= 0 && fromY >= 0) {
4237 drawHighlight(fromX, fromY, prelineGC);
4240 if (pm2X != toX || pm2Y != toY) {
4241 if (pm2X >= 0 && pm2Y >= 0) {
4242 drawHighlight(pm2X, pm2Y, lineGC);
4244 if (toX >= 0 && toY >= 0) {
4245 drawHighlight(toX, toY, prelineGC);
4255 ClearPremoveHighlights ()
4257 SetPremoveHighlights(-1, -1, -1, -1);
4261 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4263 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4264 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4266 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4267 if(textureW[kind] < W*squareSize)
4268 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4270 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4271 if(textureH[kind] < H*squareSize)
4272 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4274 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4279 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4280 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4282 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4283 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4284 squareSize, squareSize, x*fac, y*fac);
4286 if (useImages && useImageSqs) {
4290 pm = xpmLightSquare;
4295 case 2: /* neutral */
4300 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4301 squareSize, squareSize, x*fac, y*fac);
4311 case 2: /* neutral */
4316 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4321 I split out the routines to draw a piece so that I could
4322 make a generic flash routine.
4325 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4327 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4328 switch (square_color) {
4330 case 2: /* neutral */
4332 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4333 ? *pieceToOutline(piece)
4334 : *pieceToSolid(piece),
4335 dest, bwPieceGC, 0, 0,
4336 squareSize, squareSize, x, y);
4339 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4340 ? *pieceToSolid(piece)
4341 : *pieceToOutline(piece),
4342 dest, wbPieceGC, 0, 0,
4343 squareSize, squareSize, x, y);
4349 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4351 switch (square_color) {
4353 case 2: /* neutral */
4355 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4356 ? *pieceToOutline(piece)
4357 : *pieceToSolid(piece),
4358 dest, bwPieceGC, 0, 0,
4359 squareSize, squareSize, x, y, 1);
4362 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4363 ? *pieceToSolid(piece)
4364 : *pieceToOutline(piece),
4365 dest, wbPieceGC, 0, 0,
4366 squareSize, squareSize, x, y, 1);
4372 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4374 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4375 switch (square_color) {
4377 XCopyPlane(xDisplay, *pieceToSolid(piece),
4378 dest, (int) piece < (int) BlackPawn
4379 ? wlPieceGC : blPieceGC, 0, 0,
4380 squareSize, squareSize, x, y, 1);
4383 XCopyPlane(xDisplay, *pieceToSolid(piece),
4384 dest, (int) piece < (int) BlackPawn
4385 ? wdPieceGC : bdPieceGC, 0, 0,
4386 squareSize, squareSize, x, y, 1);
4388 case 2: /* neutral */
4390 XCopyPlane(xDisplay, *pieceToSolid(piece),
4391 dest, (int) piece < (int) BlackPawn
4392 ? wjPieceGC : bjPieceGC, 0, 0,
4393 squareSize, squareSize, x, y, 1);
4399 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4401 int kind, p = piece;
4403 switch (square_color) {
4405 case 2: /* neutral */
4407 if ((int)piece < (int) BlackPawn) {
4415 if ((int)piece < (int) BlackPawn) {
4423 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4424 if(useTexture & square_color+1) {
4425 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4426 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4427 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4428 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4429 XSetClipMask(xDisplay, wlPieceGC, None);
4430 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4432 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4433 dest, wlPieceGC, 0, 0,
4434 squareSize, squareSize, x, y);
4437 typedef void (*DrawFunc)();
4442 if (appData.monoMode) {
4443 if (DefaultDepth(xDisplay, xScreen) == 1) {
4444 return monoDrawPiece_1bit;
4446 return monoDrawPiece;
4450 return colorDrawPieceImage;
4452 return colorDrawPiece;
4456 /* [HR] determine square color depending on chess variant. */
4458 SquareColor (int row, int column)
4462 if (gameInfo.variant == VariantXiangqi) {
4463 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4465 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4467 } else if (row <= 4) {
4473 square_color = ((column + row) % 2) == 1;
4476 /* [hgm] holdings: next line makes all holdings squares light */
4477 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4479 return square_color;
4483 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4485 int square_color, x, y, direction, font_ascent, font_descent;
4488 XCharStruct overall;
4492 /* Calculate delay in milliseconds (2-delays per complete flash) */
4493 flash_delay = 500 / appData.flashRate;
4496 x = lineGap + ((BOARD_WIDTH-1)-column) *
4497 (squareSize + lineGap);
4498 y = lineGap + row * (squareSize + lineGap);
4500 x = lineGap + column * (squareSize + lineGap);
4501 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4502 (squareSize + lineGap);
4505 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4507 square_color = SquareColor(row, column);
4509 if ( // [HGM] holdings: blank out area between board and holdings
4510 column == BOARD_LEFT-1 || column == BOARD_RGHT
4511 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4512 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4513 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4515 // [HGM] print piece counts next to holdings
4516 string[1] = NULLCHAR;
4517 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4518 string[0] = '0' + piece;
4519 XTextExtents(countFontStruct, string, 1, &direction,
4520 &font_ascent, &font_descent, &overall);
4521 if (appData.monoMode) {
4522 XDrawImageString(xDisplay, xBoardWindow, countGC,
4523 x + squareSize - overall.width - 2,
4524 y + font_ascent + 1, string, 1);
4526 XDrawString(xDisplay, xBoardWindow, countGC,
4527 x + squareSize - overall.width - 2,
4528 y + font_ascent + 1, string, 1);
4531 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4532 string[0] = '0' + piece;
4533 XTextExtents(countFontStruct, string, 1, &direction,
4534 &font_ascent, &font_descent, &overall);
4535 if (appData.monoMode) {
4536 XDrawImageString(xDisplay, xBoardWindow, countGC,
4537 x + 2, y + font_ascent + 1, string, 1);
4539 XDrawString(xDisplay, xBoardWindow, countGC,
4540 x + 2, y + font_ascent + 1, string, 1);
4544 if (piece == EmptySquare || appData.blindfold) {
4545 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4547 drawfunc = ChooseDrawFunc();
4549 if (do_flash && appData.flashCount > 0) {
4550 for (i=0; i<appData.flashCount; ++i) {
4551 drawfunc(piece, square_color, x, y, xBoardWindow);
4552 XSync(xDisplay, False);
4553 do_flash_delay(flash_delay);
4555 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4556 XSync(xDisplay, False);
4557 do_flash_delay(flash_delay);
4560 drawfunc(piece, square_color, x, y, xBoardWindow);
4564 string[1] = NULLCHAR;
4565 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4566 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4567 string[0] = 'a' + column - BOARD_LEFT;
4568 XTextExtents(coordFontStruct, string, 1, &direction,
4569 &font_ascent, &font_descent, &overall);
4570 if (appData.monoMode) {
4571 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4572 x + squareSize - overall.width - 2,
4573 y + squareSize - font_descent - 1, string, 1);
4575 XDrawString(xDisplay, xBoardWindow, coordGC,
4576 x + squareSize - overall.width - 2,
4577 y + squareSize - font_descent - 1, string, 1);
4580 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4581 string[0] = ONE + row;
4582 XTextExtents(coordFontStruct, string, 1, &direction,
4583 &font_ascent, &font_descent, &overall);
4584 if (appData.monoMode) {
4585 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4586 x + 2, y + font_ascent + 1, string, 1);
4588 XDrawString(xDisplay, xBoardWindow, coordGC,
4589 x + 2, y + font_ascent + 1, string, 1);
4592 if(!partnerUp && marker[row][column]) {
4593 if(appData.monoMode) {
4594 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4595 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4596 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4597 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4599 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4600 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4605 /* Why is this needed on some versions of X? */
4607 EventProc (Widget widget, caddr_t unused, XEvent *event)
4609 if (!XtIsRealized(widget))
4612 switch (event->type) {
4614 if (event->xexpose.count > 0) return; /* no clipping is done */
4615 XDrawPosition(widget, True, NULL);
4616 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4617 flipView = !flipView; partnerUp = !partnerUp;
4618 XDrawPosition(widget, True, NULL);
4619 flipView = !flipView; partnerUp = !partnerUp;
4623 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4631 DrawPosition (int fullRedraw, Board board)
4633 XDrawPosition(boardWidget, fullRedraw, board);
4636 /* Returns 1 if there are "too many" differences between b1 and b2
4637 (i.e. more than 1 move was made) */
4639 too_many_diffs (Board b1, Board b2)
4644 for (i=0; i<BOARD_HEIGHT; ++i) {
4645 for (j=0; j<BOARD_WIDTH; ++j) {
4646 if (b1[i][j] != b2[i][j]) {
4647 if (++c > 4) /* Castling causes 4 diffs */
4655 /* Matrix describing castling maneuvers */
4656 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4657 static int castling_matrix[4][5] = {
4658 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4659 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4660 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4661 { 7, 7, 4, 5, 6 } /* 0-0, black */
4664 /* Checks whether castling occurred. If it did, *rrow and *rcol
4665 are set to the destination (row,col) of the rook that moved.
4667 Returns 1 if castling occurred, 0 if not.
4669 Note: Only handles a max of 1 castling move, so be sure
4670 to call too_many_diffs() first.
4673 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4678 /* For each type of castling... */
4679 for (i=0; i<4; ++i) {
4680 r = castling_matrix[i];
4682 /* Check the 4 squares involved in the castling move */
4684 for (j=1; j<=4; ++j) {
4685 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4692 /* All 4 changed, so it must be a castling move */
4701 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4703 DrawSeekAxis (int x, int y, int xTo, int yTo)
4705 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4709 DrawSeekBackground (int left, int top, int right, int bottom)
4711 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4715 DrawSeekText (char *buf, int x, int y)
4717 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4721 DrawSeekDot (int x, int y, int colorNr)
4723 int square = colorNr & 0x80;
4726 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4728 XFillRectangle(xDisplay, xBoardWindow, color,
4729 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4731 XFillArc(xDisplay, xBoardWindow, color,
4732 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4735 static int damage[2][BOARD_RANKS][BOARD_FILES];
4738 * event handler for redrawing the board
4741 XDrawPosition (Widget w, int repaint, Board board)
4744 static int lastFlipView = 0;
4745 static int lastBoardValid[2] = {0, 0};
4746 static Board lastBoard[2];
4749 int nr = twoBoards*partnerUp;
4751 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4753 if (board == NULL) {
4754 if (!lastBoardValid[nr]) return;
4755 board = lastBoard[nr];
4757 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4758 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4759 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4764 * It would be simpler to clear the window with XClearWindow()
4765 * but this causes a very distracting flicker.
4768 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4770 if ( lineGap && IsDrawArrowEnabled())
4771 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4772 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4774 /* If too much changes (begin observing new game, etc.), don't
4776 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4778 /* Special check for castling so we don't flash both the king
4779 and the rook (just flash the king). */
4781 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4782 /* Draw rook with NO flashing. King will be drawn flashing later */
4783 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4784 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4788 /* First pass -- Draw (newly) empty squares and repair damage.
4789 This prevents you from having a piece show up twice while it
4790 is flashing on its new square */
4791 for (i = 0; i < BOARD_HEIGHT; i++)
4792 for (j = 0; j < BOARD_WIDTH; j++)
4793 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4794 || damage[nr][i][j]) {
4795 DrawSquare(i, j, board[i][j], 0);
4796 damage[nr][i][j] = False;
4799 /* Second pass -- Draw piece(s) in new position and flash them */
4800 for (i = 0; i < BOARD_HEIGHT; i++)
4801 for (j = 0; j < BOARD_WIDTH; j++)
4802 if (board[i][j] != lastBoard[nr][i][j]) {
4803 DrawSquare(i, j, board[i][j], do_flash);
4807 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4808 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4809 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4811 for (i = 0; i < BOARD_HEIGHT; i++)
4812 for (j = 0; j < BOARD_WIDTH; j++) {
4813 DrawSquare(i, j, board[i][j], 0);
4814 damage[nr][i][j] = False;
4818 CopyBoard(lastBoard[nr], board);
4819 lastBoardValid[nr] = 1;
4820 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4821 lastFlipView = flipView;
4823 /* Draw highlights */
4824 if (pm1X >= 0 && pm1Y >= 0) {
4825 drawHighlight(pm1X, pm1Y, prelineGC);
4827 if (pm2X >= 0 && pm2Y >= 0) {
4828 drawHighlight(pm2X, pm2Y, prelineGC);
4830 if (hi1X >= 0 && hi1Y >= 0) {
4831 drawHighlight(hi1X, hi1Y, highlineGC);
4833 if (hi2X >= 0 && hi2Y >= 0) {
4834 drawHighlight(hi2X, hi2Y, highlineGC);
4836 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4838 /* If piece being dragged around board, must redraw that too */
4841 XSync(xDisplay, False);
4846 * event handler for redrawing the board
4849 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4851 XDrawPosition(w, True, NULL);
4856 * event handler for parsing user moves
4858 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4859 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4860 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4861 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4862 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4863 // and at the end FinishMove() to perform the move after optional promotion popups.
4864 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4866 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4868 if (w != boardWidget || errorExitStatus != -1) return;
4869 if(nprms) shiftKey = !strcmp(prms[0], "1");
4872 if (event->type == ButtonPress) {
4873 XtPopdown(promotionShell);
4874 XtDestroyWidget(promotionShell);
4875 promotionUp = False;
4883 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4884 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4885 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4889 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4891 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4892 DragPieceMove(event->xmotion.x, event->xmotion.y);
4896 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4897 { // [HGM] pv: walk PV
4898 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4901 static int savedIndex; /* gross that this is global */
4904 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4907 XawTextPosition index, dummy;
4910 XawTextGetSelectionPos(w, &index, &dummy);
4911 XtSetArg(arg, XtNstring, &val);
4912 XtGetValues(w, &arg, 1);
4913 ReplaceComment(savedIndex, val);
4914 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4915 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4919 EditCommentPopUp (int index, char *title, char *text)
4922 if (text == NULL) text = "";
4923 NewCommentPopup(title, text, index);
4932 extern Option boxOptions[];
4942 edit = boxOptions[0].handle;
4944 XtSetArg(args[j], XtNstring, &val); j++;
4945 XtGetValues(edit, args, j);
4947 SendMultiLineToICS(val);
4948 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4949 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4953 ICSInputBoxPopDown ()
4959 CommentPopUp (char *title, char *text)
4961 savedIndex = currentMove; // [HGM] vari
4962 NewCommentPopup(title, text, currentMove);
4971 static char *openName;
4977 (void) (*fileProc)(openFP, 0, openName);
4981 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
4983 fileProc = proc; /* I can't see a way not */
4984 fileOpenMode = openMode; /* to use globals here */
4985 { // [HGM] use file-selector dialog stolen from Ghostview
4986 int index; // this is not supported yet
4987 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
4988 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
4989 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
4990 ScheduleDelayedEvent(&DelayedLoad, 50);
4997 if (!filenameUp) return;
4998 XtPopdown(fileNameShell);
4999 XtDestroyWidget(fileNameShell);
5005 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5010 XtSetArg(args[0], XtNlabel, &name);
5011 XtGetValues(w, args, 1);
5013 if (strcmp(name, _("cancel")) == 0) {
5018 FileNameAction(w, NULL, NULL, NULL);
5022 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5030 name = XawDialogGetValueString(w = XtParent(w));
5032 if ((name != NULL) && (*name != NULLCHAR)) {
5033 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5034 XtPopdown(w = XtParent(XtParent(w)));
5038 p = strrchr(buf, ' ');
5045 fullname = ExpandPathName(buf);
5047 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5050 f = fopen(fullname, fileOpenMode);
5052 DisplayError(_("Failed to open file"), errno);
5054 (void) (*fileProc)(f, index, buf);
5061 XtPopdown(w = XtParent(XtParent(w)));
5071 Widget dialog, layout;
5073 Dimension bw_width, pw_width;
5075 char *PromoChars = "wglcqrbnkac+=\0";
5078 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5079 XtGetValues(boardWidget, args, j);
5082 XtSetArg(args[j], XtNresizable, True); j++;
5083 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5085 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5086 shellWidget, args, j);
5088 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5089 layoutArgs, XtNumber(layoutArgs));
5092 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5093 XtSetArg(args[j], XtNborderWidth, 0); j++;
5094 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5097 if(gameInfo.variant != VariantShogi) {
5098 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5099 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5100 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5101 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5102 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5104 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5105 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5106 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5107 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5109 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5110 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5111 gameInfo.variant == VariantGiveaway) {
5112 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5114 if(gameInfo.variant == VariantCapablanca ||
5115 gameInfo.variant == VariantGothic ||
5116 gameInfo.variant == VariantCapaRandom) {
5117 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5118 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5120 } else // [HGM] shogi
5122 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5123 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5125 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5127 XtRealizeWidget(promotionShell);
5128 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5131 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5132 XtGetValues(promotionShell, args, j);
5134 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5135 lineGap + squareSize/3 +
5136 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5137 0 : 6*(squareSize + lineGap)), &x, &y);
5140 XtSetArg(args[j], XtNx, x); j++;
5141 XtSetArg(args[j], XtNy, y); j++;
5142 XtSetValues(promotionShell, args, j);
5144 XtPopup(promotionShell, XtGrabNone);
5152 if (!promotionUp) return;
5153 XtPopdown(promotionShell);
5154 XtDestroyWidget(promotionShell);
5155 promotionUp = False;
5159 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5161 int promoChar = * (const char *) client_data;
5165 if (fromX == -1) return;
5172 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5174 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5175 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5181 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5184 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5186 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5193 if (!errorUp) return;
5195 XtPopdown(errorShell);
5196 XtDestroyWidget(errorShell);
5197 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5201 ErrorPopUp (char *title, char *label, int modal)
5204 Widget dialog, layout;
5208 Dimension bw_width, pw_width;
5209 Dimension pw_height;
5213 XtSetArg(args[i], XtNresizable, True); i++;
5214 XtSetArg(args[i], XtNtitle, title); i++;
5216 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5217 shellWidget, args, i);
5219 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5220 layoutArgs, XtNumber(layoutArgs));
5223 XtSetArg(args[i], XtNlabel, label); i++;
5224 XtSetArg(args[i], XtNborderWidth, 0); i++;
5225 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5228 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5230 XtRealizeWidget(errorShell);
5231 CatchDeleteWindow(errorShell, "ErrorPopDown");
5234 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5235 XtGetValues(boardWidget, args, i);
5237 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5238 XtSetArg(args[i], XtNheight, &pw_height); i++;
5239 XtGetValues(errorShell, args, i);
5242 /* This code seems to tickle an X bug if it is executed too soon
5243 after xboard starts up. The coordinates get transformed as if
5244 the main window was positioned at (0, 0).
5246 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5247 0 - pw_height + squareSize / 3, &x, &y);
5249 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5250 RootWindowOfScreen(XtScreen(boardWidget)),
5251 (bw_width - pw_width) / 2,
5252 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5256 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5259 XtSetArg(args[i], XtNx, x); i++;
5260 XtSetArg(args[i], XtNy, y); i++;
5261 XtSetValues(errorShell, args, i);
5264 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5267 /* Disable all user input other than deleting the window */
5268 static int frozen = 0;
5274 /* Grab by a widget that doesn't accept input */
5275 XtAddGrab(messageWidget, TRUE, FALSE);
5279 /* Undo a FreezeUI */
5283 if (!frozen) return;
5284 XtRemoveGrab(messageWidget);
5289 ModeToWidgetName (GameMode mode)
5292 case BeginningOfGame:
5293 if (appData.icsActive)
5294 return "menuMode.ICS Client";
5295 else if (appData.noChessProgram ||
5296 *appData.cmailGameName != NULLCHAR)
5297 return "menuMode.Edit Game";
5299 return "menuMode.Machine Black";
5300 case MachinePlaysBlack:
5301 return "menuMode.Machine Black";
5302 case MachinePlaysWhite:
5303 return "menuMode.Machine White";
5305 return "menuMode.Analysis Mode";
5307 return "menuMode.Analyze File";
5308 case TwoMachinesPlay:
5309 return "menuMode.Two Machines";
5311 return "menuMode.Edit Game";
5312 case PlayFromGameFile:
5313 return "menuFile.Load Game";
5315 return "menuMode.Edit Position";
5317 return "menuMode.Training";
5318 case IcsPlayingWhite:
5319 case IcsPlayingBlack:
5323 return "menuMode.ICS Client";
5334 static int oldPausing = FALSE;
5335 static GameMode oldmode = (GameMode) -1;
5338 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5340 if (pausing != oldPausing) {
5341 oldPausing = pausing;
5343 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5345 XtSetArg(args[0], XtNleftBitmap, None);
5347 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5350 if (appData.showButtonBar) {
5351 /* Always toggle, don't set. Previous code messes up when
5352 invoked while the button is pressed, as releasing it
5353 toggles the state again. */
5356 XtSetArg(args[0], XtNbackground, &oldbg);
5357 XtSetArg(args[1], XtNforeground, &oldfg);
5358 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5360 XtSetArg(args[0], XtNbackground, oldfg);
5361 XtSetArg(args[1], XtNforeground, oldbg);
5363 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5367 wname = ModeToWidgetName(oldmode);
5368 if (wname != NULL) {
5369 XtSetArg(args[0], XtNleftBitmap, None);
5370 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5372 wname = ModeToWidgetName(gameMode);
5373 if (wname != NULL) {
5374 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5375 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5378 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5379 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5381 /* Maybe all the enables should be handled here, not just this one */
5382 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5383 gameMode == Training || gameMode == PlayFromGameFile);
5388 * Button/menu procedures
5391 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5397 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5399 cmailMsgLoaded = FALSE;
5400 if (gameNumber == 0) {
5401 int error = GameListBuild(f);
5403 DisplayError(_("Cannot build game list"), error);
5404 } else if (!ListEmpty(&gameList) &&
5405 ((ListGame *) gameList.tailPred)->number > 1) {
5406 GameListPopUp(f, title);
5412 return LoadGame(f, gameNumber, title, FALSE);
5416 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5418 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5421 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5425 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5431 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5437 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5443 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5449 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5455 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5461 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5463 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5466 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5470 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5472 FileNamePopUp(_("Save game file name?"),
5473 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5474 appData.oldSaveStyle ? ".game" : ".pgn",
5479 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5481 FileNamePopUp(_("Save position file name?"),
5482 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5483 appData.oldSaveStyle ? ".pos" : ".fen",
5488 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5490 ReloadCmailMsgEvent(FALSE);
5494 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5499 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5500 char *selected_fen_position=NULL;
5503 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5504 Atom *type_return, XtPointer *value_return,
5505 unsigned long *length_return, int *format_return)
5507 char *selection_tmp;
5509 if (!selected_fen_position) return False; /* should never happen */
5510 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5511 /* note: since no XtSelectionDoneProc was registered, Xt will
5512 * automatically call XtFree on the value returned. So have to
5513 * make a copy of it allocated with XtMalloc */
5514 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5515 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5517 *value_return=selection_tmp;
5518 *length_return=strlen(selection_tmp);
5519 *type_return=*target;
5520 *format_return = 8; /* bits per byte */
5522 } else if (*target == XA_TARGETS(xDisplay)) {
5523 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5524 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5525 targets_tmp[1] = XA_STRING;
5526 *value_return = targets_tmp;
5527 *type_return = XA_ATOM;
5530 // This code leads to a read of value_return out of bounds on 64-bit systems.
5531 // Other code which I have seen always sets *format_return to 32 independent of
5532 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5533 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5534 *format_return = 8 * sizeof(Atom);
5535 if (*format_return > 32) {
5536 *length_return *= *format_return / 32;
5537 *format_return = 32;
5540 *format_return = 32;
5548 /* note: when called from menu all parameters are NULL, so no clue what the
5549 * Widget which was clicked on was, or what the click event was
5552 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5555 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5556 * have a notion of a position that is selected but not copied.
5557 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5559 if(gameMode == EditPosition) EditPositionDone(TRUE);
5560 if (selected_fen_position) free(selected_fen_position);
5561 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5562 if (!selected_fen_position) return;
5563 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5565 SendPositionSelection,
5566 NULL/* lose_ownership_proc */ ,
5567 NULL/* transfer_done_proc */);
5568 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5570 SendPositionSelection,
5571 NULL/* lose_ownership_proc */ ,
5572 NULL/* transfer_done_proc */);
5575 /* function called when the data to Paste is ready */
5577 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5578 Atom *type, XtPointer value, unsigned long *len, int *format)
5581 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5582 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5583 EditPositionPasteFEN(fenstr);
5587 /* called when Paste Position button is pressed,
5588 * all parameters will be NULL */
5589 void PastePositionProc(w, event, prms, nprms)
5595 XtGetSelectionValue(menuBarWidget,
5596 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5597 /* (XtSelectionCallbackProc) */ PastePositionCB,
5598 NULL, /* client_data passed to PastePositionCB */
5600 /* better to use the time field from the event that triggered the
5601 * call to this function, but that isn't trivial to get
5609 SendGameSelection (Widget w, Atom *selection, Atom *target,
5610 Atom *type_return, XtPointer *value_return,
5611 unsigned long *length_return, int *format_return)
5613 char *selection_tmp;
5615 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5616 FILE* f = fopen(gameCopyFilename, "r");
5619 if (f == NULL) return False;
5623 selection_tmp = XtMalloc(len + 1);
5624 count = fread(selection_tmp, 1, len, f);
5627 XtFree(selection_tmp);
5630 selection_tmp[len] = NULLCHAR;
5631 *value_return = selection_tmp;
5632 *length_return = len;
5633 *type_return = *target;
5634 *format_return = 8; /* bits per byte */
5636 } else if (*target == XA_TARGETS(xDisplay)) {
5637 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5638 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5639 targets_tmp[1] = XA_STRING;
5640 *value_return = targets_tmp;
5641 *type_return = XA_ATOM;
5644 // This code leads to a read of value_return out of bounds on 64-bit systems.
5645 // Other code which I have seen always sets *format_return to 32 independent of
5646 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5647 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5648 *format_return = 8 * sizeof(Atom);
5649 if (*format_return > 32) {
5650 *length_return *= *format_return / 32;
5651 *format_return = 32;
5654 *format_return = 32;
5666 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5667 * have a notion of a game that is selected but not copied.
5668 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5670 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5673 NULL/* lose_ownership_proc */ ,
5674 NULL/* transfer_done_proc */);
5675 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5678 NULL/* lose_ownership_proc */ ,
5679 NULL/* transfer_done_proc */);
5682 /* note: when called from menu all parameters are NULL, so no clue what the
5683 * Widget which was clicked on was, or what the click event was
5686 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5690 ret = SaveGameToFile(gameCopyFilename, FALSE);
5697 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5699 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5703 /* function called when the data to Paste is ready */
5705 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5706 Atom *type, XtPointer value, unsigned long *len, int *format)
5709 if (value == NULL || *len == 0) {
5710 return; /* nothing had been selected to copy */
5712 f = fopen(gamePasteFilename, "w");
5714 DisplayError(_("Can't open temp file"), errno);
5717 fwrite(value, 1, *len, f);
5720 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5723 /* called when Paste Game button is pressed,
5724 * all parameters will be NULL */
5726 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5728 XtGetSelectionValue(menuBarWidget,
5729 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5730 /* (XtSelectionCallbackProc) */ PasteGameCB,
5731 NULL, /* client_data passed to PasteGameCB */
5733 /* better to use the time field from the event that triggered the
5734 * call to this function, but that isn't trivial to get
5745 SaveGameProc(NULL, NULL, NULL, NULL);
5750 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5756 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5762 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5764 MachineBlackEvent();
5768 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5770 MachineWhiteEvent();
5774 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5778 if (!first.analysisSupport) {
5779 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5780 DisplayError(buf, 0);
5783 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5784 if (appData.icsActive) {
5785 if (gameMode != IcsObserving) {
5786 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5787 DisplayError(buf, 0);
5789 if (appData.icsEngineAnalyze) {
5790 if (appData.debugMode)
5791 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5797 /* if enable, use want disable icsEngineAnalyze */
5798 if (appData.icsEngineAnalyze) {
5803 appData.icsEngineAnalyze = TRUE;
5804 if (appData.debugMode)
5805 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5807 #ifndef OPTIONSDIALOG
5808 if (!appData.showThinking)
5809 ShowThinkingProc(w,event,prms,nprms);
5816 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5818 if (!first.analysisSupport) {
5820 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5821 DisplayError(buf, 0);
5824 // Reset(FALSE, TRUE);
5825 #ifndef OPTIONSDIALOG
5826 if (!appData.showThinking)
5827 ShowThinkingProc(w,event,prms,nprms);
5830 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5831 AnalysisPeriodicEvent(1);
5835 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5841 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5847 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5853 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5859 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5861 EditPositionEvent();
5865 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5871 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5875 if (PopDown(1)) { // popdown succesful
5877 XtSetArg(args[j], XtNleftBitmap, None); j++;
5878 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5879 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5880 } else // was not up
5885 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5887 if (!PopDown(4)) ICSInputBoxPopUp();
5891 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5897 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5903 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5909 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5915 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5921 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5927 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5933 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5939 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5941 UserAdjudicationEvent(+1);
5945 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5947 UserAdjudicationEvent(-1);
5951 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5953 UserAdjudicationEvent(0);
5957 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5959 if (shellUp[4] == True)
5964 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5965 { // [HGM] input: let up-arrow recall previous line from history
5972 if (!shellUp[4]) return;
5973 edit = boxOptions[0].handle;
5975 XtSetArg(args[j], XtNstring, &val); j++;
5976 XtGetValues(edit, args, j);
5977 val = PrevInHistory(val);
5978 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5979 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5981 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
5982 XawTextReplace(edit, 0, 0, &t);
5983 XawTextSetInsertionPoint(edit, 9999);
5988 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5989 { // [HGM] input: let down-arrow recall next line from history
5994 if (!shellUp[4]) return;
5995 edit = boxOptions[0].handle;
5996 val = NextInHistory();
5997 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5998 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6000 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6001 XawTextReplace(edit, 0, 0, &t);
6002 XawTextSetInsertionPoint(edit, 9999);
6007 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6009 StopObservingEvent();
6013 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6015 StopExaminingEvent();
6019 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6026 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6033 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6039 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6041 if (!TempBackwardActive) {
6042 TempBackwardActive = True;
6048 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6050 /* Check to see if triggered by a key release event for a repeating key.
6051 * If so the next queued event will be a key press of the same key at the same time */
6052 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6054 XPeekEvent(xDisplay, &next);
6055 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6056 next.xkey.keycode == event->xkey.keycode)
6060 TempBackwardActive = False;
6064 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6070 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6076 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6082 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6088 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6090 TruncateGameEvent();
6094 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6100 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6106 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6108 flipView = !flipView;
6109 DrawPosition(True, NULL);
6113 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6117 PonderNextMoveEvent(!appData.ponderNextMove);
6118 #ifndef OPTIONSDIALOG
6119 if (appData.ponderNextMove) {
6120 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6122 XtSetArg(args[0], XtNleftBitmap, None);
6124 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6129 #ifndef OPTIONSDIALOG
6131 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6135 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6137 if (appData.alwaysPromoteToQueen) {
6138 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6140 XtSetArg(args[0], XtNleftBitmap, None);
6142 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6147 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6151 appData.animateDragging = !appData.animateDragging;
6153 if (appData.animateDragging) {
6154 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6157 XtSetArg(args[0], XtNleftBitmap, None);
6159 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6164 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6168 appData.animate = !appData.animate;
6170 if (appData.animate) {
6171 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6174 XtSetArg(args[0], XtNleftBitmap, None);
6176 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6181 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6185 appData.autoCallFlag = !appData.autoCallFlag;
6187 if (appData.autoCallFlag) {
6188 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6190 XtSetArg(args[0], XtNleftBitmap, None);
6192 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6197 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6201 appData.autoFlipView = !appData.autoFlipView;
6203 if (appData.autoFlipView) {
6204 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6206 XtSetArg(args[0], XtNleftBitmap, None);
6208 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6213 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6217 appData.blindfold = !appData.blindfold;
6219 if (appData.blindfold) {
6220 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6222 XtSetArg(args[0], XtNleftBitmap, None);
6224 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6227 DrawPosition(True, NULL);
6231 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6235 appData.testLegality = !appData.testLegality;
6237 if (appData.testLegality) {
6238 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6240 XtSetArg(args[0], XtNleftBitmap, None);
6242 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6248 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6252 if (appData.flashCount == 0) {
6253 appData.flashCount = 3;
6255 appData.flashCount = -appData.flashCount;
6258 if (appData.flashCount > 0) {
6259 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6261 XtSetArg(args[0], XtNleftBitmap, None);
6263 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6269 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6273 appData.highlightDragging = !appData.highlightDragging;
6275 if (appData.highlightDragging) {
6276 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6278 XtSetArg(args[0], XtNleftBitmap, None);
6280 XtSetValues(XtNameToWidget(menuBarWidget,
6281 "menuOptions.Highlight Dragging"), args, 1);
6286 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6290 appData.highlightLastMove = !appData.highlightLastMove;
6292 if (appData.highlightLastMove) {
6293 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6295 XtSetArg(args[0], XtNleftBitmap, None);
6297 XtSetValues(XtNameToWidget(menuBarWidget,
6298 "menuOptions.Highlight Last Move"), args, 1);
6302 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6306 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6308 if (appData.highlightMoveWithArrow) {
6309 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6311 XtSetArg(args[0], XtNleftBitmap, None);
6313 XtSetValues(XtNameToWidget(menuBarWidget,
6314 "menuOptions.Arrow"), args, 1);
6319 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6323 appData.icsAlarm = !appData.icsAlarm;
6325 if (appData.icsAlarm) {
6326 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6328 XtSetArg(args[0], XtNleftBitmap, None);
6330 XtSetValues(XtNameToWidget(menuBarWidget,
6331 "menuOptions.ICS Alarm"), args, 1);
6336 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6340 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6342 if (appData.ringBellAfterMoves) {
6343 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6345 XtSetArg(args[0], XtNleftBitmap, None);
6347 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6352 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6356 appData.oneClick = !appData.oneClick;
6358 if (appData.oneClick) {
6359 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6361 XtSetArg(args[0], XtNleftBitmap, None);
6363 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6368 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6372 PeriodicUpdatesEvent(!appData.periodicUpdates);
6374 if (appData.periodicUpdates) {
6375 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6377 XtSetArg(args[0], XtNleftBitmap, None);
6379 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6384 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6388 appData.popupExitMessage = !appData.popupExitMessage;
6390 if (appData.popupExitMessage) {
6391 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6393 XtSetArg(args[0], XtNleftBitmap, None);
6395 XtSetValues(XtNameToWidget(menuBarWidget,
6396 "menuOptions.Popup Exit Message"), args, 1);
6400 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6404 appData.popupMoveErrors = !appData.popupMoveErrors;
6406 if (appData.popupMoveErrors) {
6407 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6409 XtSetArg(args[0], XtNleftBitmap, None);
6411 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6417 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6421 appData.premove = !appData.premove;
6423 if (appData.premove) {
6424 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6426 XtSetArg(args[0], XtNleftBitmap, None);
6428 XtSetValues(XtNameToWidget(menuBarWidget,
6429 "menuOptions.Premove"), args, 1);
6434 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6438 appData.showCoords = !appData.showCoords;
6440 if (appData.showCoords) {
6441 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6443 XtSetArg(args[0], XtNleftBitmap, None);
6445 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6448 DrawPosition(True, NULL);
6452 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6454 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6455 ShowThinkingEvent();
6459 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6463 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6464 ShowThinkingEvent();
6466 if (appData.hideThinkingFromHuman) {
6467 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6469 XtSetArg(args[0], XtNleftBitmap, None);
6471 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6477 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6481 saveSettingsOnExit = !saveSettingsOnExit;
6483 if (saveSettingsOnExit) {
6484 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6486 XtSetArg(args[0], XtNleftBitmap, None);
6488 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6493 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6495 SaveSettings(settingsFileName);
6499 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6502 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6508 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6512 if (nprms && *nprms > 0)
6516 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6521 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6527 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6533 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6537 char *zippy = _(" (with Zippy code)");
6541 snprintf(buf, sizeof(buf),
6543 "Copyright 1991 Digital Equipment Corporation\n"
6544 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6545 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6546 "%s is free software and carries NO WARRANTY;"
6547 "see the file COPYING for more information."),
6548 programVersion, zippy, PACKAGE);
6549 ErrorPopUp(_("About XBoard"), buf, FALSE);
6553 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6555 appData.debugMode = !appData.debugMode;
6559 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6565 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6571 DisplayMessage (char *message, char *extMessage)
6573 /* display a message in the message widget */
6582 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6587 message = extMessage;
6591 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6593 /* need to test if messageWidget already exists, since this function
6594 can also be called during the startup, if for example a Xresource
6595 is not set up correctly */
6598 XtSetArg(arg, XtNlabel, message);
6599 XtSetValues(messageWidget, &arg, 1);
6606 DisplayTitle (char *text)
6610 char title[MSG_SIZ];
6613 if (text == NULL) text = "";
6615 if (appData.titleInWindow) {
6617 XtSetArg(args[i], XtNlabel, text); i++;
6618 XtSetValues(titleWidget, args, i);
6621 if (*text != NULLCHAR) {
6622 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6623 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6624 } else if (appData.icsActive) {
6625 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6626 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6627 } else if (appData.cmailGameName[0] != NULLCHAR) {
6628 snprintf(icon, sizeof(icon), "%s", "CMail");
6629 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6631 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6632 } else if (gameInfo.variant == VariantGothic) {
6633 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6634 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6637 } else if (gameInfo.variant == VariantFalcon) {
6638 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6639 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6641 } else if (appData.noChessProgram) {
6642 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6643 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6645 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6646 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6649 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6650 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6651 XtSetValues(shellWidget, args, i);
6652 XSync(xDisplay, False);
6657 DisplayError (String message, int error)
6662 if (appData.debugMode || appData.matchMode) {
6663 fprintf(stderr, "%s: %s\n", programName, message);
6666 if (appData.debugMode || appData.matchMode) {
6667 fprintf(stderr, "%s: %s: %s\n",
6668 programName, message, strerror(error));
6670 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6673 ErrorPopUp(_("Error"), message, FALSE);
6678 DisplayMoveError (String message)
6682 DrawPosition(FALSE, NULL);
6683 if (appData.debugMode || appData.matchMode) {
6684 fprintf(stderr, "%s: %s\n", programName, message);
6686 if (appData.popupMoveErrors) {
6687 ErrorPopUp(_("Error"), message, FALSE);
6689 DisplayMessage(message, "");
6695 DisplayFatalError (String message, int error, int status)
6699 errorExitStatus = status;
6701 fprintf(stderr, "%s: %s\n", programName, message);
6703 fprintf(stderr, "%s: %s: %s\n",
6704 programName, message, strerror(error));
6705 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6708 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6709 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6716 DisplayInformation (String message)
6719 ErrorPopUp(_("Information"), message, TRUE);
6723 DisplayNote (String message)
6726 ErrorPopUp(_("Note"), message, FALSE);
6730 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6736 DisplayIcsInteractionTitle (String message)
6738 if (oldICSInteractionTitle == NULL) {
6739 /* Magic to find the old window title, adapted from vim */
6740 char *wina = getenv("WINDOWID");
6742 Window win = (Window) atoi(wina);
6743 Window root, parent, *children;
6744 unsigned int nchildren;
6745 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6747 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6748 if (!XQueryTree(xDisplay, win, &root, &parent,
6749 &children, &nchildren)) break;
6750 if (children) XFree((void *)children);
6751 if (parent == root || parent == 0) break;
6754 XSetErrorHandler(oldHandler);
6756 if (oldICSInteractionTitle == NULL) {
6757 oldICSInteractionTitle = "xterm";
6760 printf("\033]0;%s\007", message);
6764 char pendingReplyPrefix[MSG_SIZ];
6765 ProcRef pendingReplyPR;
6768 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6771 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6775 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6779 AskQuestionPopDown ()
6781 if (!askQuestionUp) return;
6782 XtPopdown(askQuestionShell);
6783 XtDestroyWidget(askQuestionShell);
6784 askQuestionUp = False;
6788 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6794 reply = XawDialogGetValueString(w = XtParent(w));
6795 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6796 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6797 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6798 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6799 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6800 AskQuestionPopDown();
6802 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6806 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6811 XtSetArg(args[0], XtNlabel, &name);
6812 XtGetValues(w, args, 1);
6814 if (strcmp(name, _("cancel")) == 0) {
6815 AskQuestionPopDown();
6817 AskQuestionReplyAction(w, NULL, NULL, NULL);
6822 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6825 Widget popup, layout, dialog, edit;
6831 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6832 pendingReplyPR = pr;
6835 XtSetArg(args[i], XtNresizable, True); i++;
6836 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6837 askQuestionShell = popup =
6838 XtCreatePopupShell(title, transientShellWidgetClass,
6839 shellWidget, args, i);
6842 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6843 layoutArgs, XtNumber(layoutArgs));
6846 XtSetArg(args[i], XtNlabel, question); i++;
6847 XtSetArg(args[i], XtNvalue, ""); i++;
6848 XtSetArg(args[i], XtNborderWidth, 0); i++;
6849 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6852 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6853 (XtPointer) dialog);
6854 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6855 (XtPointer) dialog);
6857 XtRealizeWidget(popup);
6858 CatchDeleteWindow(popup, "AskQuestionPopDown");
6860 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6861 &x, &y, &win_x, &win_y, &mask);
6863 XtSetArg(args[0], XtNx, x - 10);
6864 XtSetArg(args[1], XtNy, y - 30);
6865 XtSetValues(popup, args, 2);
6867 XtPopup(popup, XtGrabExclusive);
6868 askQuestionUp = True;
6870 edit = XtNameToWidget(dialog, "*value");
6871 XtSetKeyboardFocus(popup, edit);
6876 PlaySound (char *name)
6878 if (*name == NULLCHAR) {
6880 } else if (strcmp(name, "$") == 0) {
6881 putc(BELLCHAR, stderr);
6884 char *prefix = "", *sep = "";
6885 if(appData.soundProgram[0] == NULLCHAR) return;
6886 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6887 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6895 PlaySound(appData.soundMove);
6901 PlaySound(appData.soundIcsWin);
6907 PlaySound(appData.soundIcsLoss);
6913 PlaySound(appData.soundIcsDraw);
6917 PlayIcsUnfinishedSound ()
6919 PlaySound(appData.soundIcsUnfinished);
6925 PlaySound(appData.soundIcsAlarm);
6931 PlaySound(appData.soundTell);
6937 system("stty echo");
6944 system("stty -echo");
6949 RunCommand (char *buf)
6955 Colorize (ColorClass cc, int continuation)
6958 int count, outCount, error;
6960 if (textColors[(int)cc].bg > 0) {
6961 if (textColors[(int)cc].fg > 0) {
6962 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
6963 textColors[(int)cc].fg, textColors[(int)cc].bg);
6965 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
6966 textColors[(int)cc].bg);
6969 if (textColors[(int)cc].fg > 0) {
6970 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
6971 textColors[(int)cc].fg);
6973 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
6976 count = strlen(buf);
6977 outCount = OutputToProcess(NoProc, buf, count, &error);
6978 if (outCount < count) {
6979 DisplayFatalError(_("Error writing to display"), error, 1);
6982 if (continuation) return;
6985 PlaySound(appData.soundShout);
6988 PlaySound(appData.soundSShout);
6991 PlaySound(appData.soundChannel1);
6994 PlaySound(appData.soundChannel);
6997 PlaySound(appData.soundKibitz);
7000 PlaySound(appData.soundTell);
7002 case ColorChallenge:
7003 PlaySound(appData.soundChallenge);
7006 PlaySound(appData.soundRequest);
7009 PlaySound(appData.soundSeek);
7021 return getpwuid(getuid())->pw_name;
7025 ExpandPathName (char *path)
7027 static char static_buf[4*MSG_SIZ];
7028 char *d, *s, buf[4*MSG_SIZ];
7034 while (*s && isspace(*s))
7043 if (*(s+1) == '/') {
7044 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7048 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7049 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7050 pwd = getpwnam(buf);
7053 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7057 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7058 strcat(d, strchr(s+1, '/'));
7062 safeStrCpy(d, s, 4*MSG_SIZ );
7070 static char host_name[MSG_SIZ];
7072 #if HAVE_GETHOSTNAME
7073 gethostname(host_name, MSG_SIZ);
7075 #else /* not HAVE_GETHOSTNAME */
7076 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7077 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7079 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7081 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7082 #endif /* not HAVE_GETHOSTNAME */
7085 XtIntervalId delayedEventTimerXID = 0;
7086 DelayedEventCallback delayedEventCallback = 0;
7091 delayedEventTimerXID = 0;
7092 delayedEventCallback();
7096 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7098 if(delayedEventTimerXID && delayedEventCallback == cb)
7099 // [HGM] alive: replace, rather than add or flush identical event
7100 XtRemoveTimeOut(delayedEventTimerXID);
7101 delayedEventCallback = cb;
7102 delayedEventTimerXID =
7103 XtAppAddTimeOut(appContext, millisec,
7104 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7107 DelayedEventCallback
7110 if (delayedEventTimerXID) {
7111 return delayedEventCallback;
7118 CancelDelayedEvent ()
7120 if (delayedEventTimerXID) {
7121 XtRemoveTimeOut(delayedEventTimerXID);
7122 delayedEventTimerXID = 0;
7126 XtIntervalId loadGameTimerXID = 0;
7129 LoadGameTimerRunning ()
7131 return loadGameTimerXID != 0;
7135 StopLoadGameTimer ()
7137 if (loadGameTimerXID != 0) {
7138 XtRemoveTimeOut(loadGameTimerXID);
7139 loadGameTimerXID = 0;
7147 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7149 loadGameTimerXID = 0;
7154 StartLoadGameTimer (long millisec)
7157 XtAppAddTimeOut(appContext, millisec,
7158 (XtTimerCallbackProc) LoadGameTimerCallback,
7162 XtIntervalId analysisClockXID = 0;
7165 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7167 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7168 || appData.icsEngineAnalyze) { // [DM]
7169 AnalysisPeriodicEvent(0);
7170 StartAnalysisClock();
7175 StartAnalysisClock ()
7178 XtAppAddTimeOut(appContext, 2000,
7179 (XtTimerCallbackProc) AnalysisClockCallback,
7183 XtIntervalId clockTimerXID = 0;
7186 ClockTimerRunning ()
7188 return clockTimerXID != 0;
7194 if (clockTimerXID != 0) {
7195 XtRemoveTimeOut(clockTimerXID);
7204 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7211 StartClockTimer (long millisec)
7214 XtAppAddTimeOut(appContext, millisec,
7215 (XtTimerCallbackProc) ClockTimerCallback,
7220 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7225 /* check for low time warning */
7226 Pixel foregroundOrWarningColor = timerForegroundPixel;
7229 appData.lowTimeWarning &&
7230 (timer / 1000) < appData.icsAlarmTime)
7231 foregroundOrWarningColor = lowTimeWarningColor;
7233 if (appData.clockMode) {
7234 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7235 XtSetArg(args[0], XtNlabel, buf);
7237 snprintf(buf, MSG_SIZ, "%s ", color);
7238 XtSetArg(args[0], XtNlabel, buf);
7243 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7244 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7246 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7247 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7250 XtSetValues(w, args, 3);
7254 DisplayWhiteClock (long timeRemaining, int highlight)
7258 if(appData.noGUI) return;
7259 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7260 if (highlight && iconPixmap == bIconPixmap) {
7261 iconPixmap = wIconPixmap;
7262 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7263 XtSetValues(shellWidget, args, 1);
7268 DisplayBlackClock (long timeRemaining, int highlight)
7272 if(appData.noGUI) return;
7273 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7274 if (highlight && iconPixmap == wIconPixmap) {
7275 iconPixmap = bIconPixmap;
7276 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7277 XtSetValues(shellWidget, args, 1);
7296 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7300 int to_prog[2], from_prog[2];
7304 if (appData.debugMode) {
7305 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7308 /* We do NOT feed the cmdLine to the shell; we just
7309 parse it into blank-separated arguments in the
7310 most simple-minded way possible.
7313 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7316 while(*p == ' ') p++;
7318 if(*p == '"' || *p == '\'')
7319 p = strchr(++argv[i-1], *p);
7320 else p = strchr(p, ' ');
7321 if (p == NULL) break;
7326 SetUpChildIO(to_prog, from_prog);
7328 if ((pid = fork()) == 0) {
7330 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7331 close(to_prog[1]); // first close the unused pipe ends
7332 close(from_prog[0]);
7333 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7334 dup2(from_prog[1], 1);
7335 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7336 close(from_prog[1]); // and closing again loses one of the pipes!
7337 if(fileno(stderr) >= 2) // better safe than sorry...
7338 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7340 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7345 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7347 execvp(argv[0], argv);
7349 /* If we get here, exec failed */
7354 /* Parent process */
7356 close(from_prog[1]);
7358 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7361 cp->fdFrom = from_prog[0];
7362 cp->fdTo = to_prog[1];
7367 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7369 AlarmCallBack (int n)
7375 DestroyChildProcess (ProcRef pr, int signalType)
7377 ChildProc *cp = (ChildProc *) pr;
7379 if (cp->kind != CPReal) return;
7381 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7382 signal(SIGALRM, AlarmCallBack);
7384 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7385 kill(cp->pid, SIGKILL); // kill it forcefully
7386 wait((int *) 0); // and wait again
7390 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7392 /* Process is exiting either because of the kill or because of
7393 a quit command sent by the backend; either way, wait for it to die.
7402 InterruptChildProcess (ProcRef pr)
7404 ChildProc *cp = (ChildProc *) pr;
7406 if (cp->kind != CPReal) return;
7407 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7411 OpenTelnet (char *host, char *port, ProcRef *pr)
7413 char cmdLine[MSG_SIZ];
7415 if (port[0] == NULLCHAR) {
7416 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7418 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7420 return StartChildProcess(cmdLine, "", pr);
7424 OpenTCP (char *host, char *port, ProcRef *pr)
7427 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7428 #else /* !OMIT_SOCKETS */
7429 struct addrinfo hints;
7430 struct addrinfo *ais, *ai;
7435 memset(&hints, 0, sizeof(hints));
7436 hints.ai_family = AF_UNSPEC;
7437 hints.ai_socktype = SOCK_STREAM;
7439 error = getaddrinfo(host, port, &hints, &ais);
7441 /* a getaddrinfo error is not an errno, so can't return it */
7442 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7443 host, port, gai_strerror(error));
7447 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7448 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7452 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7465 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7471 #endif /* !OMIT_SOCKETS */
7477 OpenCommPort (char *name, ProcRef *pr)
7482 fd = open(name, 2, 0);
7483 if (fd < 0) return errno;
7485 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7496 OpenLoopback (ProcRef *pr)
7501 SetUpChildIO(to, from);
7503 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7506 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7514 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7516 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7520 #define INPUT_SOURCE_BUF_SIZE 8192
7529 char buf[INPUT_SOURCE_BUF_SIZE];
7534 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7536 InputSource *is = (InputSource *) closure;
7541 if (is->lineByLine) {
7542 count = read(is->fd, is->unused,
7543 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7545 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7548 is->unused += count;
7550 while (p < is->unused) {
7551 q = memchr(p, '\n', is->unused - p);
7552 if (q == NULL) break;
7554 (is->func)(is, is->closure, p, q - p, 0);
7558 while (p < is->unused) {
7563 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7568 (is->func)(is, is->closure, is->buf, count, error);
7573 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7576 ChildProc *cp = (ChildProc *) pr;
7578 is = (InputSource *) calloc(1, sizeof(InputSource));
7579 is->lineByLine = lineByLine;
7583 is->fd = fileno(stdin);
7585 is->kind = cp->kind;
7586 is->fd = cp->fdFrom;
7589 is->unused = is->buf;
7592 is->xid = XtAppAddInput(appContext, is->fd,
7593 (XtPointer) (XtInputReadMask),
7594 (XtInputCallbackProc) DoInputCallback,
7596 is->closure = closure;
7597 return (InputSourceRef) is;
7601 RemoveInputSource (InputSourceRef isr)
7603 InputSource *is = (InputSource *) isr;
7605 if (is->xid == 0) return;
7606 XtRemoveInput(is->xid);
7611 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7613 static int line = 0;
7614 ChildProc *cp = (ChildProc *) pr;
7619 if (appData.noJoin || !appData.useInternalWrap)
7620 outCount = fwrite(message, 1, count, stdout);
7623 int width = get_term_width();
7624 int len = wrap(NULL, message, count, width, &line);
7625 char *msg = malloc(len);
7629 outCount = fwrite(message, 1, count, stdout);
7632 dbgchk = wrap(msg, message, count, width, &line);
7633 if (dbgchk != len && appData.debugMode)
7634 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7635 outCount = fwrite(msg, 1, dbgchk, stdout);
7641 outCount = write(cp->fdTo, message, count);
7651 /* Output message to process, with "ms" milliseconds of delay
7652 between each character. This is needed when sending the logon
7653 script to ICC, which for some reason doesn't like the
7654 instantaneous send. */
7656 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7658 ChildProc *cp = (ChildProc *) pr;
7663 r = write(cp->fdTo, message++, 1);
7676 /**** Animation code by Hugh Fisher, DCS, ANU.
7678 Known problem: if a window overlapping the board is
7679 moved away while a piece is being animated underneath,
7680 the newly exposed area won't be updated properly.
7681 I can live with this.
7683 Known problem: if you look carefully at the animation
7684 of pieces in mono mode, they are being drawn as solid
7685 shapes without interior detail while moving. Fixing
7686 this would be a major complication for minimal return.
7689 /* Masks for XPM pieces. Black and white pieces can have
7690 different shapes, but in the interest of retaining my
7691 sanity pieces must have the same outline on both light
7692 and dark squares, and all pieces must use the same
7693 background square colors/images. */
7695 static int xpmDone = 0;
7698 CreateAnimMasks (int pieceDepth)
7704 unsigned long plane;
7707 /* Need a bitmap just to get a GC with right depth */
7708 buf = XCreatePixmap(xDisplay, xBoardWindow,
7710 values.foreground = 1;
7711 values.background = 0;
7712 /* Don't use XtGetGC, not read only */
7713 maskGC = XCreateGC(xDisplay, buf,
7714 GCForeground | GCBackground, &values);
7715 XFreePixmap(xDisplay, buf);
7717 buf = XCreatePixmap(xDisplay, xBoardWindow,
7718 squareSize, squareSize, pieceDepth);
7719 values.foreground = XBlackPixel(xDisplay, xScreen);
7720 values.background = XWhitePixel(xDisplay, xScreen);
7721 bufGC = XCreateGC(xDisplay, buf,
7722 GCForeground | GCBackground, &values);
7724 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7725 /* Begin with empty mask */
7726 if(!xpmDone) // [HGM] pieces: keep using existing
7727 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7728 squareSize, squareSize, 1);
7729 XSetFunction(xDisplay, maskGC, GXclear);
7730 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7731 0, 0, squareSize, squareSize);
7733 /* Take a copy of the piece */
7738 XSetFunction(xDisplay, bufGC, GXcopy);
7739 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7741 0, 0, squareSize, squareSize, 0, 0);
7743 /* XOR the background (light) over the piece */
7744 XSetFunction(xDisplay, bufGC, GXxor);
7746 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7747 0, 0, squareSize, squareSize, 0, 0);
7749 XSetForeground(xDisplay, bufGC, lightSquareColor);
7750 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7753 /* We now have an inverted piece image with the background
7754 erased. Construct mask by just selecting all the non-zero
7755 pixels - no need to reconstruct the original image. */
7756 XSetFunction(xDisplay, maskGC, GXor);
7758 /* Might be quicker to download an XImage and create bitmap
7759 data from it rather than this N copies per piece, but it
7760 only takes a fraction of a second and there is a much
7761 longer delay for loading the pieces. */
7762 for (n = 0; n < pieceDepth; n ++) {
7763 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7764 0, 0, squareSize, squareSize,
7770 XFreePixmap(xDisplay, buf);
7771 XFreeGC(xDisplay, bufGC);
7772 XFreeGC(xDisplay, maskGC);
7776 InitAnimState (AnimState *anim, XWindowAttributes *info)
7781 /* Each buffer is square size, same depth as window */
7782 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7783 squareSize, squareSize, info->depth);
7784 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7785 squareSize, squareSize, info->depth);
7787 /* Create a plain GC for blitting */
7788 mask = GCForeground | GCBackground | GCFunction |
7789 GCPlaneMask | GCGraphicsExposures;
7790 values.foreground = XBlackPixel(xDisplay, xScreen);
7791 values.background = XWhitePixel(xDisplay, xScreen);
7792 values.function = GXcopy;
7793 values.plane_mask = AllPlanes;
7794 values.graphics_exposures = False;
7795 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7797 /* Piece will be copied from an existing context at
7798 the start of each new animation/drag. */
7799 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7801 /* Outline will be a read-only copy of an existing */
7802 anim->outlineGC = None;
7808 XWindowAttributes info;
7810 if (xpmDone && gameInfo.variant == oldVariant) return;
7811 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7812 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7814 InitAnimState(&game, &info);
7815 InitAnimState(&player, &info);
7817 /* For XPM pieces, we need bitmaps to use as masks. */
7819 CreateAnimMasks(info.depth), xpmDone = 1;
7824 static Boolean frameWaiting;
7827 FrameAlarm (int sig)
7829 frameWaiting = False;
7830 /* In case System-V style signals. Needed?? */
7831 signal(SIGALRM, FrameAlarm);
7835 FrameDelay (int time)
7837 struct itimerval delay;
7839 XSync(xDisplay, False);
7842 frameWaiting = True;
7843 signal(SIGALRM, FrameAlarm);
7844 delay.it_interval.tv_sec =
7845 delay.it_value.tv_sec = time / 1000;
7846 delay.it_interval.tv_usec =
7847 delay.it_value.tv_usec = (time % 1000) * 1000;
7848 setitimer(ITIMER_REAL, &delay, NULL);
7849 while (frameWaiting) pause();
7850 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7851 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7852 setitimer(ITIMER_REAL, &delay, NULL);
7859 FrameDelay (int time)
7861 XSync(xDisplay, False);
7863 usleep(time * 1000);
7874 /* Convert board position to corner of screen rect and color */
7877 ScreenSquare (int column, int row, XPoint *pt, int *color)
7880 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7881 pt->y = lineGap + row * (squareSize + lineGap);
7883 pt->x = lineGap + column * (squareSize + lineGap);
7884 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7886 *color = SquareColor(row, column);
7889 /* Convert window coords to square */
7892 BoardSquare (int x, int y, int *column, int *row)
7894 *column = EventToSquare(x, BOARD_WIDTH);
7895 if (flipView && *column >= 0)
7896 *column = BOARD_WIDTH - 1 - *column;
7897 *row = EventToSquare(y, BOARD_HEIGHT);
7898 if (!flipView && *row >= 0)
7899 *row = BOARD_HEIGHT - 1 - *row;
7904 #undef Max /* just in case */
7906 #define Max(a, b) ((a) > (b) ? (a) : (b))
7907 #define Min(a, b) ((a) < (b) ? (a) : (b))
7910 SetRect (XRectangle *rect, int x, int y, int width, int height)
7914 rect->width = width;
7915 rect->height = height;
7918 /* Test if two frames overlap. If they do, return
7919 intersection rect within old and location of
7920 that rect within new. */
7923 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
7925 if (old->x > new->x + size || new->x > old->x + size ||
7926 old->y > new->y + size || new->y > old->y + size) {
7929 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7930 size - abs(old->x - new->x), size - abs(old->y - new->y));
7931 pt->x = Max(old->x - new->x, 0);
7932 pt->y = Max(old->y - new->y, 0);
7937 /* For two overlapping frames, return the rect(s)
7938 in the old that do not intersect with the new. */
7941 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
7945 /* If old = new (shouldn't happen) then nothing to draw */
7946 if (old->x == new->x && old->y == new->y) {
7950 /* Work out what bits overlap. Since we know the rects
7951 are the same size we don't need a full intersect calc. */
7953 /* Top or bottom edge? */
7954 if (new->y > old->y) {
7955 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
7957 } else if (old->y > new->y) {
7958 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
7959 size, old->y - new->y);
7962 /* Left or right edge - don't overlap any update calculated above. */
7963 if (new->x > old->x) {
7964 SetRect(&(update[count]), old->x, Max(new->y, old->y),
7965 new->x - old->x, size - abs(new->y - old->y));
7967 } else if (old->x > new->x) {
7968 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
7969 old->x - new->x, size - abs(new->y - old->y));
7976 /* Generate a series of frame coords from start->mid->finish.
7977 The movement rate doubles until the half way point is
7978 reached, then halves back down to the final destination,
7979 which gives a nice slow in/out effect. The algorithmn
7980 may seem to generate too many intermediates for short
7981 moves, but remember that the purpose is to attract the
7982 viewers attention to the piece about to be moved and
7983 then to where it ends up. Too few frames would be less
7987 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
7989 int fraction, n, count;
7993 /* Slow in, stepping 1/16th, then 1/8th, ... */
7995 for (n = 0; n < factor; n++)
7997 for (n = 0; n < factor; n++) {
7998 frames[count].x = start->x + (mid->x - start->x) / fraction;
7999 frames[count].y = start->y + (mid->y - start->y) / fraction;
8001 fraction = fraction / 2;
8005 frames[count] = *mid;
8008 /* Slow out, stepping 1/2, then 1/4, ... */
8010 for (n = 0; n < factor; n++) {
8011 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8012 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8014 fraction = fraction * 2;
8019 /* Draw a piece on the screen without disturbing what's there */
8022 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8026 /* Bitmap for piece being moved. */
8027 if (appData.monoMode) {
8028 *mask = *pieceToSolid(piece);
8029 } else if (useImages) {
8031 *mask = xpmMask[piece];
8033 *mask = ximMaskPm[piece];
8036 *mask = *pieceToSolid(piece);
8039 /* GC for piece being moved. Square color doesn't matter, but
8040 since it gets modified we make a copy of the original. */
8042 if (appData.monoMode)
8047 if (appData.monoMode)
8052 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8054 /* Outline only used in mono mode and is not modified */
8056 *outline = bwPieceGC;
8058 *outline = wbPieceGC;
8062 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8067 /* Draw solid rectangle which will be clipped to shape of piece */
8068 XFillRectangle(xDisplay, dest, clip,
8069 0, 0, squareSize, squareSize);
8070 if (appData.monoMode)
8071 /* Also draw outline in contrasting color for black
8072 on black / white on white cases */
8073 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8074 0, 0, squareSize, squareSize, 0, 0, 1);
8076 /* Copy the piece */
8081 if(appData.upsideDown && flipView) kind ^= 2;
8082 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8084 0, 0, squareSize, squareSize,
8089 /* Animate the movement of a single piece */
8092 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8096 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8097 /* The old buffer is initialised with the start square (empty) */
8098 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8099 anim->prevFrame = *start;
8101 /* The piece will be drawn using its own bitmap as a matte */
8102 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8103 XSetClipMask(xDisplay, anim->pieceGC, mask);
8107 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8109 XRectangle updates[4];
8114 /* Save what we are about to draw into the new buffer */
8115 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8116 frame->x, frame->y, squareSize, squareSize,
8119 /* Erase bits of the previous frame */
8120 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8121 /* Where the new frame overlapped the previous,
8122 the contents in newBuf are wrong. */
8123 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8124 overlap.x, overlap.y,
8125 overlap.width, overlap.height,
8127 /* Repaint the areas in the old that don't overlap new */
8128 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8129 for (i = 0; i < count; i++)
8130 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8131 updates[i].x - anim->prevFrame.x,
8132 updates[i].y - anim->prevFrame.y,
8133 updates[i].width, updates[i].height,
8134 updates[i].x, updates[i].y);
8136 /* Easy when no overlap */
8137 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8138 0, 0, squareSize, squareSize,
8139 anim->prevFrame.x, anim->prevFrame.y);
8142 /* Save this frame for next time round */
8143 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8144 0, 0, squareSize, squareSize,
8146 anim->prevFrame = *frame;
8148 /* Draw piece over original screen contents, not current,
8149 and copy entire rect. Wipes out overlapping piece images. */
8150 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8151 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8152 0, 0, squareSize, squareSize,
8153 frame->x, frame->y);
8157 EndAnimation (AnimState *anim, XPoint *finish)
8159 XRectangle updates[4];
8164 /* The main code will redraw the final square, so we
8165 only need to erase the bits that don't overlap. */
8166 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8167 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8168 for (i = 0; i < count; i++)
8169 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8170 updates[i].x - anim->prevFrame.x,
8171 updates[i].y - anim->prevFrame.y,
8172 updates[i].width, updates[i].height,
8173 updates[i].x, updates[i].y);
8175 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8176 0, 0, squareSize, squareSize,
8177 anim->prevFrame.x, anim->prevFrame.y);
8182 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8186 BeginAnimation(anim, piece, startColor, start);
8187 for (n = 0; n < nFrames; n++) {
8188 AnimationFrame(anim, &(frames[n]), piece);
8189 FrameDelay(appData.animSpeed);
8191 EndAnimation(anim, finish);
8195 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8198 ChessSquare piece = board[fromY][toY];
8199 board[fromY][toY] = EmptySquare;
8200 DrawPosition(FALSE, board);
8202 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8203 y = lineGap + toY * (squareSize + lineGap);
8205 x = lineGap + toX * (squareSize + lineGap);
8206 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8208 for(i=1; i<4*kFactor; i++) {
8209 int r = squareSize * 9 * i/(20*kFactor - 5);
8210 XFillArc(xDisplay, xBoardWindow, highlineGC,
8211 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8212 FrameDelay(appData.animSpeed);
8214 board[fromY][toY] = piece;
8217 /* Main control logic for deciding what to animate and how */
8220 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8224 XPoint start, finish, mid;
8225 XPoint frames[kFactor * 2 + 1];
8226 int nFrames, startColor, endColor;
8228 /* Are we animating? */
8229 if (!appData.animate || appData.blindfold)
8232 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8233 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8234 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8236 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8237 piece = board[fromY][fromX];
8238 if (piece >= EmptySquare) return;
8243 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8246 if (appData.debugMode) {
8247 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8248 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8249 piece, fromX, fromY, toX, toY); }
8251 ScreenSquare(fromX, fromY, &start, &startColor);
8252 ScreenSquare(toX, toY, &finish, &endColor);
8255 /* Knight: make straight movement then diagonal */
8256 if (abs(toY - fromY) < abs(toX - fromX)) {
8257 mid.x = start.x + (finish.x - start.x) / 2;
8261 mid.y = start.y + (finish.y - start.y) / 2;
8264 mid.x = start.x + (finish.x - start.x) / 2;
8265 mid.y = start.y + (finish.y - start.y) / 2;
8268 /* Don't use as many frames for very short moves */
8269 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8270 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8272 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8273 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8274 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8276 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8277 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8280 /* Be sure end square is redrawn */
8281 damage[0][toY][toX] = True;
8285 DragPieceBegin (int x, int y, Boolean instantly)
8287 int boardX, boardY, color;
8290 /* Are we animating? */
8291 if (!appData.animateDragging || appData.blindfold)
8294 /* Figure out which square we start in and the
8295 mouse position relative to top left corner. */
8296 BoardSquare(x, y, &boardX, &boardY);
8297 player.startBoardX = boardX;
8298 player.startBoardY = boardY;
8299 ScreenSquare(boardX, boardY, &corner, &color);
8300 player.startSquare = corner;
8301 player.startColor = color;
8302 /* As soon as we start dragging, the piece will jump slightly to
8303 be centered over the mouse pointer. */
8304 player.mouseDelta.x = squareSize/2;
8305 player.mouseDelta.y = squareSize/2;
8306 /* Initialise animation */
8307 player.dragPiece = PieceForSquare(boardX, boardY);
8309 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8310 player.dragActive = True;
8311 BeginAnimation(&player, player.dragPiece, color, &corner);
8312 /* Mark this square as needing to be redrawn. Note that
8313 we don't remove the piece though, since logically (ie
8314 as seen by opponent) the move hasn't been made yet. */
8315 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8316 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8317 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8318 corner.x, corner.y, squareSize, squareSize,
8319 0, 0); // [HGM] zh: unstack in stead of grab
8320 if(gatingPiece != EmptySquare) {
8321 /* Kludge alert: When gating we want the introduced
8322 piece to appear on the from square. To generate an
8323 image of it, we draw it on the board, copy the image,
8324 and draw the original piece again. */
8325 ChessSquare piece = boards[currentMove][boardY][boardX];
8326 DrawSquare(boardY, boardX, gatingPiece, 0);
8327 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8328 corner.x, corner.y, squareSize, squareSize, 0, 0);
8329 DrawSquare(boardY, boardX, piece, 0);
8331 damage[0][boardY][boardX] = True;
8333 player.dragActive = False;
8338 ChangeDragPiece (ChessSquare piece)
8341 player.dragPiece = piece;
8342 /* The piece will be drawn using its own bitmap as a matte */
8343 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8344 XSetClipMask(xDisplay, player.pieceGC, mask);
8348 DragPieceMove (int x, int y)
8352 /* Are we animating? */
8353 if (!appData.animateDragging || appData.blindfold)
8357 if (! player.dragActive)
8359 /* Move piece, maintaining same relative position
8360 of mouse within square */
8361 corner.x = x - player.mouseDelta.x;
8362 corner.y = y - player.mouseDelta.y;
8363 AnimationFrame(&player, &corner, player.dragPiece);
8365 if (appData.highlightDragging) {
8367 BoardSquare(x, y, &boardX, &boardY);
8368 SetHighlights(fromX, fromY, boardX, boardY);
8374 DragPieceEnd (int x, int y)
8376 int boardX, boardY, color;
8379 /* Are we animating? */
8380 if (!appData.animateDragging || appData.blindfold)
8384 if (! player.dragActive)
8386 /* Last frame in sequence is square piece is
8387 placed on, which may not match mouse exactly. */
8388 BoardSquare(x, y, &boardX, &boardY);
8389 ScreenSquare(boardX, boardY, &corner, &color);
8390 EndAnimation(&player, &corner);
8392 /* Be sure end square is redrawn */
8393 damage[0][boardY][boardX] = True;
8395 /* This prevents weird things happening with fast successive
8396 clicks which on my Sun at least can cause motion events
8397 without corresponding press/release. */
8398 player.dragActive = False;
8401 /* Handle expose event while piece being dragged */
8406 if (!player.dragActive || appData.blindfold)
8409 /* What we're doing: logically, the move hasn't been made yet,
8410 so the piece is still in it's original square. But visually
8411 it's being dragged around the board. So we erase the square
8412 that the piece is on and draw it at the last known drag point. */
8413 BlankSquare(player.startSquare.x, player.startSquare.y,
8414 player.startColor, EmptySquare, xBoardWindow, 1);
8415 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8416 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8419 #include <sys/ioctl.h>
8423 int fd, default_width;
8426 default_width = 79; // this is FICS default anyway...
8428 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8430 if (!ioctl(fd, TIOCGSIZE, &win))
8431 default_width = win.ts_cols;
8432 #elif defined(TIOCGWINSZ)
8434 if (!ioctl(fd, TIOCGWINSZ, &win))
8435 default_width = win.ws_col;
8437 return default_width;
8443 static int old_width = 0;
8444 int new_width = get_term_width();
8446 if (old_width != new_width)
8447 ics_printf("set width %d\n", new_width);
8448 old_width = new_width;
8452 NotifyFrontendLogin ()
8457 /* [AS] Arrow highlighting support */
8459 static double A_WIDTH = 5; /* Width of arrow body */
8461 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8462 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8473 return (int) (x + 0.5);
8477 SquareToPos (int rank, int file, int *x, int *y)
8480 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8481 *y = lineGap + rank * (squareSize + lineGap);
8483 *x = lineGap + file * (squareSize + lineGap);
8484 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8488 /* Draw an arrow between two points using current settings */
8490 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8493 double dx, dy, j, k, x, y;
8496 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8498 arrow[0].x = s_x + A_WIDTH + 0.5;
8501 arrow[1].x = s_x + A_WIDTH + 0.5;
8502 arrow[1].y = d_y - h;
8504 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8505 arrow[2].y = d_y - h;
8510 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8511 arrow[5].y = d_y - h;
8513 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8514 arrow[4].y = d_y - h;
8516 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8519 else if( d_y == s_y ) {
8520 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8523 arrow[0].y = s_y + A_WIDTH + 0.5;
8525 arrow[1].x = d_x - w;
8526 arrow[1].y = s_y + A_WIDTH + 0.5;
8528 arrow[2].x = d_x - w;
8529 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8534 arrow[5].x = d_x - w;
8535 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8537 arrow[4].x = d_x - w;
8538 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8541 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8544 /* [AS] Needed a lot of paper for this! :-) */
8545 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8546 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8548 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8550 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8555 arrow[0].x = Round(x - j);
8556 arrow[0].y = Round(y + j*dx);
8558 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8559 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8562 x = (double) d_x - k;
8563 y = (double) d_y - k*dy;
8566 x = (double) d_x + k;
8567 y = (double) d_y + k*dy;
8570 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8572 arrow[6].x = Round(x - j);
8573 arrow[6].y = Round(y + j*dx);
8575 arrow[2].x = Round(arrow[6].x + 2*j);
8576 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8578 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8579 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8584 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8585 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8588 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8589 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8590 // Polygon( hdc, arrow, 7 );
8593 /* [AS] Draw an arrow between two squares */
8595 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8597 int s_x, s_y, d_x, d_y, hor, vert, i;
8599 if( s_col == d_col && s_row == d_row ) {
8603 /* Get source and destination points */
8604 SquareToPos( s_row, s_col, &s_x, &s_y);
8605 SquareToPos( d_row, d_col, &d_x, &d_y);
8608 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8610 else if( d_y < s_y ) {
8611 d_y += squareSize / 2 + squareSize / 4;
8614 d_y += squareSize / 2;
8618 d_x += squareSize / 2 - squareSize / 4;
8620 else if( d_x < s_x ) {
8621 d_x += squareSize / 2 + squareSize / 4;
8624 d_x += squareSize / 2;
8627 s_x += squareSize / 2;
8628 s_y += squareSize / 2;
8631 A_WIDTH = squareSize / 14.; //[HGM] make float
8633 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8635 hor = 64*s_col + 32; vert = 64*s_row + 32;
8636 for(i=0; i<= 64; i++) {
8637 damage[0][vert+6>>6][hor+6>>6] = True;
8638 damage[0][vert-6>>6][hor+6>>6] = True;
8639 damage[0][vert+6>>6][hor-6>>6] = True;
8640 damage[0][vert-6>>6][hor-6>>6] = True;
8641 hor += d_col - s_col; vert += d_row - s_row;
8646 IsDrawArrowEnabled ()
8648 return appData.highlightMoveWithArrow && squareSize >= 32;
8652 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8654 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8655 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8659 UpdateLogos (int displ)
8661 return; // no logos in XBoard yet