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", EditTagsProc },
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);
4216 if(toX<0) // clearing the highlights must have damaged arrow
4217 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4227 SetHighlights(-1, -1, -1, -1);
4232 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4234 if (pm1X != fromX || pm1Y != fromY) {
4235 if (pm1X >= 0 && pm1Y >= 0) {
4236 drawHighlight(pm1X, pm1Y, lineGC);
4238 if (fromX >= 0 && fromY >= 0) {
4239 drawHighlight(fromX, fromY, prelineGC);
4242 if (pm2X != toX || pm2Y != toY) {
4243 if (pm2X >= 0 && pm2Y >= 0) {
4244 drawHighlight(pm2X, pm2Y, lineGC);
4246 if (toX >= 0 && toY >= 0) {
4247 drawHighlight(toX, toY, prelineGC);
4257 ClearPremoveHighlights ()
4259 SetPremoveHighlights(-1, -1, -1, -1);
4263 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4265 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4266 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4268 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4269 if(textureW[kind] < W*squareSize)
4270 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4272 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4273 if(textureH[kind] < H*squareSize)
4274 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4276 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4281 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4282 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4284 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4285 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4286 squareSize, squareSize, x*fac, y*fac);
4288 if (useImages && useImageSqs) {
4292 pm = xpmLightSquare;
4297 case 2: /* neutral */
4302 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4303 squareSize, squareSize, x*fac, y*fac);
4313 case 2: /* neutral */
4318 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4323 I split out the routines to draw a piece so that I could
4324 make a generic flash routine.
4327 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4329 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4330 switch (square_color) {
4332 case 2: /* neutral */
4334 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4335 ? *pieceToOutline(piece)
4336 : *pieceToSolid(piece),
4337 dest, bwPieceGC, 0, 0,
4338 squareSize, squareSize, x, y);
4341 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4342 ? *pieceToSolid(piece)
4343 : *pieceToOutline(piece),
4344 dest, wbPieceGC, 0, 0,
4345 squareSize, squareSize, x, y);
4351 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4353 switch (square_color) {
4355 case 2: /* neutral */
4357 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4358 ? *pieceToOutline(piece)
4359 : *pieceToSolid(piece),
4360 dest, bwPieceGC, 0, 0,
4361 squareSize, squareSize, x, y, 1);
4364 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4365 ? *pieceToSolid(piece)
4366 : *pieceToOutline(piece),
4367 dest, wbPieceGC, 0, 0,
4368 squareSize, squareSize, x, y, 1);
4374 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4376 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4377 switch (square_color) {
4379 XCopyPlane(xDisplay, *pieceToSolid(piece),
4380 dest, (int) piece < (int) BlackPawn
4381 ? wlPieceGC : blPieceGC, 0, 0,
4382 squareSize, squareSize, x, y, 1);
4385 XCopyPlane(xDisplay, *pieceToSolid(piece),
4386 dest, (int) piece < (int) BlackPawn
4387 ? wdPieceGC : bdPieceGC, 0, 0,
4388 squareSize, squareSize, x, y, 1);
4390 case 2: /* neutral */
4392 XCopyPlane(xDisplay, *pieceToSolid(piece),
4393 dest, (int) piece < (int) BlackPawn
4394 ? wjPieceGC : bjPieceGC, 0, 0,
4395 squareSize, squareSize, x, y, 1);
4401 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4403 int kind, p = piece;
4405 switch (square_color) {
4407 case 2: /* neutral */
4409 if ((int)piece < (int) BlackPawn) {
4417 if ((int)piece < (int) BlackPawn) {
4425 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4426 if(useTexture & square_color+1) {
4427 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4428 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4429 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4430 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4431 XSetClipMask(xDisplay, wlPieceGC, None);
4432 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4434 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4435 dest, wlPieceGC, 0, 0,
4436 squareSize, squareSize, x, y);
4439 typedef void (*DrawFunc)();
4444 if (appData.monoMode) {
4445 if (DefaultDepth(xDisplay, xScreen) == 1) {
4446 return monoDrawPiece_1bit;
4448 return monoDrawPiece;
4452 return colorDrawPieceImage;
4454 return colorDrawPiece;
4458 /* [HR] determine square color depending on chess variant. */
4460 SquareColor (int row, int column)
4464 if (gameInfo.variant == VariantXiangqi) {
4465 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4467 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4469 } else if (row <= 4) {
4475 square_color = ((column + row) % 2) == 1;
4478 /* [hgm] holdings: next line makes all holdings squares light */
4479 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4481 return square_color;
4485 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4487 int square_color, x, y, direction, font_ascent, font_descent;
4490 XCharStruct overall;
4494 /* Calculate delay in milliseconds (2-delays per complete flash) */
4495 flash_delay = 500 / appData.flashRate;
4498 x = lineGap + ((BOARD_WIDTH-1)-column) *
4499 (squareSize + lineGap);
4500 y = lineGap + row * (squareSize + lineGap);
4502 x = lineGap + column * (squareSize + lineGap);
4503 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4504 (squareSize + lineGap);
4507 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4509 square_color = SquareColor(row, column);
4511 if ( // [HGM] holdings: blank out area between board and holdings
4512 column == BOARD_LEFT-1 || column == BOARD_RGHT
4513 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4514 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4515 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4517 // [HGM] print piece counts next to holdings
4518 string[1] = NULLCHAR;
4519 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4520 string[0] = '0' + piece;
4521 XTextExtents(countFontStruct, string, 1, &direction,
4522 &font_ascent, &font_descent, &overall);
4523 if (appData.monoMode) {
4524 XDrawImageString(xDisplay, xBoardWindow, countGC,
4525 x + squareSize - overall.width - 2,
4526 y + font_ascent + 1, string, 1);
4528 XDrawString(xDisplay, xBoardWindow, countGC,
4529 x + squareSize - overall.width - 2,
4530 y + font_ascent + 1, string, 1);
4533 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4534 string[0] = '0' + piece;
4535 XTextExtents(countFontStruct, string, 1, &direction,
4536 &font_ascent, &font_descent, &overall);
4537 if (appData.monoMode) {
4538 XDrawImageString(xDisplay, xBoardWindow, countGC,
4539 x + 2, y + font_ascent + 1, string, 1);
4541 XDrawString(xDisplay, xBoardWindow, countGC,
4542 x + 2, y + font_ascent + 1, string, 1);
4546 if (piece == EmptySquare || appData.blindfold) {
4547 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4549 drawfunc = ChooseDrawFunc();
4551 if (do_flash && appData.flashCount > 0) {
4552 for (i=0; i<appData.flashCount; ++i) {
4553 drawfunc(piece, square_color, x, y, xBoardWindow);
4554 XSync(xDisplay, False);
4555 do_flash_delay(flash_delay);
4557 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4558 XSync(xDisplay, False);
4559 do_flash_delay(flash_delay);
4562 drawfunc(piece, square_color, x, y, xBoardWindow);
4566 string[1] = NULLCHAR;
4567 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4568 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4569 string[0] = 'a' + column - BOARD_LEFT;
4570 XTextExtents(coordFontStruct, string, 1, &direction,
4571 &font_ascent, &font_descent, &overall);
4572 if (appData.monoMode) {
4573 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4574 x + squareSize - overall.width - 2,
4575 y + squareSize - font_descent - 1, string, 1);
4577 XDrawString(xDisplay, xBoardWindow, coordGC,
4578 x + squareSize - overall.width - 2,
4579 y + squareSize - font_descent - 1, string, 1);
4582 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4583 string[0] = ONE + row;
4584 XTextExtents(coordFontStruct, string, 1, &direction,
4585 &font_ascent, &font_descent, &overall);
4586 if (appData.monoMode) {
4587 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4588 x + 2, y + font_ascent + 1, string, 1);
4590 XDrawString(xDisplay, xBoardWindow, coordGC,
4591 x + 2, y + font_ascent + 1, string, 1);
4594 if(!partnerUp && marker[row][column]) {
4595 if(appData.monoMode) {
4596 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4597 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4598 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4599 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4601 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4602 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4607 /* Why is this needed on some versions of X? */
4609 EventProc (Widget widget, caddr_t unused, XEvent *event)
4611 if (!XtIsRealized(widget))
4614 switch (event->type) {
4616 if (event->xexpose.count > 0) return; /* no clipping is done */
4617 XDrawPosition(widget, True, NULL);
4618 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4619 flipView = !flipView; partnerUp = !partnerUp;
4620 XDrawPosition(widget, True, NULL);
4621 flipView = !flipView; partnerUp = !partnerUp;
4625 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4633 DrawPosition (int fullRedraw, Board board)
4635 XDrawPosition(boardWidget, fullRedraw, board);
4638 /* Returns 1 if there are "too many" differences between b1 and b2
4639 (i.e. more than 1 move was made) */
4641 too_many_diffs (Board b1, Board b2)
4646 for (i=0; i<BOARD_HEIGHT; ++i) {
4647 for (j=0; j<BOARD_WIDTH; ++j) {
4648 if (b1[i][j] != b2[i][j]) {
4649 if (++c > 4) /* Castling causes 4 diffs */
4657 /* Matrix describing castling maneuvers */
4658 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4659 static int castling_matrix[4][5] = {
4660 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4661 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4662 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4663 { 7, 7, 4, 5, 6 } /* 0-0, black */
4666 /* Checks whether castling occurred. If it did, *rrow and *rcol
4667 are set to the destination (row,col) of the rook that moved.
4669 Returns 1 if castling occurred, 0 if not.
4671 Note: Only handles a max of 1 castling move, so be sure
4672 to call too_many_diffs() first.
4675 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4680 /* For each type of castling... */
4681 for (i=0; i<4; ++i) {
4682 r = castling_matrix[i];
4684 /* Check the 4 squares involved in the castling move */
4686 for (j=1; j<=4; ++j) {
4687 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4694 /* All 4 changed, so it must be a castling move */
4703 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4705 DrawSeekAxis (int x, int y, int xTo, int yTo)
4707 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4711 DrawSeekBackground (int left, int top, int right, int bottom)
4713 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4717 DrawSeekText (char *buf, int x, int y)
4719 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4723 DrawSeekDot (int x, int y, int colorNr)
4725 int square = colorNr & 0x80;
4728 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4730 XFillRectangle(xDisplay, xBoardWindow, color,
4731 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4733 XFillArc(xDisplay, xBoardWindow, color,
4734 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4737 static int damage[2][BOARD_RANKS][BOARD_FILES];
4740 * event handler for redrawing the board
4743 XDrawPosition (Widget w, int repaint, Board board)
4746 static int lastFlipView = 0;
4747 static int lastBoardValid[2] = {0, 0};
4748 static Board lastBoard[2];
4751 int nr = twoBoards*partnerUp;
4753 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4755 if (board == NULL) {
4756 if (!lastBoardValid[nr]) return;
4757 board = lastBoard[nr];
4759 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4760 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4761 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4766 * It would be simpler to clear the window with XClearWindow()
4767 * but this causes a very distracting flicker.
4770 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4772 if ( lineGap && IsDrawArrowEnabled())
4773 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4774 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4776 /* If too much changes (begin observing new game, etc.), don't
4778 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4780 /* Special check for castling so we don't flash both the king
4781 and the rook (just flash the king). */
4783 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4784 /* Draw rook with NO flashing. King will be drawn flashing later */
4785 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4786 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4790 /* First pass -- Draw (newly) empty squares and repair damage.
4791 This prevents you from having a piece show up twice while it
4792 is flashing on its new square */
4793 for (i = 0; i < BOARD_HEIGHT; i++)
4794 for (j = 0; j < BOARD_WIDTH; j++)
4795 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4796 || damage[nr][i][j]) {
4797 DrawSquare(i, j, board[i][j], 0);
4798 damage[nr][i][j] = False;
4801 /* Second pass -- Draw piece(s) in new position and flash them */
4802 for (i = 0; i < BOARD_HEIGHT; i++)
4803 for (j = 0; j < BOARD_WIDTH; j++)
4804 if (board[i][j] != lastBoard[nr][i][j]) {
4805 DrawSquare(i, j, board[i][j], do_flash);
4809 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4810 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4811 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4813 for (i = 0; i < BOARD_HEIGHT; i++)
4814 for (j = 0; j < BOARD_WIDTH; j++) {
4815 DrawSquare(i, j, board[i][j], 0);
4816 damage[nr][i][j] = False;
4820 CopyBoard(lastBoard[nr], board);
4821 lastBoardValid[nr] = 1;
4822 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4823 lastFlipView = flipView;
4825 /* Draw highlights */
4826 if (pm1X >= 0 && pm1Y >= 0) {
4827 drawHighlight(pm1X, pm1Y, prelineGC);
4829 if (pm2X >= 0 && pm2Y >= 0) {
4830 drawHighlight(pm2X, pm2Y, prelineGC);
4832 if (hi1X >= 0 && hi1Y >= 0) {
4833 drawHighlight(hi1X, hi1Y, highlineGC);
4835 if (hi2X >= 0 && hi2Y >= 0) {
4836 drawHighlight(hi2X, hi2Y, highlineGC);
4838 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4840 /* If piece being dragged around board, must redraw that too */
4843 XSync(xDisplay, False);
4848 * event handler for redrawing the board
4851 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4853 XDrawPosition(w, True, NULL);
4858 * event handler for parsing user moves
4860 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4861 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4862 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4863 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4864 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4865 // and at the end FinishMove() to perform the move after optional promotion popups.
4866 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4868 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4870 if (w != boardWidget || errorExitStatus != -1) return;
4871 if(nprms) shiftKey = !strcmp(prms[0], "1");
4874 if (event->type == ButtonPress) {
4875 XtPopdown(promotionShell);
4876 XtDestroyWidget(promotionShell);
4877 promotionUp = False;
4885 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4886 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4887 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4891 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4893 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4894 DragPieceMove(event->xmotion.x, event->xmotion.y);
4898 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4899 { // [HGM] pv: walk PV
4900 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4903 static int savedIndex; /* gross that this is global */
4906 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4909 XawTextPosition index, dummy;
4912 XawTextGetSelectionPos(w, &index, &dummy);
4913 XtSetArg(arg, XtNstring, &val);
4914 XtGetValues(w, &arg, 1);
4915 ReplaceComment(savedIndex, val);
4916 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4917 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4921 EditCommentPopUp (int index, char *title, char *text)
4924 if (text == NULL) text = "";
4925 NewCommentPopup(title, text, index);
4934 extern Option boxOptions[];
4944 edit = boxOptions[0].handle;
4946 XtSetArg(args[j], XtNstring, &val); j++;
4947 XtGetValues(edit, args, j);
4949 SendMultiLineToICS(val);
4950 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4951 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4955 ICSInputBoxPopDown ()
4961 CommentPopUp (char *title, char *text)
4963 savedIndex = currentMove; // [HGM] vari
4964 NewCommentPopup(title, text, currentMove);
4973 static char *openName;
4979 (void) (*fileProc)(openFP, 0, openName);
4983 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
4985 fileProc = proc; /* I can't see a way not */
4986 fileOpenMode = openMode; /* to use globals here */
4987 { // [HGM] use file-selector dialog stolen from Ghostview
4988 int index; // this is not supported yet
4989 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
4990 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
4991 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
4992 ScheduleDelayedEvent(&DelayedLoad, 50);
4999 if (!filenameUp) return;
5000 XtPopdown(fileNameShell);
5001 XtDestroyWidget(fileNameShell);
5007 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5012 XtSetArg(args[0], XtNlabel, &name);
5013 XtGetValues(w, args, 1);
5015 if (strcmp(name, _("cancel")) == 0) {
5020 FileNameAction(w, NULL, NULL, NULL);
5024 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5032 name = XawDialogGetValueString(w = XtParent(w));
5034 if ((name != NULL) && (*name != NULLCHAR)) {
5035 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5036 XtPopdown(w = XtParent(XtParent(w)));
5040 p = strrchr(buf, ' ');
5047 fullname = ExpandPathName(buf);
5049 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5052 f = fopen(fullname, fileOpenMode);
5054 DisplayError(_("Failed to open file"), errno);
5056 (void) (*fileProc)(f, index, buf);
5063 XtPopdown(w = XtParent(XtParent(w)));
5073 Widget dialog, layout;
5075 Dimension bw_width, pw_width;
5077 char *PromoChars = "wglcqrbnkac+=\0";
5080 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5081 XtGetValues(boardWidget, args, j);
5084 XtSetArg(args[j], XtNresizable, True); j++;
5085 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5087 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5088 shellWidget, args, j);
5090 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5091 layoutArgs, XtNumber(layoutArgs));
5094 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5095 XtSetArg(args[j], XtNborderWidth, 0); j++;
5096 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5099 if(gameInfo.variant != VariantShogi) {
5100 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5101 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5102 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5103 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5104 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5106 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5107 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5108 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5109 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5111 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5112 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5113 gameInfo.variant == VariantGiveaway) {
5114 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5116 if(gameInfo.variant == VariantCapablanca ||
5117 gameInfo.variant == VariantGothic ||
5118 gameInfo.variant == VariantCapaRandom) {
5119 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5120 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5122 } else // [HGM] shogi
5124 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5125 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5127 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5129 XtRealizeWidget(promotionShell);
5130 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5133 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5134 XtGetValues(promotionShell, args, j);
5136 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5137 lineGap + squareSize/3 +
5138 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5139 0 : 6*(squareSize + lineGap)), &x, &y);
5142 XtSetArg(args[j], XtNx, x); j++;
5143 XtSetArg(args[j], XtNy, y); j++;
5144 XtSetValues(promotionShell, args, j);
5146 XtPopup(promotionShell, XtGrabNone);
5154 if (!promotionUp) return;
5155 XtPopdown(promotionShell);
5156 XtDestroyWidget(promotionShell);
5157 promotionUp = False;
5161 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5163 int promoChar = * (const char *) client_data;
5167 if (fromX == -1) return;
5174 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5176 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5177 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5183 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5186 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5188 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5195 if (!errorUp) return;
5197 XtPopdown(errorShell);
5198 XtDestroyWidget(errorShell);
5199 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5203 ErrorPopUp (char *title, char *label, int modal)
5206 Widget dialog, layout;
5210 Dimension bw_width, pw_width;
5211 Dimension pw_height;
5215 XtSetArg(args[i], XtNresizable, True); i++;
5216 XtSetArg(args[i], XtNtitle, title); i++;
5218 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5219 shellWidget, args, i);
5221 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5222 layoutArgs, XtNumber(layoutArgs));
5225 XtSetArg(args[i], XtNlabel, label); i++;
5226 XtSetArg(args[i], XtNborderWidth, 0); i++;
5227 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5230 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5232 XtRealizeWidget(errorShell);
5233 CatchDeleteWindow(errorShell, "ErrorPopDown");
5236 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5237 XtGetValues(boardWidget, args, i);
5239 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5240 XtSetArg(args[i], XtNheight, &pw_height); i++;
5241 XtGetValues(errorShell, args, i);
5244 /* This code seems to tickle an X bug if it is executed too soon
5245 after xboard starts up. The coordinates get transformed as if
5246 the main window was positioned at (0, 0).
5248 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5249 0 - pw_height + squareSize / 3, &x, &y);
5251 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5252 RootWindowOfScreen(XtScreen(boardWidget)),
5253 (bw_width - pw_width) / 2,
5254 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5258 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5261 XtSetArg(args[i], XtNx, x); i++;
5262 XtSetArg(args[i], XtNy, y); i++;
5263 XtSetValues(errorShell, args, i);
5266 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5269 /* Disable all user input other than deleting the window */
5270 static int frozen = 0;
5276 /* Grab by a widget that doesn't accept input */
5277 XtAddGrab(messageWidget, TRUE, FALSE);
5281 /* Undo a FreezeUI */
5285 if (!frozen) return;
5286 XtRemoveGrab(messageWidget);
5291 ModeToWidgetName (GameMode mode)
5294 case BeginningOfGame:
5295 if (appData.icsActive)
5296 return "menuMode.ICS Client";
5297 else if (appData.noChessProgram ||
5298 *appData.cmailGameName != NULLCHAR)
5299 return "menuMode.Edit Game";
5301 return "menuMode.Machine Black";
5302 case MachinePlaysBlack:
5303 return "menuMode.Machine Black";
5304 case MachinePlaysWhite:
5305 return "menuMode.Machine White";
5307 return "menuMode.Analysis Mode";
5309 return "menuMode.Analyze File";
5310 case TwoMachinesPlay:
5311 return "menuMode.Two Machines";
5313 return "menuMode.Edit Game";
5314 case PlayFromGameFile:
5315 return "menuFile.Load Game";
5317 return "menuMode.Edit Position";
5319 return "menuMode.Training";
5320 case IcsPlayingWhite:
5321 case IcsPlayingBlack:
5325 return "menuMode.ICS Client";
5336 static int oldPausing = FALSE;
5337 static GameMode oldmode = (GameMode) -1;
5340 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5342 if (pausing != oldPausing) {
5343 oldPausing = pausing;
5345 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5347 XtSetArg(args[0], XtNleftBitmap, None);
5349 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5352 if (appData.showButtonBar) {
5353 /* Always toggle, don't set. Previous code messes up when
5354 invoked while the button is pressed, as releasing it
5355 toggles the state again. */
5358 XtSetArg(args[0], XtNbackground, &oldbg);
5359 XtSetArg(args[1], XtNforeground, &oldfg);
5360 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5362 XtSetArg(args[0], XtNbackground, oldfg);
5363 XtSetArg(args[1], XtNforeground, oldbg);
5365 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5369 wname = ModeToWidgetName(oldmode);
5370 if (wname != NULL) {
5371 XtSetArg(args[0], XtNleftBitmap, None);
5372 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5374 wname = ModeToWidgetName(gameMode);
5375 if (wname != NULL) {
5376 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5377 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5380 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5381 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5383 /* Maybe all the enables should be handled here, not just this one */
5384 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5385 gameMode == Training || gameMode == PlayFromGameFile);
5390 * Button/menu procedures
5393 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5399 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5401 cmailMsgLoaded = FALSE;
5402 if (gameNumber == 0) {
5403 int error = GameListBuild(f);
5405 DisplayError(_("Cannot build game list"), error);
5406 } else if (!ListEmpty(&gameList) &&
5407 ((ListGame *) gameList.tailPred)->number > 1) {
5408 GameListPopUp(f, title);
5414 return LoadGame(f, gameNumber, title, FALSE);
5418 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5420 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5423 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5427 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5433 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5439 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5445 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5451 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5457 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5463 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5465 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5468 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5472 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5474 FileNamePopUp(_("Save game file name?"),
5475 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5476 appData.oldSaveStyle ? ".game" : ".pgn",
5481 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5483 FileNamePopUp(_("Save position file name?"),
5484 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5485 appData.oldSaveStyle ? ".pos" : ".fen",
5490 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5492 ReloadCmailMsgEvent(FALSE);
5496 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5501 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5502 char *selected_fen_position=NULL;
5505 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5506 Atom *type_return, XtPointer *value_return,
5507 unsigned long *length_return, int *format_return)
5509 char *selection_tmp;
5511 if (!selected_fen_position) return False; /* should never happen */
5512 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5513 /* note: since no XtSelectionDoneProc was registered, Xt will
5514 * automatically call XtFree on the value returned. So have to
5515 * make a copy of it allocated with XtMalloc */
5516 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5517 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5519 *value_return=selection_tmp;
5520 *length_return=strlen(selection_tmp);
5521 *type_return=*target;
5522 *format_return = 8; /* bits per byte */
5524 } else if (*target == XA_TARGETS(xDisplay)) {
5525 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5526 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5527 targets_tmp[1] = XA_STRING;
5528 *value_return = targets_tmp;
5529 *type_return = XA_ATOM;
5532 // This code leads to a read of value_return out of bounds on 64-bit systems.
5533 // Other code which I have seen always sets *format_return to 32 independent of
5534 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5535 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5536 *format_return = 8 * sizeof(Atom);
5537 if (*format_return > 32) {
5538 *length_return *= *format_return / 32;
5539 *format_return = 32;
5542 *format_return = 32;
5550 /* note: when called from menu all parameters are NULL, so no clue what the
5551 * Widget which was clicked on was, or what the click event was
5554 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5557 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5558 * have a notion of a position that is selected but not copied.
5559 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5561 if(gameMode == EditPosition) EditPositionDone(TRUE);
5562 if (selected_fen_position) free(selected_fen_position);
5563 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5564 if (!selected_fen_position) return;
5565 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5567 SendPositionSelection,
5568 NULL/* lose_ownership_proc */ ,
5569 NULL/* transfer_done_proc */);
5570 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5572 SendPositionSelection,
5573 NULL/* lose_ownership_proc */ ,
5574 NULL/* transfer_done_proc */);
5577 /* function called when the data to Paste is ready */
5579 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5580 Atom *type, XtPointer value, unsigned long *len, int *format)
5583 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5584 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5585 EditPositionPasteFEN(fenstr);
5589 /* called when Paste Position button is pressed,
5590 * all parameters will be NULL */
5591 void PastePositionProc(w, event, prms, nprms)
5597 XtGetSelectionValue(menuBarWidget,
5598 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5599 /* (XtSelectionCallbackProc) */ PastePositionCB,
5600 NULL, /* client_data passed to PastePositionCB */
5602 /* better to use the time field from the event that triggered the
5603 * call to this function, but that isn't trivial to get
5611 SendGameSelection (Widget w, Atom *selection, Atom *target,
5612 Atom *type_return, XtPointer *value_return,
5613 unsigned long *length_return, int *format_return)
5615 char *selection_tmp;
5617 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5618 FILE* f = fopen(gameCopyFilename, "r");
5621 if (f == NULL) return False;
5625 selection_tmp = XtMalloc(len + 1);
5626 count = fread(selection_tmp, 1, len, f);
5629 XtFree(selection_tmp);
5632 selection_tmp[len] = NULLCHAR;
5633 *value_return = selection_tmp;
5634 *length_return = len;
5635 *type_return = *target;
5636 *format_return = 8; /* bits per byte */
5638 } else if (*target == XA_TARGETS(xDisplay)) {
5639 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5640 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5641 targets_tmp[1] = XA_STRING;
5642 *value_return = targets_tmp;
5643 *type_return = XA_ATOM;
5646 // This code leads to a read of value_return out of bounds on 64-bit systems.
5647 // Other code which I have seen always sets *format_return to 32 independent of
5648 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5649 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5650 *format_return = 8 * sizeof(Atom);
5651 if (*format_return > 32) {
5652 *length_return *= *format_return / 32;
5653 *format_return = 32;
5656 *format_return = 32;
5668 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5669 * have a notion of a game that is selected but not copied.
5670 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5672 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5675 NULL/* lose_ownership_proc */ ,
5676 NULL/* transfer_done_proc */);
5677 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5680 NULL/* lose_ownership_proc */ ,
5681 NULL/* transfer_done_proc */);
5684 /* note: when called from menu all parameters are NULL, so no clue what the
5685 * Widget which was clicked on was, or what the click event was
5688 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5692 ret = SaveGameToFile(gameCopyFilename, FALSE);
5699 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5701 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5705 /* function called when the data to Paste is ready */
5707 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5708 Atom *type, XtPointer value, unsigned long *len, int *format)
5711 if (value == NULL || *len == 0) {
5712 return; /* nothing had been selected to copy */
5714 f = fopen(gamePasteFilename, "w");
5716 DisplayError(_("Can't open temp file"), errno);
5719 fwrite(value, 1, *len, f);
5722 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5725 /* called when Paste Game button is pressed,
5726 * all parameters will be NULL */
5728 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5730 XtGetSelectionValue(menuBarWidget,
5731 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5732 /* (XtSelectionCallbackProc) */ PasteGameCB,
5733 NULL, /* client_data passed to PasteGameCB */
5735 /* better to use the time field from the event that triggered the
5736 * call to this function, but that isn't trivial to get
5747 SaveGameProc(NULL, NULL, NULL, NULL);
5752 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5758 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5764 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5766 MachineBlackEvent();
5770 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5772 MachineWhiteEvent();
5776 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5780 if (!first.analysisSupport) {
5781 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5782 DisplayError(buf, 0);
5785 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5786 if (appData.icsActive) {
5787 if (gameMode != IcsObserving) {
5788 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5789 DisplayError(buf, 0);
5791 if (appData.icsEngineAnalyze) {
5792 if (appData.debugMode)
5793 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5799 /* if enable, use want disable icsEngineAnalyze */
5800 if (appData.icsEngineAnalyze) {
5805 appData.icsEngineAnalyze = TRUE;
5806 if (appData.debugMode)
5807 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5809 #ifndef OPTIONSDIALOG
5810 if (!appData.showThinking)
5811 ShowThinkingProc(w,event,prms,nprms);
5818 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5820 if (!first.analysisSupport) {
5822 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5823 DisplayError(buf, 0);
5826 // Reset(FALSE, TRUE);
5827 #ifndef OPTIONSDIALOG
5828 if (!appData.showThinking)
5829 ShowThinkingProc(w,event,prms,nprms);
5832 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5833 AnalysisPeriodicEvent(1);
5837 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5843 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5849 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5855 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5861 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5863 EditPositionEvent();
5867 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5873 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5877 if (PopDown(1)) { // popdown succesful
5879 XtSetArg(args[j], XtNleftBitmap, None); j++;
5880 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5881 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5882 } else // was not up
5887 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5889 if (!PopDown(4)) ICSInputBoxPopUp();
5893 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5899 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5905 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5911 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5917 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5923 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5929 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5935 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5941 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5943 UserAdjudicationEvent(+1);
5947 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5949 UserAdjudicationEvent(-1);
5953 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5955 UserAdjudicationEvent(0);
5959 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5961 if (shellUp[4] == True)
5966 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5967 { // [HGM] input: let up-arrow recall previous line from history
5974 if (!shellUp[4]) return;
5975 edit = boxOptions[0].handle;
5977 XtSetArg(args[j], XtNstring, &val); j++;
5978 XtGetValues(edit, args, j);
5979 val = PrevInHistory(val);
5980 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5981 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5983 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
5984 XawTextReplace(edit, 0, 0, &t);
5985 XawTextSetInsertionPoint(edit, 9999);
5990 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5991 { // [HGM] input: let down-arrow recall next line from history
5996 if (!shellUp[4]) return;
5997 edit = boxOptions[0].handle;
5998 val = NextInHistory();
5999 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6000 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6002 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6003 XawTextReplace(edit, 0, 0, &t);
6004 XawTextSetInsertionPoint(edit, 9999);
6009 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6011 StopObservingEvent();
6015 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6017 StopExaminingEvent();
6021 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6028 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6035 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6041 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6043 if (!TempBackwardActive) {
6044 TempBackwardActive = True;
6050 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6052 /* Check to see if triggered by a key release event for a repeating key.
6053 * If so the next queued event will be a key press of the same key at the same time */
6054 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6056 XPeekEvent(xDisplay, &next);
6057 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6058 next.xkey.keycode == event->xkey.keycode)
6062 TempBackwardActive = False;
6066 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6072 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6078 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6084 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6090 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6092 TruncateGameEvent();
6096 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6102 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6108 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6110 flipView = !flipView;
6111 DrawPosition(True, NULL);
6115 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6119 PonderNextMoveEvent(!appData.ponderNextMove);
6120 #ifndef OPTIONSDIALOG
6121 if (appData.ponderNextMove) {
6122 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6124 XtSetArg(args[0], XtNleftBitmap, None);
6126 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6131 #ifndef OPTIONSDIALOG
6133 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6137 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6139 if (appData.alwaysPromoteToQueen) {
6140 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6142 XtSetArg(args[0], XtNleftBitmap, None);
6144 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6149 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6153 appData.animateDragging = !appData.animateDragging;
6155 if (appData.animateDragging) {
6156 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6159 XtSetArg(args[0], XtNleftBitmap, None);
6161 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6166 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6170 appData.animate = !appData.animate;
6172 if (appData.animate) {
6173 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6176 XtSetArg(args[0], XtNleftBitmap, None);
6178 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6183 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6187 appData.autoCallFlag = !appData.autoCallFlag;
6189 if (appData.autoCallFlag) {
6190 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6192 XtSetArg(args[0], XtNleftBitmap, None);
6194 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6199 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6203 appData.autoFlipView = !appData.autoFlipView;
6205 if (appData.autoFlipView) {
6206 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6208 XtSetArg(args[0], XtNleftBitmap, None);
6210 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6215 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6219 appData.blindfold = !appData.blindfold;
6221 if (appData.blindfold) {
6222 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6224 XtSetArg(args[0], XtNleftBitmap, None);
6226 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6229 DrawPosition(True, NULL);
6233 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6237 appData.testLegality = !appData.testLegality;
6239 if (appData.testLegality) {
6240 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6242 XtSetArg(args[0], XtNleftBitmap, None);
6244 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6250 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6254 if (appData.flashCount == 0) {
6255 appData.flashCount = 3;
6257 appData.flashCount = -appData.flashCount;
6260 if (appData.flashCount > 0) {
6261 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6263 XtSetArg(args[0], XtNleftBitmap, None);
6265 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6271 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6275 appData.highlightDragging = !appData.highlightDragging;
6277 if (appData.highlightDragging) {
6278 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6280 XtSetArg(args[0], XtNleftBitmap, None);
6282 XtSetValues(XtNameToWidget(menuBarWidget,
6283 "menuOptions.Highlight Dragging"), args, 1);
6288 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6292 appData.highlightLastMove = !appData.highlightLastMove;
6294 if (appData.highlightLastMove) {
6295 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6297 XtSetArg(args[0], XtNleftBitmap, None);
6299 XtSetValues(XtNameToWidget(menuBarWidget,
6300 "menuOptions.Highlight Last Move"), args, 1);
6304 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6308 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6310 if (appData.highlightMoveWithArrow) {
6311 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6313 XtSetArg(args[0], XtNleftBitmap, None);
6315 XtSetValues(XtNameToWidget(menuBarWidget,
6316 "menuOptions.Arrow"), args, 1);
6321 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6325 appData.icsAlarm = !appData.icsAlarm;
6327 if (appData.icsAlarm) {
6328 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6330 XtSetArg(args[0], XtNleftBitmap, None);
6332 XtSetValues(XtNameToWidget(menuBarWidget,
6333 "menuOptions.ICS Alarm"), args, 1);
6338 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6342 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6344 if (appData.ringBellAfterMoves) {
6345 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6347 XtSetArg(args[0], XtNleftBitmap, None);
6349 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6354 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6358 appData.oneClick = !appData.oneClick;
6360 if (appData.oneClick) {
6361 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6363 XtSetArg(args[0], XtNleftBitmap, None);
6365 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6370 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6374 PeriodicUpdatesEvent(!appData.periodicUpdates);
6376 if (appData.periodicUpdates) {
6377 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6379 XtSetArg(args[0], XtNleftBitmap, None);
6381 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6386 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6390 appData.popupExitMessage = !appData.popupExitMessage;
6392 if (appData.popupExitMessage) {
6393 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6395 XtSetArg(args[0], XtNleftBitmap, None);
6397 XtSetValues(XtNameToWidget(menuBarWidget,
6398 "menuOptions.Popup Exit Message"), args, 1);
6402 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6406 appData.popupMoveErrors = !appData.popupMoveErrors;
6408 if (appData.popupMoveErrors) {
6409 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6411 XtSetArg(args[0], XtNleftBitmap, None);
6413 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6419 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6423 appData.premove = !appData.premove;
6425 if (appData.premove) {
6426 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6428 XtSetArg(args[0], XtNleftBitmap, None);
6430 XtSetValues(XtNameToWidget(menuBarWidget,
6431 "menuOptions.Premove"), args, 1);
6436 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6440 appData.showCoords = !appData.showCoords;
6442 if (appData.showCoords) {
6443 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6445 XtSetArg(args[0], XtNleftBitmap, None);
6447 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6450 DrawPosition(True, NULL);
6454 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6456 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6457 ShowThinkingEvent();
6461 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6465 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6466 ShowThinkingEvent();
6468 if (appData.hideThinkingFromHuman) {
6469 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6471 XtSetArg(args[0], XtNleftBitmap, None);
6473 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6479 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6483 saveSettingsOnExit = !saveSettingsOnExit;
6485 if (saveSettingsOnExit) {
6486 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6488 XtSetArg(args[0], XtNleftBitmap, None);
6490 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6495 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6497 SaveSettings(settingsFileName);
6501 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6504 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6510 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6514 if (nprms && *nprms > 0)
6518 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6523 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6529 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6535 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6539 char *zippy = _(" (with Zippy code)");
6543 snprintf(buf, sizeof(buf),
6545 "Copyright 1991 Digital Equipment Corporation\n"
6546 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6547 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6548 "%s is free software and carries NO WARRANTY;"
6549 "see the file COPYING for more information."),
6550 programVersion, zippy, PACKAGE);
6551 ErrorPopUp(_("About XBoard"), buf, FALSE);
6555 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6557 appData.debugMode = !appData.debugMode;
6561 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6567 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6573 DisplayMessage (char *message, char *extMessage)
6575 /* display a message in the message widget */
6584 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6589 message = extMessage;
6593 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6595 /* need to test if messageWidget already exists, since this function
6596 can also be called during the startup, if for example a Xresource
6597 is not set up correctly */
6600 XtSetArg(arg, XtNlabel, message);
6601 XtSetValues(messageWidget, &arg, 1);
6608 DisplayTitle (char *text)
6612 char title[MSG_SIZ];
6615 if (text == NULL) text = "";
6617 if (appData.titleInWindow) {
6619 XtSetArg(args[i], XtNlabel, text); i++;
6620 XtSetValues(titleWidget, args, i);
6623 if (*text != NULLCHAR) {
6624 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6625 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6626 } else if (appData.icsActive) {
6627 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6628 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6629 } else if (appData.cmailGameName[0] != NULLCHAR) {
6630 snprintf(icon, sizeof(icon), "%s", "CMail");
6631 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6633 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6634 } else if (gameInfo.variant == VariantGothic) {
6635 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6636 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6639 } else if (gameInfo.variant == VariantFalcon) {
6640 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6641 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6643 } else if (appData.noChessProgram) {
6644 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6645 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6647 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6648 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6651 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6652 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6653 XtSetValues(shellWidget, args, i);
6654 XSync(xDisplay, False);
6659 DisplayError (String message, int error)
6664 if (appData.debugMode || appData.matchMode) {
6665 fprintf(stderr, "%s: %s\n", programName, message);
6668 if (appData.debugMode || appData.matchMode) {
6669 fprintf(stderr, "%s: %s: %s\n",
6670 programName, message, strerror(error));
6672 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6675 ErrorPopUp(_("Error"), message, FALSE);
6680 DisplayMoveError (String message)
6684 DrawPosition(FALSE, NULL);
6685 if (appData.debugMode || appData.matchMode) {
6686 fprintf(stderr, "%s: %s\n", programName, message);
6688 if (appData.popupMoveErrors) {
6689 ErrorPopUp(_("Error"), message, FALSE);
6691 DisplayMessage(message, "");
6697 DisplayFatalError (String message, int error, int status)
6701 errorExitStatus = status;
6703 fprintf(stderr, "%s: %s\n", programName, message);
6705 fprintf(stderr, "%s: %s: %s\n",
6706 programName, message, strerror(error));
6707 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6710 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6711 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6718 DisplayInformation (String message)
6721 ErrorPopUp(_("Information"), message, TRUE);
6725 DisplayNote (String message)
6728 ErrorPopUp(_("Note"), message, FALSE);
6732 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6738 DisplayIcsInteractionTitle (String message)
6740 if (oldICSInteractionTitle == NULL) {
6741 /* Magic to find the old window title, adapted from vim */
6742 char *wina = getenv("WINDOWID");
6744 Window win = (Window) atoi(wina);
6745 Window root, parent, *children;
6746 unsigned int nchildren;
6747 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6749 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6750 if (!XQueryTree(xDisplay, win, &root, &parent,
6751 &children, &nchildren)) break;
6752 if (children) XFree((void *)children);
6753 if (parent == root || parent == 0) break;
6756 XSetErrorHandler(oldHandler);
6758 if (oldICSInteractionTitle == NULL) {
6759 oldICSInteractionTitle = "xterm";
6762 printf("\033]0;%s\007", message);
6766 char pendingReplyPrefix[MSG_SIZ];
6767 ProcRef pendingReplyPR;
6770 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6773 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6777 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6781 AskQuestionPopDown ()
6783 if (!askQuestionUp) return;
6784 XtPopdown(askQuestionShell);
6785 XtDestroyWidget(askQuestionShell);
6786 askQuestionUp = False;
6790 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6796 reply = XawDialogGetValueString(w = XtParent(w));
6797 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6798 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6799 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6800 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6801 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6802 AskQuestionPopDown();
6804 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6808 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6813 XtSetArg(args[0], XtNlabel, &name);
6814 XtGetValues(w, args, 1);
6816 if (strcmp(name, _("cancel")) == 0) {
6817 AskQuestionPopDown();
6819 AskQuestionReplyAction(w, NULL, NULL, NULL);
6824 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6827 Widget popup, layout, dialog, edit;
6833 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6834 pendingReplyPR = pr;
6837 XtSetArg(args[i], XtNresizable, True); i++;
6838 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6839 askQuestionShell = popup =
6840 XtCreatePopupShell(title, transientShellWidgetClass,
6841 shellWidget, args, i);
6844 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6845 layoutArgs, XtNumber(layoutArgs));
6848 XtSetArg(args[i], XtNlabel, question); i++;
6849 XtSetArg(args[i], XtNvalue, ""); i++;
6850 XtSetArg(args[i], XtNborderWidth, 0); i++;
6851 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6854 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6855 (XtPointer) dialog);
6856 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6857 (XtPointer) dialog);
6859 XtRealizeWidget(popup);
6860 CatchDeleteWindow(popup, "AskQuestionPopDown");
6862 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6863 &x, &y, &win_x, &win_y, &mask);
6865 XtSetArg(args[0], XtNx, x - 10);
6866 XtSetArg(args[1], XtNy, y - 30);
6867 XtSetValues(popup, args, 2);
6869 XtPopup(popup, XtGrabExclusive);
6870 askQuestionUp = True;
6872 edit = XtNameToWidget(dialog, "*value");
6873 XtSetKeyboardFocus(popup, edit);
6878 PlaySound (char *name)
6880 if (*name == NULLCHAR) {
6882 } else if (strcmp(name, "$") == 0) {
6883 putc(BELLCHAR, stderr);
6886 char *prefix = "", *sep = "";
6887 if(appData.soundProgram[0] == NULLCHAR) return;
6888 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6889 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6897 PlaySound(appData.soundMove);
6903 PlaySound(appData.soundIcsWin);
6909 PlaySound(appData.soundIcsLoss);
6915 PlaySound(appData.soundIcsDraw);
6919 PlayIcsUnfinishedSound ()
6921 PlaySound(appData.soundIcsUnfinished);
6927 PlaySound(appData.soundIcsAlarm);
6933 PlaySound(appData.soundTell);
6939 system("stty echo");
6946 system("stty -echo");
6951 RunCommand (char *buf)
6957 Colorize (ColorClass cc, int continuation)
6960 int count, outCount, error;
6962 if (textColors[(int)cc].bg > 0) {
6963 if (textColors[(int)cc].fg > 0) {
6964 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
6965 textColors[(int)cc].fg, textColors[(int)cc].bg);
6967 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
6968 textColors[(int)cc].bg);
6971 if (textColors[(int)cc].fg > 0) {
6972 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
6973 textColors[(int)cc].fg);
6975 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
6978 count = strlen(buf);
6979 outCount = OutputToProcess(NoProc, buf, count, &error);
6980 if (outCount < count) {
6981 DisplayFatalError(_("Error writing to display"), error, 1);
6984 if (continuation) return;
6987 PlaySound(appData.soundShout);
6990 PlaySound(appData.soundSShout);
6993 PlaySound(appData.soundChannel1);
6996 PlaySound(appData.soundChannel);
6999 PlaySound(appData.soundKibitz);
7002 PlaySound(appData.soundTell);
7004 case ColorChallenge:
7005 PlaySound(appData.soundChallenge);
7008 PlaySound(appData.soundRequest);
7011 PlaySound(appData.soundSeek);
7023 return getpwuid(getuid())->pw_name;
7027 ExpandPathName (char *path)
7029 static char static_buf[4*MSG_SIZ];
7030 char *d, *s, buf[4*MSG_SIZ];
7036 while (*s && isspace(*s))
7045 if (*(s+1) == '/') {
7046 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7050 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7051 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7052 pwd = getpwnam(buf);
7055 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7059 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7060 strcat(d, strchr(s+1, '/'));
7064 safeStrCpy(d, s, 4*MSG_SIZ );
7072 static char host_name[MSG_SIZ];
7074 #if HAVE_GETHOSTNAME
7075 gethostname(host_name, MSG_SIZ);
7077 #else /* not HAVE_GETHOSTNAME */
7078 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7079 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7081 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7083 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7084 #endif /* not HAVE_GETHOSTNAME */
7087 XtIntervalId delayedEventTimerXID = 0;
7088 DelayedEventCallback delayedEventCallback = 0;
7093 delayedEventTimerXID = 0;
7094 delayedEventCallback();
7098 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7100 if(delayedEventTimerXID && delayedEventCallback == cb)
7101 // [HGM] alive: replace, rather than add or flush identical event
7102 XtRemoveTimeOut(delayedEventTimerXID);
7103 delayedEventCallback = cb;
7104 delayedEventTimerXID =
7105 XtAppAddTimeOut(appContext, millisec,
7106 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7109 DelayedEventCallback
7112 if (delayedEventTimerXID) {
7113 return delayedEventCallback;
7120 CancelDelayedEvent ()
7122 if (delayedEventTimerXID) {
7123 XtRemoveTimeOut(delayedEventTimerXID);
7124 delayedEventTimerXID = 0;
7128 XtIntervalId loadGameTimerXID = 0;
7131 LoadGameTimerRunning ()
7133 return loadGameTimerXID != 0;
7137 StopLoadGameTimer ()
7139 if (loadGameTimerXID != 0) {
7140 XtRemoveTimeOut(loadGameTimerXID);
7141 loadGameTimerXID = 0;
7149 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7151 loadGameTimerXID = 0;
7156 StartLoadGameTimer (long millisec)
7159 XtAppAddTimeOut(appContext, millisec,
7160 (XtTimerCallbackProc) LoadGameTimerCallback,
7164 XtIntervalId analysisClockXID = 0;
7167 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7169 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7170 || appData.icsEngineAnalyze) { // [DM]
7171 AnalysisPeriodicEvent(0);
7172 StartAnalysisClock();
7177 StartAnalysisClock ()
7180 XtAppAddTimeOut(appContext, 2000,
7181 (XtTimerCallbackProc) AnalysisClockCallback,
7185 XtIntervalId clockTimerXID = 0;
7188 ClockTimerRunning ()
7190 return clockTimerXID != 0;
7196 if (clockTimerXID != 0) {
7197 XtRemoveTimeOut(clockTimerXID);
7206 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7213 StartClockTimer (long millisec)
7216 XtAppAddTimeOut(appContext, millisec,
7217 (XtTimerCallbackProc) ClockTimerCallback,
7222 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7227 /* check for low time warning */
7228 Pixel foregroundOrWarningColor = timerForegroundPixel;
7231 appData.lowTimeWarning &&
7232 (timer / 1000) < appData.icsAlarmTime)
7233 foregroundOrWarningColor = lowTimeWarningColor;
7235 if (appData.clockMode) {
7236 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7237 XtSetArg(args[0], XtNlabel, buf);
7239 snprintf(buf, MSG_SIZ, "%s ", color);
7240 XtSetArg(args[0], XtNlabel, buf);
7245 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7246 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7248 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7249 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7252 XtSetValues(w, args, 3);
7256 DisplayWhiteClock (long timeRemaining, int highlight)
7260 if(appData.noGUI) return;
7261 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7262 if (highlight && iconPixmap == bIconPixmap) {
7263 iconPixmap = wIconPixmap;
7264 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7265 XtSetValues(shellWidget, args, 1);
7270 DisplayBlackClock (long timeRemaining, int highlight)
7274 if(appData.noGUI) return;
7275 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7276 if (highlight && iconPixmap == wIconPixmap) {
7277 iconPixmap = bIconPixmap;
7278 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7279 XtSetValues(shellWidget, args, 1);
7298 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7302 int to_prog[2], from_prog[2];
7306 if (appData.debugMode) {
7307 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7310 /* We do NOT feed the cmdLine to the shell; we just
7311 parse it into blank-separated arguments in the
7312 most simple-minded way possible.
7315 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7318 while(*p == ' ') p++;
7320 if(*p == '"' || *p == '\'')
7321 p = strchr(++argv[i-1], *p);
7322 else p = strchr(p, ' ');
7323 if (p == NULL) break;
7328 SetUpChildIO(to_prog, from_prog);
7330 if ((pid = fork()) == 0) {
7332 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7333 close(to_prog[1]); // first close the unused pipe ends
7334 close(from_prog[0]);
7335 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7336 dup2(from_prog[1], 1);
7337 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7338 close(from_prog[1]); // and closing again loses one of the pipes!
7339 if(fileno(stderr) >= 2) // better safe than sorry...
7340 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7342 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7347 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7349 execvp(argv[0], argv);
7351 /* If we get here, exec failed */
7356 /* Parent process */
7358 close(from_prog[1]);
7360 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7363 cp->fdFrom = from_prog[0];
7364 cp->fdTo = to_prog[1];
7369 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7371 AlarmCallBack (int n)
7377 DestroyChildProcess (ProcRef pr, int signalType)
7379 ChildProc *cp = (ChildProc *) pr;
7381 if (cp->kind != CPReal) return;
7383 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7384 signal(SIGALRM, AlarmCallBack);
7386 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7387 kill(cp->pid, SIGKILL); // kill it forcefully
7388 wait((int *) 0); // and wait again
7392 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7394 /* Process is exiting either because of the kill or because of
7395 a quit command sent by the backend; either way, wait for it to die.
7404 InterruptChildProcess (ProcRef pr)
7406 ChildProc *cp = (ChildProc *) pr;
7408 if (cp->kind != CPReal) return;
7409 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7413 OpenTelnet (char *host, char *port, ProcRef *pr)
7415 char cmdLine[MSG_SIZ];
7417 if (port[0] == NULLCHAR) {
7418 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7420 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7422 return StartChildProcess(cmdLine, "", pr);
7426 OpenTCP (char *host, char *port, ProcRef *pr)
7429 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7430 #else /* !OMIT_SOCKETS */
7431 struct addrinfo hints;
7432 struct addrinfo *ais, *ai;
7437 memset(&hints, 0, sizeof(hints));
7438 hints.ai_family = AF_UNSPEC;
7439 hints.ai_socktype = SOCK_STREAM;
7441 error = getaddrinfo(host, port, &hints, &ais);
7443 /* a getaddrinfo error is not an errno, so can't return it */
7444 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7445 host, port, gai_strerror(error));
7449 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7450 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7454 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7467 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7473 #endif /* !OMIT_SOCKETS */
7479 OpenCommPort (char *name, ProcRef *pr)
7484 fd = open(name, 2, 0);
7485 if (fd < 0) return errno;
7487 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7498 OpenLoopback (ProcRef *pr)
7503 SetUpChildIO(to, from);
7505 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7508 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7516 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7518 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7522 #define INPUT_SOURCE_BUF_SIZE 8192
7531 char buf[INPUT_SOURCE_BUF_SIZE];
7536 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7538 InputSource *is = (InputSource *) closure;
7543 if (is->lineByLine) {
7544 count = read(is->fd, is->unused,
7545 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7547 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7550 is->unused += count;
7552 while (p < is->unused) {
7553 q = memchr(p, '\n', is->unused - p);
7554 if (q == NULL) break;
7556 (is->func)(is, is->closure, p, q - p, 0);
7560 while (p < is->unused) {
7565 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7570 (is->func)(is, is->closure, is->buf, count, error);
7575 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7578 ChildProc *cp = (ChildProc *) pr;
7580 is = (InputSource *) calloc(1, sizeof(InputSource));
7581 is->lineByLine = lineByLine;
7585 is->fd = fileno(stdin);
7587 is->kind = cp->kind;
7588 is->fd = cp->fdFrom;
7591 is->unused = is->buf;
7594 is->xid = XtAppAddInput(appContext, is->fd,
7595 (XtPointer) (XtInputReadMask),
7596 (XtInputCallbackProc) DoInputCallback,
7598 is->closure = closure;
7599 return (InputSourceRef) is;
7603 RemoveInputSource (InputSourceRef isr)
7605 InputSource *is = (InputSource *) isr;
7607 if (is->xid == 0) return;
7608 XtRemoveInput(is->xid);
7613 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7615 static int line = 0;
7616 ChildProc *cp = (ChildProc *) pr;
7621 if (appData.noJoin || !appData.useInternalWrap)
7622 outCount = fwrite(message, 1, count, stdout);
7625 int width = get_term_width();
7626 int len = wrap(NULL, message, count, width, &line);
7627 char *msg = malloc(len);
7631 outCount = fwrite(message, 1, count, stdout);
7634 dbgchk = wrap(msg, message, count, width, &line);
7635 if (dbgchk != len && appData.debugMode)
7636 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7637 outCount = fwrite(msg, 1, dbgchk, stdout);
7643 outCount = write(cp->fdTo, message, count);
7653 /* Output message to process, with "ms" milliseconds of delay
7654 between each character. This is needed when sending the logon
7655 script to ICC, which for some reason doesn't like the
7656 instantaneous send. */
7658 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7660 ChildProc *cp = (ChildProc *) pr;
7665 r = write(cp->fdTo, message++, 1);
7678 /**** Animation code by Hugh Fisher, DCS, ANU.
7680 Known problem: if a window overlapping the board is
7681 moved away while a piece is being animated underneath,
7682 the newly exposed area won't be updated properly.
7683 I can live with this.
7685 Known problem: if you look carefully at the animation
7686 of pieces in mono mode, they are being drawn as solid
7687 shapes without interior detail while moving. Fixing
7688 this would be a major complication for minimal return.
7691 /* Masks for XPM pieces. Black and white pieces can have
7692 different shapes, but in the interest of retaining my
7693 sanity pieces must have the same outline on both light
7694 and dark squares, and all pieces must use the same
7695 background square colors/images. */
7697 static int xpmDone = 0;
7700 CreateAnimMasks (int pieceDepth)
7706 unsigned long plane;
7709 /* Need a bitmap just to get a GC with right depth */
7710 buf = XCreatePixmap(xDisplay, xBoardWindow,
7712 values.foreground = 1;
7713 values.background = 0;
7714 /* Don't use XtGetGC, not read only */
7715 maskGC = XCreateGC(xDisplay, buf,
7716 GCForeground | GCBackground, &values);
7717 XFreePixmap(xDisplay, buf);
7719 buf = XCreatePixmap(xDisplay, xBoardWindow,
7720 squareSize, squareSize, pieceDepth);
7721 values.foreground = XBlackPixel(xDisplay, xScreen);
7722 values.background = XWhitePixel(xDisplay, xScreen);
7723 bufGC = XCreateGC(xDisplay, buf,
7724 GCForeground | GCBackground, &values);
7726 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7727 /* Begin with empty mask */
7728 if(!xpmDone) // [HGM] pieces: keep using existing
7729 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7730 squareSize, squareSize, 1);
7731 XSetFunction(xDisplay, maskGC, GXclear);
7732 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7733 0, 0, squareSize, squareSize);
7735 /* Take a copy of the piece */
7740 XSetFunction(xDisplay, bufGC, GXcopy);
7741 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7743 0, 0, squareSize, squareSize, 0, 0);
7745 /* XOR the background (light) over the piece */
7746 XSetFunction(xDisplay, bufGC, GXxor);
7748 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7749 0, 0, squareSize, squareSize, 0, 0);
7751 XSetForeground(xDisplay, bufGC, lightSquareColor);
7752 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7755 /* We now have an inverted piece image with the background
7756 erased. Construct mask by just selecting all the non-zero
7757 pixels - no need to reconstruct the original image. */
7758 XSetFunction(xDisplay, maskGC, GXor);
7760 /* Might be quicker to download an XImage and create bitmap
7761 data from it rather than this N copies per piece, but it
7762 only takes a fraction of a second and there is a much
7763 longer delay for loading the pieces. */
7764 for (n = 0; n < pieceDepth; n ++) {
7765 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7766 0, 0, squareSize, squareSize,
7772 XFreePixmap(xDisplay, buf);
7773 XFreeGC(xDisplay, bufGC);
7774 XFreeGC(xDisplay, maskGC);
7778 InitAnimState (AnimState *anim, XWindowAttributes *info)
7783 /* Each buffer is square size, same depth as window */
7784 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7785 squareSize, squareSize, info->depth);
7786 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7787 squareSize, squareSize, info->depth);
7789 /* Create a plain GC for blitting */
7790 mask = GCForeground | GCBackground | GCFunction |
7791 GCPlaneMask | GCGraphicsExposures;
7792 values.foreground = XBlackPixel(xDisplay, xScreen);
7793 values.background = XWhitePixel(xDisplay, xScreen);
7794 values.function = GXcopy;
7795 values.plane_mask = AllPlanes;
7796 values.graphics_exposures = False;
7797 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7799 /* Piece will be copied from an existing context at
7800 the start of each new animation/drag. */
7801 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7803 /* Outline will be a read-only copy of an existing */
7804 anim->outlineGC = None;
7810 XWindowAttributes info;
7812 if (xpmDone && gameInfo.variant == oldVariant) return;
7813 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7814 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7816 InitAnimState(&game, &info);
7817 InitAnimState(&player, &info);
7819 /* For XPM pieces, we need bitmaps to use as masks. */
7821 CreateAnimMasks(info.depth), xpmDone = 1;
7826 static Boolean frameWaiting;
7829 FrameAlarm (int sig)
7831 frameWaiting = False;
7832 /* In case System-V style signals. Needed?? */
7833 signal(SIGALRM, FrameAlarm);
7837 FrameDelay (int time)
7839 struct itimerval delay;
7841 XSync(xDisplay, False);
7844 frameWaiting = True;
7845 signal(SIGALRM, FrameAlarm);
7846 delay.it_interval.tv_sec =
7847 delay.it_value.tv_sec = time / 1000;
7848 delay.it_interval.tv_usec =
7849 delay.it_value.tv_usec = (time % 1000) * 1000;
7850 setitimer(ITIMER_REAL, &delay, NULL);
7851 while (frameWaiting) pause();
7852 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7853 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7854 setitimer(ITIMER_REAL, &delay, NULL);
7861 FrameDelay (int time)
7863 XSync(xDisplay, False);
7865 usleep(time * 1000);
7876 /* Convert board position to corner of screen rect and color */
7879 ScreenSquare (int column, int row, XPoint *pt, int *color)
7882 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7883 pt->y = lineGap + row * (squareSize + lineGap);
7885 pt->x = lineGap + column * (squareSize + lineGap);
7886 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7888 *color = SquareColor(row, column);
7891 /* Convert window coords to square */
7894 BoardSquare (int x, int y, int *column, int *row)
7896 *column = EventToSquare(x, BOARD_WIDTH);
7897 if (flipView && *column >= 0)
7898 *column = BOARD_WIDTH - 1 - *column;
7899 *row = EventToSquare(y, BOARD_HEIGHT);
7900 if (!flipView && *row >= 0)
7901 *row = BOARD_HEIGHT - 1 - *row;
7906 #undef Max /* just in case */
7908 #define Max(a, b) ((a) > (b) ? (a) : (b))
7909 #define Min(a, b) ((a) < (b) ? (a) : (b))
7912 SetRect (XRectangle *rect, int x, int y, int width, int height)
7916 rect->width = width;
7917 rect->height = height;
7920 /* Test if two frames overlap. If they do, return
7921 intersection rect within old and location of
7922 that rect within new. */
7925 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
7927 if (old->x > new->x + size || new->x > old->x + size ||
7928 old->y > new->y + size || new->y > old->y + size) {
7931 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7932 size - abs(old->x - new->x), size - abs(old->y - new->y));
7933 pt->x = Max(old->x - new->x, 0);
7934 pt->y = Max(old->y - new->y, 0);
7939 /* For two overlapping frames, return the rect(s)
7940 in the old that do not intersect with the new. */
7943 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
7947 /* If old = new (shouldn't happen) then nothing to draw */
7948 if (old->x == new->x && old->y == new->y) {
7952 /* Work out what bits overlap. Since we know the rects
7953 are the same size we don't need a full intersect calc. */
7955 /* Top or bottom edge? */
7956 if (new->y > old->y) {
7957 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
7959 } else if (old->y > new->y) {
7960 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
7961 size, old->y - new->y);
7964 /* Left or right edge - don't overlap any update calculated above. */
7965 if (new->x > old->x) {
7966 SetRect(&(update[count]), old->x, Max(new->y, old->y),
7967 new->x - old->x, size - abs(new->y - old->y));
7969 } else if (old->x > new->x) {
7970 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
7971 old->x - new->x, size - abs(new->y - old->y));
7978 /* Generate a series of frame coords from start->mid->finish.
7979 The movement rate doubles until the half way point is
7980 reached, then halves back down to the final destination,
7981 which gives a nice slow in/out effect. The algorithmn
7982 may seem to generate too many intermediates for short
7983 moves, but remember that the purpose is to attract the
7984 viewers attention to the piece about to be moved and
7985 then to where it ends up. Too few frames would be less
7989 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
7991 int fraction, n, count;
7995 /* Slow in, stepping 1/16th, then 1/8th, ... */
7997 for (n = 0; n < factor; n++)
7999 for (n = 0; n < factor; n++) {
8000 frames[count].x = start->x + (mid->x - start->x) / fraction;
8001 frames[count].y = start->y + (mid->y - start->y) / fraction;
8003 fraction = fraction / 2;
8007 frames[count] = *mid;
8010 /* Slow out, stepping 1/2, then 1/4, ... */
8012 for (n = 0; n < factor; n++) {
8013 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8014 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8016 fraction = fraction * 2;
8021 /* Draw a piece on the screen without disturbing what's there */
8024 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8028 /* Bitmap for piece being moved. */
8029 if (appData.monoMode) {
8030 *mask = *pieceToSolid(piece);
8031 } else if (useImages) {
8033 *mask = xpmMask[piece];
8035 *mask = ximMaskPm[piece];
8038 *mask = *pieceToSolid(piece);
8041 /* GC for piece being moved. Square color doesn't matter, but
8042 since it gets modified we make a copy of the original. */
8044 if (appData.monoMode)
8049 if (appData.monoMode)
8054 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8056 /* Outline only used in mono mode and is not modified */
8058 *outline = bwPieceGC;
8060 *outline = wbPieceGC;
8064 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8069 /* Draw solid rectangle which will be clipped to shape of piece */
8070 XFillRectangle(xDisplay, dest, clip,
8071 0, 0, squareSize, squareSize);
8072 if (appData.monoMode)
8073 /* Also draw outline in contrasting color for black
8074 on black / white on white cases */
8075 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8076 0, 0, squareSize, squareSize, 0, 0, 1);
8078 /* Copy the piece */
8083 if(appData.upsideDown && flipView) kind ^= 2;
8084 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8086 0, 0, squareSize, squareSize,
8091 /* Animate the movement of a single piece */
8094 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8098 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8099 /* The old buffer is initialised with the start square (empty) */
8100 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8101 anim->prevFrame = *start;
8103 /* The piece will be drawn using its own bitmap as a matte */
8104 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8105 XSetClipMask(xDisplay, anim->pieceGC, mask);
8109 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8111 XRectangle updates[4];
8116 /* Save what we are about to draw into the new buffer */
8117 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8118 frame->x, frame->y, squareSize, squareSize,
8121 /* Erase bits of the previous frame */
8122 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8123 /* Where the new frame overlapped the previous,
8124 the contents in newBuf are wrong. */
8125 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8126 overlap.x, overlap.y,
8127 overlap.width, overlap.height,
8129 /* Repaint the areas in the old that don't overlap new */
8130 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8131 for (i = 0; i < count; i++)
8132 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8133 updates[i].x - anim->prevFrame.x,
8134 updates[i].y - anim->prevFrame.y,
8135 updates[i].width, updates[i].height,
8136 updates[i].x, updates[i].y);
8138 /* Easy when no overlap */
8139 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8140 0, 0, squareSize, squareSize,
8141 anim->prevFrame.x, anim->prevFrame.y);
8144 /* Save this frame for next time round */
8145 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8146 0, 0, squareSize, squareSize,
8148 anim->prevFrame = *frame;
8150 /* Draw piece over original screen contents, not current,
8151 and copy entire rect. Wipes out overlapping piece images. */
8152 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8153 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8154 0, 0, squareSize, squareSize,
8155 frame->x, frame->y);
8159 EndAnimation (AnimState *anim, XPoint *finish)
8161 XRectangle updates[4];
8166 /* The main code will redraw the final square, so we
8167 only need to erase the bits that don't overlap. */
8168 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8169 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8170 for (i = 0; i < count; i++)
8171 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8172 updates[i].x - anim->prevFrame.x,
8173 updates[i].y - anim->prevFrame.y,
8174 updates[i].width, updates[i].height,
8175 updates[i].x, updates[i].y);
8177 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8178 0, 0, squareSize, squareSize,
8179 anim->prevFrame.x, anim->prevFrame.y);
8184 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8188 BeginAnimation(anim, piece, startColor, start);
8189 for (n = 0; n < nFrames; n++) {
8190 AnimationFrame(anim, &(frames[n]), piece);
8191 FrameDelay(appData.animSpeed);
8193 EndAnimation(anim, finish);
8197 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8200 ChessSquare piece = board[fromY][toY];
8201 board[fromY][toY] = EmptySquare;
8202 DrawPosition(FALSE, board);
8204 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8205 y = lineGap + toY * (squareSize + lineGap);
8207 x = lineGap + toX * (squareSize + lineGap);
8208 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8210 for(i=1; i<4*kFactor; i++) {
8211 int r = squareSize * 9 * i/(20*kFactor - 5);
8212 XFillArc(xDisplay, xBoardWindow, highlineGC,
8213 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8214 FrameDelay(appData.animSpeed);
8216 board[fromY][toY] = piece;
8219 /* Main control logic for deciding what to animate and how */
8222 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8226 XPoint start, finish, mid;
8227 XPoint frames[kFactor * 2 + 1];
8228 int nFrames, startColor, endColor;
8230 /* Are we animating? */
8231 if (!appData.animate || appData.blindfold)
8234 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8235 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8236 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8238 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8239 piece = board[fromY][fromX];
8240 if (piece >= EmptySquare) return;
8245 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8248 if (appData.debugMode) {
8249 fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8250 _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8251 piece, fromX, fromY, toX, toY); }
8253 ScreenSquare(fromX, fromY, &start, &startColor);
8254 ScreenSquare(toX, toY, &finish, &endColor);
8257 /* Knight: make straight movement then diagonal */
8258 if (abs(toY - fromY) < abs(toX - fromX)) {
8259 mid.x = start.x + (finish.x - start.x) / 2;
8263 mid.y = start.y + (finish.y - start.y) / 2;
8266 mid.x = start.x + (finish.x - start.x) / 2;
8267 mid.y = start.y + (finish.y - start.y) / 2;
8270 /* Don't use as many frames for very short moves */
8271 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8272 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8274 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8275 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8276 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8278 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8279 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8282 /* Be sure end square is redrawn */
8283 damage[0][toY][toX] = True;
8287 DragPieceBegin (int x, int y, Boolean instantly)
8289 int boardX, boardY, color;
8292 /* Are we animating? */
8293 if (!appData.animateDragging || appData.blindfold)
8296 /* Figure out which square we start in and the
8297 mouse position relative to top left corner. */
8298 BoardSquare(x, y, &boardX, &boardY);
8299 player.startBoardX = boardX;
8300 player.startBoardY = boardY;
8301 ScreenSquare(boardX, boardY, &corner, &color);
8302 player.startSquare = corner;
8303 player.startColor = color;
8304 /* As soon as we start dragging, the piece will jump slightly to
8305 be centered over the mouse pointer. */
8306 player.mouseDelta.x = squareSize/2;
8307 player.mouseDelta.y = squareSize/2;
8308 /* Initialise animation */
8309 player.dragPiece = PieceForSquare(boardX, boardY);
8311 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8312 player.dragActive = True;
8313 BeginAnimation(&player, player.dragPiece, color, &corner);
8314 /* Mark this square as needing to be redrawn. Note that
8315 we don't remove the piece though, since logically (ie
8316 as seen by opponent) the move hasn't been made yet. */
8317 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8318 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8319 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8320 corner.x, corner.y, squareSize, squareSize,
8321 0, 0); // [HGM] zh: unstack in stead of grab
8322 if(gatingPiece != EmptySquare) {
8323 /* Kludge alert: When gating we want the introduced
8324 piece to appear on the from square. To generate an
8325 image of it, we draw it on the board, copy the image,
8326 and draw the original piece again. */
8327 ChessSquare piece = boards[currentMove][boardY][boardX];
8328 DrawSquare(boardY, boardX, gatingPiece, 0);
8329 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8330 corner.x, corner.y, squareSize, squareSize, 0, 0);
8331 DrawSquare(boardY, boardX, piece, 0);
8333 damage[0][boardY][boardX] = True;
8335 player.dragActive = False;
8340 ChangeDragPiece (ChessSquare piece)
8343 player.dragPiece = piece;
8344 /* The piece will be drawn using its own bitmap as a matte */
8345 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8346 XSetClipMask(xDisplay, player.pieceGC, mask);
8350 DragPieceMove (int x, int y)
8354 /* Are we animating? */
8355 if (!appData.animateDragging || appData.blindfold)
8359 if (! player.dragActive)
8361 /* Move piece, maintaining same relative position
8362 of mouse within square */
8363 corner.x = x - player.mouseDelta.x;
8364 corner.y = y - player.mouseDelta.y;
8365 AnimationFrame(&player, &corner, player.dragPiece);
8367 if (appData.highlightDragging) {
8369 BoardSquare(x, y, &boardX, &boardY);
8370 SetHighlights(fromX, fromY, boardX, boardY);
8376 DragPieceEnd (int x, int y)
8378 int boardX, boardY, color;
8381 /* Are we animating? */
8382 if (!appData.animateDragging || appData.blindfold)
8386 if (! player.dragActive)
8388 /* Last frame in sequence is square piece is
8389 placed on, which may not match mouse exactly. */
8390 BoardSquare(x, y, &boardX, &boardY);
8391 ScreenSquare(boardX, boardY, &corner, &color);
8392 EndAnimation(&player, &corner);
8394 /* Be sure end square is redrawn */
8395 damage[0][boardY][boardX] = True;
8397 /* This prevents weird things happening with fast successive
8398 clicks which on my Sun at least can cause motion events
8399 without corresponding press/release. */
8400 player.dragActive = False;
8403 /* Handle expose event while piece being dragged */
8408 if (!player.dragActive || appData.blindfold)
8411 /* What we're doing: logically, the move hasn't been made yet,
8412 so the piece is still in it's original square. But visually
8413 it's being dragged around the board. So we erase the square
8414 that the piece is on and draw it at the last known drag point. */
8415 BlankSquare(player.startSquare.x, player.startSquare.y,
8416 player.startColor, EmptySquare, xBoardWindow, 1);
8417 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8418 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8421 #include <sys/ioctl.h>
8425 int fd, default_width;
8428 default_width = 79; // this is FICS default anyway...
8430 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8432 if (!ioctl(fd, TIOCGSIZE, &win))
8433 default_width = win.ts_cols;
8434 #elif defined(TIOCGWINSZ)
8436 if (!ioctl(fd, TIOCGWINSZ, &win))
8437 default_width = win.ws_col;
8439 return default_width;
8445 static int old_width = 0;
8446 int new_width = get_term_width();
8448 if (old_width != new_width)
8449 ics_printf("set width %d\n", new_width);
8450 old_width = new_width;
8454 NotifyFrontendLogin ()
8459 /* [AS] Arrow highlighting support */
8461 static double A_WIDTH = 5; /* Width of arrow body */
8463 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8464 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8475 return (int) (x + 0.5);
8479 SquareToPos (int rank, int file, int *x, int *y)
8482 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8483 *y = lineGap + rank * (squareSize + lineGap);
8485 *x = lineGap + file * (squareSize + lineGap);
8486 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8490 /* Draw an arrow between two points using current settings */
8492 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8495 double dx, dy, j, k, x, y;
8498 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8500 arrow[0].x = s_x + A_WIDTH + 0.5;
8503 arrow[1].x = s_x + A_WIDTH + 0.5;
8504 arrow[1].y = d_y - h;
8506 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8507 arrow[2].y = d_y - h;
8512 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8513 arrow[5].y = d_y - h;
8515 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8516 arrow[4].y = d_y - h;
8518 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8521 else if( d_y == s_y ) {
8522 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8525 arrow[0].y = s_y + A_WIDTH + 0.5;
8527 arrow[1].x = d_x - w;
8528 arrow[1].y = s_y + A_WIDTH + 0.5;
8530 arrow[2].x = d_x - w;
8531 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8536 arrow[5].x = d_x - w;
8537 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8539 arrow[4].x = d_x - w;
8540 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8543 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8546 /* [AS] Needed a lot of paper for this! :-) */
8547 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8548 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8550 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8552 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8557 arrow[0].x = Round(x - j);
8558 arrow[0].y = Round(y + j*dx);
8560 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8561 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8564 x = (double) d_x - k;
8565 y = (double) d_y - k*dy;
8568 x = (double) d_x + k;
8569 y = (double) d_y + k*dy;
8572 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8574 arrow[6].x = Round(x - j);
8575 arrow[6].y = Round(y + j*dx);
8577 arrow[2].x = Round(arrow[6].x + 2*j);
8578 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8580 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8581 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8586 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8587 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8590 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8591 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8592 // Polygon( hdc, arrow, 7 );
8596 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8599 hor = 64*s_col + 32; vert = 64*s_row + 32;
8600 for(i=0; i<= 64; i++) {
8601 damage[0][vert+6>>6][hor+6>>6] = True;
8602 damage[0][vert-6>>6][hor+6>>6] = True;
8603 damage[0][vert+6>>6][hor-6>>6] = True;
8604 damage[0][vert-6>>6][hor-6>>6] = True;
8605 hor += d_col - s_col; vert += d_row - s_row;
8609 /* [AS] Draw an arrow between two squares */
8611 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8613 int s_x, s_y, d_x, d_y;
8615 if( s_col == d_col && s_row == d_row ) {
8619 /* Get source and destination points */
8620 SquareToPos( s_row, s_col, &s_x, &s_y);
8621 SquareToPos( d_row, d_col, &d_x, &d_y);
8624 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8626 else if( d_y < s_y ) {
8627 d_y += squareSize / 2 + squareSize / 4;
8630 d_y += squareSize / 2;
8634 d_x += squareSize / 2 - squareSize / 4;
8636 else if( d_x < s_x ) {
8637 d_x += squareSize / 2 + squareSize / 4;
8640 d_x += squareSize / 2;
8643 s_x += squareSize / 2;
8644 s_y += squareSize / 2;
8647 A_WIDTH = squareSize / 14.; //[HGM] make float
8649 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8650 ArrowDamage(s_col, s_row, d_col, d_row);
8654 IsDrawArrowEnabled ()
8656 return appData.highlightMoveWithArrow && squareSize >= 32;
8660 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8662 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8663 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8667 UpdateLogos (int displ)
8669 return; // no logos in XBoard yet