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 Dimension textHeight;
544 Pixel timerForegroundPixel, timerBackgroundPixel;
545 Pixel buttonForegroundPixel, buttonBackgroundPixel;
546 char *chessDir, *programName, *programVersion,
547 *gameCopyFilename, *gamePasteFilename;
548 Boolean alwaysOnTop = False;
549 Boolean saveSettingsOnExit;
550 char *settingsFileName;
551 char *icsTextMenuString;
553 char *firstChessProgramNames;
554 char *secondChessProgramNames;
556 WindowPlacement wpMain;
557 WindowPlacement wpConsole;
558 WindowPlacement wpComment;
559 WindowPlacement wpMoveHistory;
560 WindowPlacement wpEvalGraph;
561 WindowPlacement wpEngineOutput;
562 WindowPlacement wpGameList;
563 WindowPlacement wpTags;
565 extern Widget shells[];
566 extern Boolean shellUp[];
570 Pixmap pieceBitmap[2][(int)BlackPawn];
571 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
572 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
573 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
574 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
575 Pixmap xpmBoardBitmap[2];
576 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
577 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
578 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
579 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
580 XImage *ximLightSquare, *ximDarkSquare;
583 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
584 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
586 #define White(piece) ((int)(piece) < (int)BlackPawn)
588 /* Variables for doing smooth animation. This whole thing
589 would be much easier if the board was double-buffered,
590 but that would require a fairly major rewrite. */
595 GC blitGC, pieceGC, outlineGC;
596 XPoint startSquare, prevFrame, mouseDelta;
600 int startBoardX, startBoardY;
603 /* There can be two pieces being animated at once: a player
604 can begin dragging a piece before the remote opponent has moved. */
606 static AnimState game, player;
608 /* Bitmaps for use as masks when drawing XPM pieces.
609 Need one for each black and white piece. */
610 static Pixmap xpmMask[BlackKing + 1];
612 /* This magic number is the number of intermediate frames used
613 in each half of the animation. For short moves it's reduced
614 by 1. The total number of frames will be factor * 2 + 1. */
617 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
619 MenuItem fileMenu[] = {
620 {N_("New Game Ctrl+N"), "New Game", ResetProc},
621 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
622 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
623 {"----", NULL, NothingProc},
624 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
625 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
626 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
627 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
628 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
629 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
630 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
631 {"----", NULL, NothingProc},
632 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
633 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
634 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
635 {"----", NULL, NothingProc},
636 {N_("Mail Move"), "Mail Move", MailMoveProc},
637 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
638 {"----", NULL, NothingProc},
639 {N_("Quit Ctr+Q"), "Exit", QuitProc},
643 MenuItem editMenu[] = {
644 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
645 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
646 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
647 {"----", NULL, NothingProc},
648 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
649 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
650 {"----", NULL, NothingProc},
651 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
652 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
653 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
654 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
655 {N_("Edit Book"), "Edit Book", EditBookProc},
656 {"----", NULL, NothingProc},
657 {N_("Revert Home"), "Revert", RevertProc},
658 {N_("Annotate"), "Annotate", AnnotateProc},
659 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
660 {"----", NULL, NothingProc},
661 {N_("Backward Alt+Left"), "Backward", BackwardProc},
662 {N_("Forward Alt+Right"), "Forward", ForwardProc},
663 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
664 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
668 MenuItem viewMenu[] = {
669 {N_("Flip View F2"), "Flip View", FlipViewProc},
670 {"----", NULL, NothingProc},
671 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
672 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
673 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
674 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
675 {N_("ICS text menu"), "ICStex", IcsTextProc},
676 {"----", NULL, NothingProc},
677 {N_("Tags"), "Show Tags", EditTagsProc},
678 {N_("Comments"), "Show Comments", EditCommentProc},
679 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
680 {"----", NULL, NothingProc},
681 {N_("Board..."), "Board Options", BoardOptionsProc},
682 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
686 MenuItem modeMenu[] = {
687 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
688 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
689 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
690 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
691 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
692 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
693 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
694 {N_("Training"), "Training", TrainingProc},
695 {N_("ICS Client"), "ICS Client", IcsClientProc},
696 {"----", NULL, NothingProc},
697 {N_("Machine Match"), "Machine Match", MatchProc},
698 {N_("Pause Pause"), "Pause", PauseProc},
702 MenuItem actionMenu[] = {
703 {N_("Accept F3"), "Accept", AcceptProc},
704 {N_("Decline F4"), "Decline", DeclineProc},
705 {N_("Rematch F12"), "Rematch", RematchProc},
706 {"----", NULL, NothingProc},
707 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
708 {N_("Draw F6"), "Draw", DrawProc},
709 {N_("Adjourn F7"), "Adjourn", AdjournProc},
710 {N_("Abort F8"),"Abort", AbortProc},
711 {N_("Resign F9"), "Resign", ResignProc},
712 {"----", NULL, NothingProc},
713 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
714 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
715 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
716 {"----", NULL, NothingProc},
717 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
718 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
719 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
723 MenuItem engineMenu[] = {
724 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
725 {"----", NULL, NothingProc},
726 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
727 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
728 {"----", NULL, NothingProc},
729 {N_("Hint"), "Hint", HintProc},
730 {N_("Book"), "Book", BookProc},
731 {"----", NULL, NothingProc},
732 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
733 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
737 MenuItem optionsMenu[] = {
738 #define OPTIONSDIALOG
740 {N_("General ..."), "General", OptionsProc},
742 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
743 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
744 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
745 {N_("ICS ..."), "ICS", IcsOptionsProc},
746 {N_("Match ..."), "Match", MatchOptionsProc},
747 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
748 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
749 // {N_(" ..."), "", OptionsProc},
750 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
751 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
752 {"----", NULL, NothingProc},
753 #ifndef OPTIONSDIALOG
754 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
755 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
756 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
757 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
758 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
759 {N_("Blindfold"), "Blindfold", BlindfoldProc},
760 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
762 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
764 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
765 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
766 {N_("Move Sound"), "Move Sound", MoveSoundProc},
767 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
768 {N_("One-Click Moving"), "OneClick", OneClickProc},
769 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
770 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
771 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
772 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
773 // {N_("Premove"), "Premove", PremoveProc},
774 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
775 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
776 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
777 {"----", NULL, NothingProc},
779 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
780 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
784 MenuItem helpMenu[] = {
785 {N_("Info XBoard"), "Info XBoard", InfoProc},
786 {N_("Man XBoard F1"), "Man XBoard", ManProc},
787 {"----", NULL, NothingProc},
788 {N_("About XBoard"), "About XBoard", AboutProc},
793 {N_("File"), "File", fileMenu},
794 {N_("Edit"), "Edit", editMenu},
795 {N_("View"), "View", viewMenu},
796 {N_("Mode"), "Mode", modeMenu},
797 {N_("Action"), "Action", actionMenu},
798 {N_("Engine"), "Engine", engineMenu},
799 {N_("Options"), "Options", optionsMenu},
800 {N_("Help"), "Help", helpMenu},
804 #define PAUSE_BUTTON "P"
805 MenuItem buttonBar[] = {
806 {"<<", "<<", ToStartProc},
807 {"<", "<", BackwardProc},
808 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
809 {">", ">", ForwardProc},
810 {">>", ">>", ToEndProc},
814 #define PIECE_MENU_SIZE 18
815 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
816 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
817 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
818 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
819 N_("Empty square"), N_("Clear board") },
820 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
821 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
822 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
823 N_("Empty square"), N_("Clear board") }
825 /* must be in same order as pieceMenuStrings! */
826 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
827 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
828 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
829 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
830 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
831 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
832 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
833 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
834 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
837 #define DROP_MENU_SIZE 6
838 String dropMenuStrings[DROP_MENU_SIZE] = {
839 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
841 /* must be in same order as dropMenuStrings! */
842 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
843 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
844 WhiteRook, WhiteQueen
852 DropMenuEnables dmEnables[] = {
870 { XtNborderWidth, 0 },
871 { XtNdefaultDistance, 0 },
875 { XtNborderWidth, 0 },
876 { XtNresizable, (XtArgVal) True },
880 { XtNborderWidth, 0 },
886 { XtNjustify, (XtArgVal) XtJustifyRight },
887 { XtNlabel, (XtArgVal) "..." },
888 { XtNresizable, (XtArgVal) True },
889 { XtNresize, (XtArgVal) False }
892 Arg messageArgs[] = {
893 { XtNjustify, (XtArgVal) XtJustifyLeft },
894 { XtNlabel, (XtArgVal) "..." },
895 { XtNresizable, (XtArgVal) True },
896 { XtNresize, (XtArgVal) False }
900 { XtNborderWidth, 0 },
901 { XtNjustify, (XtArgVal) XtJustifyLeft }
904 XtResource clientResources[] = {
905 { "flashCount", "flashCount", XtRInt, sizeof(int),
906 XtOffset(AppDataPtr, flashCount), XtRImmediate,
907 (XtPointer) FLASH_COUNT },
910 XrmOptionDescRec shellOptions[] = {
911 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
912 { "-flash", "flashCount", XrmoptionNoArg, "3" },
913 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
916 XtActionsRec boardActions[] = {
917 { "DrawPosition", DrawPositionProc },
918 { "HandleUserMove", HandleUserMove },
919 { "AnimateUserMove", AnimateUserMove },
920 { "HandlePV", HandlePV },
921 { "SelectPV", SelectPV },
922 { "StopPV", StopPV },
923 { "FileNameAction", FileNameAction },
924 { "AskQuestionProc", AskQuestionProc },
925 { "AskQuestionReplyAction", AskQuestionReplyAction },
926 { "PieceMenuPopup", PieceMenuPopup },
927 { "WhiteClock", WhiteClock },
928 { "BlackClock", BlackClock },
929 { "ResetProc", ResetProc },
930 { "NewVariantProc", NewVariantProc },
931 { "LoadGameProc", LoadGameProc },
932 { "LoadNextGameProc", LoadNextGameProc },
933 { "LoadPrevGameProc", LoadPrevGameProc },
934 { "LoadSelectedProc", LoadSelectedProc },
935 { "SetFilterProc", SetFilterProc },
936 { "ReloadGameProc", ReloadGameProc },
937 { "LoadPositionProc", LoadPositionProc },
938 { "LoadNextPositionProc", LoadNextPositionProc },
939 { "LoadPrevPositionProc", LoadPrevPositionProc },
940 { "ReloadPositionProc", ReloadPositionProc },
941 { "CopyPositionProc", CopyPositionProc },
942 { "PastePositionProc", PastePositionProc },
943 { "CopyGameProc", CopyGameProc },
944 { "CopyGameListProc", CopyGameListProc },
945 { "PasteGameProc", PasteGameProc },
946 { "SaveGameProc", SaveGameProc },
947 { "SavePositionProc", SavePositionProc },
948 { "MailMoveProc", MailMoveProc },
949 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
950 { "QuitProc", QuitProc },
951 { "MachineWhiteProc", MachineWhiteProc },
952 { "MachineBlackProc", MachineBlackProc },
953 { "AnalysisModeProc", AnalyzeModeProc },
954 { "AnalyzeFileProc", AnalyzeFileProc },
955 { "TwoMachinesProc", TwoMachinesProc },
956 { "IcsClientProc", IcsClientProc },
957 { "EditGameProc", EditGameProc },
958 { "EditPositionProc", EditPositionProc },
959 { "TrainingProc", EditPositionProc },
960 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
961 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
962 { "ShowGameListProc", ShowGameListProc },
963 { "ShowMoveListProc", HistoryShowProc},
964 { "EditTagsProc", EditTagsProc },
965 { "EditBookProc", EditBookProc },
966 { "EditCommentProc", EditCommentProc },
967 { "IcsInputBoxProc", IcsInputBoxProc },
968 { "PauseProc", PauseProc },
969 { "AcceptProc", AcceptProc },
970 { "DeclineProc", DeclineProc },
971 { "RematchProc", RematchProc },
972 { "CallFlagProc", CallFlagProc },
973 { "DrawProc", DrawProc },
974 { "AdjournProc", AdjournProc },
975 { "AbortProc", AbortProc },
976 { "ResignProc", ResignProc },
977 { "AdjuWhiteProc", AdjuWhiteProc },
978 { "AdjuBlackProc", AdjuBlackProc },
979 { "AdjuDrawProc", AdjuDrawProc },
980 { "TypeInProc", TypeInProc },
981 { "EnterKeyProc", EnterKeyProc },
982 { "UpKeyProc", UpKeyProc },
983 { "DownKeyProc", DownKeyProc },
984 { "StopObservingProc", StopObservingProc },
985 { "StopExaminingProc", StopExaminingProc },
986 { "UploadProc", UploadProc },
987 { "BackwardProc", BackwardProc },
988 { "ForwardProc", ForwardProc },
989 { "TempBackwardProc", TempBackwardProc },
990 { "TempForwardProc", TempForwardProc },
991 { "ToStartProc", ToStartProc },
992 { "ToEndProc", ToEndProc },
993 { "RevertProc", RevertProc },
994 { "AnnotateProc", AnnotateProc },
995 { "TruncateGameProc", TruncateGameProc },
996 { "MoveNowProc", MoveNowProc },
997 { "RetractMoveProc", RetractMoveProc },
998 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
999 { "UciMenuProc", (XtActionProc) UciMenuProc },
1000 { "TimeControlProc", (XtActionProc) TimeControlProc },
1001 { "FlipViewProc", FlipViewProc },
1002 { "PonderNextMoveProc", PonderNextMoveProc },
1003 #ifndef OPTIONSDIALOG
1004 { "AlwaysQueenProc", AlwaysQueenProc },
1005 { "AnimateDraggingProc", AnimateDraggingProc },
1006 { "AnimateMovingProc", AnimateMovingProc },
1007 { "AutoflagProc", AutoflagProc },
1008 { "AutoflipProc", AutoflipProc },
1009 { "BlindfoldProc", BlindfoldProc },
1010 { "FlashMovesProc", FlashMovesProc },
1012 { "HighlightDraggingProc", HighlightDraggingProc },
1014 { "HighlightLastMoveProc", HighlightLastMoveProc },
1015 // { "IcsAlarmProc", IcsAlarmProc },
1016 { "MoveSoundProc", MoveSoundProc },
1017 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1018 { "PopupExitMessageProc", PopupExitMessageProc },
1019 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1020 // { "PremoveProc", PremoveProc },
1021 { "ShowCoordsProc", ShowCoordsProc },
1022 { "ShowThinkingProc", ShowThinkingProc },
1023 { "HideThinkingProc", HideThinkingProc },
1024 { "TestLegalityProc", TestLegalityProc },
1026 { "SaveSettingsProc", SaveSettingsProc },
1027 { "SaveOnExitProc", SaveOnExitProc },
1028 { "InfoProc", InfoProc },
1029 { "ManProc", ManProc },
1030 { "HintProc", HintProc },
1031 { "BookProc", BookProc },
1032 { "AboutGameProc", AboutGameProc },
1033 { "AboutProc", AboutProc },
1034 { "DebugProc", DebugProc },
1035 { "NothingProc", NothingProc },
1036 { "CommentClick", (XtActionProc) CommentClick },
1037 { "CommentPopDown", (XtActionProc) CommentPopDown },
1038 { "TagsPopDown", (XtActionProc) TagsPopDown },
1039 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1040 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1041 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1042 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1043 { "GameListPopDown", (XtActionProc) GameListPopDown },
1044 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1045 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1046 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1047 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1048 { "GenericPopDown", (XtActionProc) GenericPopDown },
1049 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1050 { "SelectMove", (XtActionProc) SelectMove },
1053 char globalTranslations[] =
1054 ":<Key>F9: ResignProc() \n \
1055 :Ctrl<Key>n: ResetProc() \n \
1056 :Meta<Key>V: NewVariantProc() \n \
1057 :Ctrl<Key>o: LoadGameProc() \n \
1058 :Meta<Key>Next: LoadNextGameProc() \n \
1059 :Meta<Key>Prior: LoadPrevGameProc() \n \
1060 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1061 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1062 :Ctrl<Key>s: SaveGameProc() \n \
1063 :Ctrl<Key>c: CopyGameProc() \n \
1064 :Ctrl<Key>v: PasteGameProc() \n \
1065 :Ctrl<Key>O: LoadPositionProc() \n \
1066 :Shift<Key>Next: LoadNextPositionProc() \n \
1067 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1068 :Ctrl<Key>S: SavePositionProc() \n \
1069 :Ctrl<Key>C: CopyPositionProc() \n \
1070 :Ctrl<Key>V: PastePositionProc() \n \
1071 :Ctrl<Key>q: QuitProc() \n \
1072 :Ctrl<Key>w: MachineWhiteProc() \n \
1073 :Ctrl<Key>b: MachineBlackProc() \n \
1074 :Ctrl<Key>t: TwoMachinesProc() \n \
1075 :Ctrl<Key>a: AnalysisModeProc() \n \
1076 :Ctrl<Key>g: AnalyzeFileProc() \n \
1077 :Ctrl<Key>e: EditGameProc() \n \
1078 :Ctrl<Key>E: EditPositionProc() \n \
1079 :Meta<Key>O: EngineOutputProc() \n \
1080 :Meta<Key>E: EvalGraphProc() \n \
1081 :Meta<Key>G: ShowGameListProc() \n \
1082 :Meta<Key>H: ShowMoveListProc() \n \
1083 :<Key>Pause: PauseProc() \n \
1084 :<Key>F3: AcceptProc() \n \
1085 :<Key>F4: DeclineProc() \n \
1086 :<Key>F12: RematchProc() \n \
1087 :<Key>F5: CallFlagProc() \n \
1088 :<Key>F6: DrawProc() \n \
1089 :<Key>F7: AdjournProc() \n \
1090 :<Key>F8: AbortProc() \n \
1091 :<Key>F10: StopObservingProc() \n \
1092 :<Key>F11: StopExaminingProc() \n \
1093 :Meta Ctrl<Key>F12: DebugProc() \n \
1094 :Meta<Key>End: ToEndProc() \n \
1095 :Meta<Key>Right: ForwardProc() \n \
1096 :Meta<Key>Home: ToStartProc() \n \
1097 :Meta<Key>Left: BackwardProc() \n \
1098 :<Key>Left: BackwardProc() \n \
1099 :<Key>Right: ForwardProc() \n \
1100 :<Key>Home: RevertProc() \n \
1101 :<Key>End: TruncateGameProc() \n \
1102 :Ctrl<Key>m: MoveNowProc() \n \
1103 :Ctrl<Key>x: RetractMoveProc() \n \
1104 :Meta<Key>J: EngineMenuProc() \n \
1105 :Meta<Key>U: UciMenuProc() \n \
1106 :Meta<Key>T: TimeControlProc() \n \
1107 :Ctrl<Key>P: PonderNextMoveProc() \n "
1108 #ifndef OPTIONSDIALOG
1110 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1111 :Ctrl<Key>F: AutoflagProc() \n \
1112 :Ctrl<Key>A: AnimateMovingProc() \n \
1113 :Ctrl<Key>L: TestLegalityProc() \n \
1114 :Ctrl<Key>H: HideThinkingProc() \n "
1117 :<Key>F1: ManProc() \n \
1118 :<Key>F2: FlipViewProc() \n \
1119 :<KeyDown>Return: TempBackwardProc() \n \
1120 :<KeyUp>Return: TempForwardProc() \n";
1122 char boardTranslations[] =
1123 "<Btn1Down>: HandleUserMove(0) \n \
1124 Shift<Btn1Up>: HandleUserMove(1) \n \
1125 <Btn1Up>: HandleUserMove(0) \n \
1126 <Btn1Motion>: AnimateUserMove() \n \
1127 <Btn3Motion>: HandlePV() \n \
1128 <Btn2Motion>: HandlePV() \n \
1129 <Btn3Up>: PieceMenuPopup(menuB) \n \
1130 <Btn2Up>: PieceMenuPopup(menuB) \n \
1131 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1132 PieceMenuPopup(menuB) \n \
1133 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1134 PieceMenuPopup(menuW) \n \
1135 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1136 PieceMenuPopup(menuW) \n \
1137 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1138 PieceMenuPopup(menuB) \n";
1140 char whiteTranslations[] =
1141 "Shift<BtnDown>: WhiteClock(1)\n \
1142 <BtnDown>: WhiteClock(0)\n";
1143 char blackTranslations[] =
1144 "Shift<BtnDown>: BlackClock(1)\n \
1145 <BtnDown>: BlackClock(0)\n";
1147 char ICSInputTranslations[] =
1148 "<Key>Up: UpKeyProc() \n "
1149 "<Key>Down: DownKeyProc() \n "
1150 "<Key>Return: EnterKeyProc() \n";
1152 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1153 // as the widget is destroyed before the up-click can call extend-end
1154 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1156 String xboardResources[] = {
1157 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1158 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1159 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1164 /* Max possible square size */
1165 #define MAXSQSIZE 256
1167 static int xpm_avail[MAXSQSIZE];
1169 #ifdef HAVE_DIR_STRUCT
1171 /* Extract piece size from filename */
1173 xpm_getsize (char *name, int len, char *ext)
1181 if ((p=strchr(name, '.')) == NULL ||
1182 StrCaseCmp(p+1, ext) != 0)
1188 while (*p && isdigit(*p))
1195 /* Setup xpm_avail */
1197 xpm_getavail (char *dirname, char *ext)
1203 for (i=0; i<MAXSQSIZE; ++i)
1206 if (appData.debugMode)
1207 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1209 dir = opendir(dirname);
1212 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1213 programName, dirname);
1217 while ((ent=readdir(dir)) != NULL) {
1218 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1219 if (i > 0 && i < MAXSQSIZE)
1229 xpm_print_avail (FILE *fp, char *ext)
1233 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1234 for (i=1; i<MAXSQSIZE; ++i) {
1240 /* Return XPM piecesize closest to size */
1242 xpm_closest_to (char *dirname, int size, char *ext)
1245 int sm_diff = MAXSQSIZE;
1249 xpm_getavail(dirname, ext);
1251 if (appData.debugMode)
1252 xpm_print_avail(stderr, ext);
1254 for (i=1; i<MAXSQSIZE; ++i) {
1257 diff = (diff<0) ? -diff : diff;
1258 if (diff < sm_diff) {
1266 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1272 #else /* !HAVE_DIR_STRUCT */
1273 /* If we are on a system without a DIR struct, we can't
1274 read the directory, so we can't collect a list of
1275 filenames, etc., so we can't do any size-fitting. */
1277 xpm_closest_to (char *dirname, int size, char *ext)
1279 fprintf(stderr, _("\
1280 Warning: No DIR structure found on this system --\n\
1281 Unable to autosize for XPM/XIM pieces.\n\
1282 Please report this error to %s.\n\
1283 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1286 #endif /* HAVE_DIR_STRUCT */
1288 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1289 "magenta", "cyan", "white" };
1293 TextColors textColors[(int)NColorClasses];
1295 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1297 parse_color (char *str, int which)
1299 char *p, buf[100], *d;
1302 if (strlen(str) > 99) /* watch bounds on buf */
1307 for (i=0; i<which; ++i) {
1314 /* Could be looking at something like:
1316 .. in which case we want to stop on a comma also */
1317 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1321 return -1; /* Use default for empty field */
1324 if (which == 2 || isdigit(*p))
1327 while (*p && isalpha(*p))
1332 for (i=0; i<8; ++i) {
1333 if (!StrCaseCmp(buf, cnames[i]))
1334 return which? (i+40) : (i+30);
1336 if (!StrCaseCmp(buf, "default")) return -1;
1338 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1343 parse_cpair (ColorClass cc, char *str)
1345 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1346 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1351 /* bg and attr are optional */
1352 textColors[(int)cc].bg = parse_color(str, 1);
1353 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1354 textColors[(int)cc].attr = 0;
1360 /* Arrange to catch delete-window events */
1361 Atom wm_delete_window;
1363 CatchDeleteWindow (Widget w, String procname)
1366 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1367 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1368 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1375 XtSetArg(args[0], XtNiconic, False);
1376 XtSetValues(shellWidget, args, 1);
1378 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1381 //---------------------------------------------------------------------------------------------------------
1382 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1385 #define CW_USEDEFAULT (1<<31)
1386 #define ICS_TEXT_MENU_SIZE 90
1387 #define DEBUG_FILE "xboard.debug"
1388 #define SetCurrentDirectory chdir
1389 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1393 // these two must some day move to frontend.h, when they are implemented
1394 Boolean GameListIsUp();
1396 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1399 // front-end part of option handling
1401 // [HGM] This platform-dependent table provides the location for storing the color info
1402 extern char *crWhite, * crBlack;
1406 &appData.whitePieceColor,
1407 &appData.blackPieceColor,
1408 &appData.lightSquareColor,
1409 &appData.darkSquareColor,
1410 &appData.highlightSquareColor,
1411 &appData.premoveHighlightColor,
1412 &appData.lowTimeWarningColor,
1423 // [HGM] font: keep a font for each square size, even non-stndard ones
1424 #define NUM_SIZES 18
1425 #define MAX_SIZE 130
1426 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1427 char *fontTable[NUM_FONTS][MAX_SIZE];
1430 ParseFont (char *name, int number)
1431 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1433 if(sscanf(name, "size%d:", &size)) {
1434 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1435 // defer processing it until we know if it matches our board size
1436 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1437 fontTable[number][size] = strdup(strchr(name, ':')+1);
1438 fontValid[number][size] = True;
1443 case 0: // CLOCK_FONT
1444 appData.clockFont = strdup(name);
1446 case 1: // MESSAGE_FONT
1447 appData.font = strdup(name);
1449 case 2: // COORD_FONT
1450 appData.coordFont = strdup(name);
1455 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1460 { // only 2 fonts currently
1461 appData.clockFont = CLOCK_FONT_NAME;
1462 appData.coordFont = COORD_FONT_NAME;
1463 appData.font = DEFAULT_FONT_NAME;
1468 { // no-op, until we identify the code for this already in XBoard and move it here
1472 ParseColor (int n, char *name)
1473 { // in XBoard, just copy the color-name string
1474 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1478 ParseTextAttribs (ColorClass cc, char *s)
1480 (&appData.colorShout)[cc] = strdup(s);
1484 ParseBoardSize (void *addr, char *name)
1486 appData.boardSize = strdup(name);
1491 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1495 SetCommPortDefaults ()
1496 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1499 // [HGM] args: these three cases taken out to stay in front-end
1501 SaveFontArg (FILE *f, ArgDescriptor *ad)
1504 int i, n = (int)(intptr_t)ad->argLoc;
1506 case 0: // CLOCK_FONT
1507 name = appData.clockFont;
1509 case 1: // MESSAGE_FONT
1510 name = appData.font;
1512 case 2: // COORD_FONT
1513 name = appData.coordFont;
1518 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1519 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1520 fontTable[n][squareSize] = strdup(name);
1521 fontValid[n][squareSize] = True;
1524 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1525 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1530 { // nothing to do, as the sounds are at all times represented by their text-string names already
1534 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1535 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1536 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1540 SaveColor (FILE *f, ArgDescriptor *ad)
1541 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1542 if(colorVariable[(int)(intptr_t)ad->argLoc])
1543 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1547 SaveBoardSize (FILE *f, char *name, void *addr)
1548 { // wrapper to shield back-end from BoardSize & sizeInfo
1549 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1553 ParseCommPortSettings (char *s)
1554 { // no such option in XBoard (yet)
1557 extern Widget engineOutputShell;
1561 GetActualPlacement (Widget wg, WindowPlacement *wp)
1566 XWindowAttributes winAt;
1573 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1574 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1575 wp->x = rx - winAt.x;
1576 wp->y = ry - winAt.y;
1577 wp->height = winAt.height;
1578 wp->width = winAt.width;
1579 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1584 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1585 // In XBoard this will have to wait until awareness of window parameters is implemented
1586 GetActualPlacement(shellWidget, &wpMain);
1587 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1588 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1589 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1590 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1591 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1592 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1596 PrintCommPortSettings (FILE *f, char *name)
1597 { // This option does not exist in XBoard
1601 MySearchPath (char *installDir, char *name, char *fullname)
1602 { // just append installDir and name. Perhaps ExpandPath should be used here?
1603 name = ExpandPathName(name);
1604 if(name && name[0] == '/')
1605 safeStrCpy(fullname, name, MSG_SIZ );
1607 sprintf(fullname, "%s%c%s", installDir, '/', name);
1613 MyGetFullPathName (char *name, char *fullname)
1614 { // should use ExpandPath?
1615 name = ExpandPathName(name);
1616 safeStrCpy(fullname, name, MSG_SIZ );
1621 EnsureOnScreen (int *x, int *y, int minX, int minY)
1628 { // [HGM] args: allows testing if main window is realized from back-end
1629 return xBoardWindow != 0;
1633 PopUpStartupDialog ()
1634 { // start menu not implemented in XBoard
1638 ConvertToLine (int argc, char **argv)
1640 static char line[128*1024], buf[1024];
1644 for(i=1; i<argc; i++)
1646 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1647 && argv[i][0] != '{' )
1648 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1650 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1651 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1654 line[strlen(line)-1] = NULLCHAR;
1658 //--------------------------------------------------------------------------------------------
1660 extern Boolean twoBoards, partnerUp;
1663 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1665 #define BoardSize int
1667 InitDrawingSizes (BoardSize boardSize, int flags)
1668 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1669 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1671 XtGeometryResult gres;
1673 static Dimension oldWidth, oldHeight;
1674 static VariantClass oldVariant;
1675 static int oldDual = -1, oldMono = -1;
1677 if(!formWidget) return;
1679 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1680 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1681 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1683 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1685 * Enable shell resizing.
1687 shellArgs[0].value = (XtArgVal) &w;
1688 shellArgs[1].value = (XtArgVal) &h;
1689 XtGetValues(shellWidget, shellArgs, 2);
1691 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1692 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1693 XtSetValues(shellWidget, &shellArgs[2], 4);
1695 XtSetArg(args[0], XtNdefaultDistance, &sep);
1696 XtGetValues(formWidget, args, 1);
1698 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1700 hOffset = boardWidth + 10;
1701 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1702 secondSegments[i] = gridSegments[i];
1703 secondSegments[i].x1 += hOffset;
1704 secondSegments[i].x2 += hOffset;
1707 XtSetArg(args[0], XtNwidth, boardWidth);
1708 XtSetArg(args[1], XtNheight, boardHeight);
1709 XtSetValues(boardWidget, args, 2);
1711 timerWidth = (boardWidth - sep) / 2;
1712 XtSetArg(args[0], XtNwidth, timerWidth);
1713 XtSetValues(whiteTimerWidget, args, 1);
1714 XtSetValues(blackTimerWidget, args, 1);
1716 XawFormDoLayout(formWidget, False);
1718 if (appData.titleInWindow) {
1720 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1721 XtSetArg(args[i], XtNheight, &h); i++;
1722 XtGetValues(titleWidget, args, i);
1724 w = boardWidth - 2*bor;
1726 XtSetArg(args[0], XtNwidth, &w);
1727 XtGetValues(menuBarWidget, args, 1);
1728 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1731 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1732 if (gres != XtGeometryYes && appData.debugMode) {
1734 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1735 programName, gres, w, h, wr, hr);
1739 XawFormDoLayout(formWidget, True);
1742 * Inhibit shell resizing.
1744 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1745 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1746 shellArgs[4].value = shellArgs[2].value = w;
1747 shellArgs[5].value = shellArgs[3].value = h;
1748 XtSetValues(shellWidget, &shellArgs[0], 6);
1751 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1754 if(gameInfo.variant != oldVariant) { // and only if variant changed
1757 for(i=0; i<4; i++) {
1759 for(p=0; p<=(int)WhiteKing; p++)
1760 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1761 if(gameInfo.variant == VariantShogi) {
1762 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1763 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1764 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1765 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1766 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1769 if(gameInfo.variant == VariantGothic) {
1770 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1773 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1774 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1775 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1778 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1779 for(p=0; p<=(int)WhiteKing; p++)
1780 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1781 if(gameInfo.variant == VariantShogi) {
1782 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1783 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1784 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1785 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1786 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1789 if(gameInfo.variant == VariantGothic) {
1790 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1793 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1794 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1795 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1800 for(i=0; i<2; i++) {
1802 for(p=0; p<=(int)WhiteKing; p++)
1803 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1804 if(gameInfo.variant == VariantShogi) {
1805 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1806 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1807 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1808 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1809 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1812 if(gameInfo.variant == VariantGothic) {
1813 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1816 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1817 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1818 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1822 oldMono = -10; // kludge to force recreation of animation masks
1825 if(appData.monoMode != oldMono)
1828 oldMono = appData.monoMode;
1833 ParseIcsTextColors ()
1834 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1835 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1836 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1837 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1838 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1839 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1840 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1841 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1842 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1843 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1844 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1846 if (appData.colorize) {
1848 _("%s: can't parse color names; disabling colorization\n"),
1851 appData.colorize = FALSE;
1857 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1858 XrmValue vFrom, vTo;
1859 int forceMono = False;
1861 if (!appData.monoMode) {
1862 vFrom.addr = (caddr_t) appData.lightSquareColor;
1863 vFrom.size = strlen(appData.lightSquareColor);
1864 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1865 if (vTo.addr == NULL) {
1866 appData.monoMode = True;
1869 lightSquareColor = *(Pixel *) vTo.addr;
1872 if (!appData.monoMode) {
1873 vFrom.addr = (caddr_t) appData.darkSquareColor;
1874 vFrom.size = strlen(appData.darkSquareColor);
1875 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1876 if (vTo.addr == NULL) {
1877 appData.monoMode = True;
1880 darkSquareColor = *(Pixel *) vTo.addr;
1883 if (!appData.monoMode) {
1884 vFrom.addr = (caddr_t) appData.whitePieceColor;
1885 vFrom.size = strlen(appData.whitePieceColor);
1886 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1887 if (vTo.addr == NULL) {
1888 appData.monoMode = True;
1891 whitePieceColor = *(Pixel *) vTo.addr;
1894 if (!appData.monoMode) {
1895 vFrom.addr = (caddr_t) appData.blackPieceColor;
1896 vFrom.size = strlen(appData.blackPieceColor);
1897 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1898 if (vTo.addr == NULL) {
1899 appData.monoMode = True;
1902 blackPieceColor = *(Pixel *) vTo.addr;
1906 if (!appData.monoMode) {
1907 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1908 vFrom.size = strlen(appData.highlightSquareColor);
1909 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1910 if (vTo.addr == NULL) {
1911 appData.monoMode = True;
1914 highlightSquareColor = *(Pixel *) vTo.addr;
1918 if (!appData.monoMode) {
1919 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1920 vFrom.size = strlen(appData.premoveHighlightColor);
1921 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1922 if (vTo.addr == NULL) {
1923 appData.monoMode = True;
1926 premoveHighlightColor = *(Pixel *) vTo.addr;
1934 { // [HGM] taken out of main
1936 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1937 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1938 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1940 if (appData.bitmapDirectory[0] != NULLCHAR) {
1944 CreateXPMBoard(appData.liteBackTextureFile, 1);
1945 CreateXPMBoard(appData.darkBackTextureFile, 0);
1949 /* Create regular pieces */
1950 if (!useImages) CreatePieces();
1955 main (int argc, char **argv)
1957 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1958 XSetWindowAttributes window_attributes;
1960 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1961 XrmValue vFrom, vTo;
1962 XtGeometryResult gres;
1965 int forceMono = False;
1967 srandom(time(0)); // [HGM] book: make random truly random
1969 setbuf(stdout, NULL);
1970 setbuf(stderr, NULL);
1973 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1974 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1978 programName = strrchr(argv[0], '/');
1979 if (programName == NULL)
1980 programName = argv[0];
1985 XtSetLanguageProc(NULL, NULL, NULL);
1986 bindtextdomain(PACKAGE, LOCALEDIR);
1987 textdomain(PACKAGE);
1991 XtAppInitialize(&appContext, "XBoard", shellOptions,
1992 XtNumber(shellOptions),
1993 &argc, argv, xboardResources, NULL, 0);
1994 appData.boardSize = "";
1995 InitAppData(ConvertToLine(argc, argv));
1997 if (p == NULL) p = "/tmp";
1998 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1999 gameCopyFilename = (char*) malloc(i);
2000 gamePasteFilename = (char*) malloc(i);
2001 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2002 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2004 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2005 clientResources, XtNumber(clientResources),
2008 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2009 static char buf[MSG_SIZ];
2010 EscapeExpand(buf, appData.firstInitString);
2011 appData.firstInitString = strdup(buf);
2012 EscapeExpand(buf, appData.secondInitString);
2013 appData.secondInitString = strdup(buf);
2014 EscapeExpand(buf, appData.firstComputerString);
2015 appData.firstComputerString = strdup(buf);
2016 EscapeExpand(buf, appData.secondComputerString);
2017 appData.secondComputerString = strdup(buf);
2020 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2023 if (chdir(chessDir) != 0) {
2024 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2030 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2031 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2032 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2033 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2036 setbuf(debugFP, NULL);
2040 if (appData.debugMode) {
2041 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2045 /* [HGM,HR] make sure board size is acceptable */
2046 if(appData.NrFiles > BOARD_FILES ||
2047 appData.NrRanks > BOARD_RANKS )
2048 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2051 /* This feature does not work; animation needs a rewrite */
2052 appData.highlightDragging = FALSE;
2056 xDisplay = XtDisplay(shellWidget);
2057 xScreen = DefaultScreen(xDisplay);
2058 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2060 gameInfo.variant = StringToVariant(appData.variant);
2061 InitPosition(FALSE);
2064 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2066 if (isdigit(appData.boardSize[0])) {
2067 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2068 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2069 &fontPxlSize, &smallLayout, &tinyLayout);
2071 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2072 programName, appData.boardSize);
2076 /* Find some defaults; use the nearest known size */
2077 SizeDefaults *szd, *nearest;
2078 int distance = 99999;
2079 nearest = szd = sizeDefaults;
2080 while (szd->name != NULL) {
2081 if (abs(szd->squareSize - squareSize) < distance) {
2083 distance = abs(szd->squareSize - squareSize);
2084 if (distance == 0) break;
2088 if (i < 2) lineGap = nearest->lineGap;
2089 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2090 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2091 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2092 if (i < 6) smallLayout = nearest->smallLayout;
2093 if (i < 7) tinyLayout = nearest->tinyLayout;
2096 SizeDefaults *szd = sizeDefaults;
2097 if (*appData.boardSize == NULLCHAR) {
2098 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2099 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2102 if (szd->name == NULL) szd--;
2103 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2105 while (szd->name != NULL &&
2106 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2107 if (szd->name == NULL) {
2108 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2109 programName, appData.boardSize);
2113 squareSize = szd->squareSize;
2114 lineGap = szd->lineGap;
2115 clockFontPxlSize = szd->clockFontPxlSize;
2116 coordFontPxlSize = szd->coordFontPxlSize;
2117 fontPxlSize = szd->fontPxlSize;
2118 smallLayout = szd->smallLayout;
2119 tinyLayout = szd->tinyLayout;
2120 // [HGM] font: use defaults from settings file if available and not overruled
2122 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2123 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2124 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2125 appData.font = fontTable[MESSAGE_FONT][squareSize];
2126 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2127 appData.coordFont = fontTable[COORD_FONT][squareSize];
2129 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2130 if (strlen(appData.pixmapDirectory) > 0) {
2131 p = ExpandPathName(appData.pixmapDirectory);
2133 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2134 appData.pixmapDirectory);
2137 if (appData.debugMode) {
2138 fprintf(stderr, _("\
2139 XBoard square size (hint): %d\n\
2140 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2142 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2143 if (appData.debugMode) {
2144 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2147 defaultLineGap = lineGap;
2148 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2150 /* [HR] height treated separately (hacked) */
2151 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2152 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2153 if (appData.showJail == 1) {
2154 /* Jail on top and bottom */
2155 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2156 XtSetArg(boardArgs[2], XtNheight,
2157 boardHeight + 2*(lineGap + squareSize));
2158 } else if (appData.showJail == 2) {
2160 XtSetArg(boardArgs[1], XtNwidth,
2161 boardWidth + 2*(lineGap + squareSize));
2162 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2165 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2166 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2170 * Determine what fonts to use.
2173 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2174 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2175 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2176 fontSet = CreateFontSet(appData.font);
2177 clockFontSet = CreateFontSet(appData.clockFont);
2179 /* For the coordFont, use the 0th font of the fontset. */
2180 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2181 XFontStruct **font_struct_list;
2182 XFontSetExtents *fontSize;
2183 char **font_name_list;
2184 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2185 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2186 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2187 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
2188 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
2191 appData.font = FindFont(appData.font, fontPxlSize);
2192 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2193 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2194 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2195 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2196 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2197 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2199 countFontID = coordFontID; // [HGM] holdings
2200 countFontStruct = coordFontStruct;
2202 xdb = XtDatabase(xDisplay);
2204 XrmPutLineResource(&xdb, "*international: True");
2205 vTo.size = sizeof(XFontSet);
2206 vTo.addr = (XtPointer) &fontSet;
2207 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2209 XrmPutStringResource(&xdb, "*font", appData.font);
2213 * Detect if there are not enough colors available and adapt.
2215 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2216 appData.monoMode = True;
2219 forceMono = MakeColors();
2222 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2224 appData.monoMode = True;
2227 if (appData.lowTimeWarning && !appData.monoMode) {
2228 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2229 vFrom.size = strlen(appData.lowTimeWarningColor);
2230 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2231 if (vTo.addr == NULL)
2232 appData.monoMode = True;
2234 lowTimeWarningColor = *(Pixel *) vTo.addr;
2237 if (appData.monoMode && appData.debugMode) {
2238 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2239 (unsigned long) XWhitePixel(xDisplay, xScreen),
2240 (unsigned long) XBlackPixel(xDisplay, xScreen));
2243 ParseIcsTextColors();
2244 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2245 textColors[ColorNone].attr = 0;
2247 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2253 layoutName = "tinyLayout";
2254 } else if (smallLayout) {
2255 layoutName = "smallLayout";
2257 layoutName = "normalLayout";
2259 /* Outer layoutWidget is there only to provide a name for use in
2260 resources that depend on the layout style */
2262 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2263 layoutArgs, XtNumber(layoutArgs));
2265 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2266 formArgs, XtNumber(formArgs));
2267 XtSetArg(args[0], XtNdefaultDistance, &sep);
2268 XtGetValues(formWidget, args, 1);
2271 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2272 XtSetArg(args[0], XtNtop, XtChainTop);
2273 XtSetArg(args[1], XtNbottom, XtChainTop);
2274 XtSetArg(args[2], XtNright, XtChainLeft);
2275 XtSetValues(menuBarWidget, args, 3);
2277 widgetList[j++] = whiteTimerWidget =
2278 XtCreateWidget("whiteTime", labelWidgetClass,
2279 formWidget, timerArgs, XtNumber(timerArgs));
2281 XtSetArg(args[0], XtNfontSet, clockFontSet);
2283 XtSetArg(args[0], XtNfont, clockFontStruct);
2285 XtSetArg(args[1], XtNtop, XtChainTop);
2286 XtSetArg(args[2], XtNbottom, XtChainTop);
2287 XtSetValues(whiteTimerWidget, args, 3);
2289 widgetList[j++] = blackTimerWidget =
2290 XtCreateWidget("blackTime", labelWidgetClass,
2291 formWidget, timerArgs, XtNumber(timerArgs));
2293 XtSetArg(args[0], XtNfontSet, clockFontSet);
2295 XtSetArg(args[0], XtNfont, clockFontStruct);
2297 XtSetArg(args[1], XtNtop, XtChainTop);
2298 XtSetArg(args[2], XtNbottom, XtChainTop);
2299 XtSetValues(blackTimerWidget, args, 3);
2301 if (appData.titleInWindow) {
2302 widgetList[j++] = titleWidget =
2303 XtCreateWidget("title", labelWidgetClass, formWidget,
2304 titleArgs, XtNumber(titleArgs));
2305 XtSetArg(args[0], XtNtop, XtChainTop);
2306 XtSetArg(args[1], XtNbottom, XtChainTop);
2307 XtSetValues(titleWidget, args, 2);
2310 if (appData.showButtonBar) {
2311 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2312 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2313 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2314 XtSetArg(args[2], XtNtop, XtChainTop);
2315 XtSetArg(args[3], XtNbottom, XtChainTop);
2316 XtSetValues(buttonBarWidget, args, 4);
2319 widgetList[j++] = messageWidget =
2320 XtCreateWidget("message", labelWidgetClass, formWidget,
2321 messageArgs, XtNumber(messageArgs));
2322 XtSetArg(args[0], XtNtop, XtChainTop);
2323 XtSetArg(args[1], XtNbottom, XtChainTop);
2324 XtSetValues(messageWidget, args, 2);
2326 widgetList[j++] = boardWidget =
2327 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2328 XtNumber(boardArgs));
2330 XtManageChildren(widgetList, j);
2332 timerWidth = (boardWidth - sep) / 2;
2333 XtSetArg(args[0], XtNwidth, timerWidth);
2334 XtSetValues(whiteTimerWidget, args, 1);
2335 XtSetValues(blackTimerWidget, args, 1);
2337 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2338 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2339 XtGetValues(whiteTimerWidget, args, 2);
2341 if (appData.showButtonBar) {
2342 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2343 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2344 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2348 * formWidget uses these constraints but they are stored
2352 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2353 XtSetValues(menuBarWidget, args, i);
2354 if (appData.titleInWindow) {
2357 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2358 XtSetValues(whiteTimerWidget, args, i);
2360 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2361 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2362 XtSetValues(blackTimerWidget, args, i);
2364 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2365 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2366 XtSetValues(titleWidget, args, i);
2368 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2369 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2370 XtSetValues(messageWidget, args, i);
2371 if (appData.showButtonBar) {
2373 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2374 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2375 XtSetValues(buttonBarWidget, args, i);
2379 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2380 XtSetValues(whiteTimerWidget, args, i);
2382 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2383 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2384 XtSetValues(blackTimerWidget, args, i);
2386 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2387 XtSetValues(titleWidget, args, i);
2389 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2390 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2391 XtSetValues(messageWidget, args, i);
2392 if (appData.showButtonBar) {
2394 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2395 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2396 XtSetValues(buttonBarWidget, args, i);
2401 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2402 XtSetValues(whiteTimerWidget, args, i);
2404 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2405 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2406 XtSetValues(blackTimerWidget, args, i);
2408 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2409 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2410 XtSetValues(messageWidget, args, i);
2411 if (appData.showButtonBar) {
2413 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2414 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2415 XtSetValues(buttonBarWidget, args, i);
2419 XtSetArg(args[0], XtNfromVert, messageWidget);
2420 XtSetArg(args[1], XtNtop, XtChainTop);
2421 XtSetArg(args[2], XtNbottom, XtChainBottom);
2422 XtSetArg(args[3], XtNleft, XtChainLeft);
2423 XtSetArg(args[4], XtNright, XtChainRight);
2424 XtSetValues(boardWidget, args, 5);
2426 XtRealizeWidget(shellWidget);
2429 XtSetArg(args[0], XtNx, wpMain.x);
2430 XtSetArg(args[1], XtNy, wpMain.y);
2431 XtSetValues(shellWidget, args, 2);
2435 * Correct the width of the message and title widgets.
2436 * It is not known why some systems need the extra fudge term.
2437 * The value "2" is probably larger than needed.
2439 XawFormDoLayout(formWidget, False);
2441 #define WIDTH_FUDGE 2
2443 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2444 XtSetArg(args[i], XtNheight, &h); i++;
2445 XtGetValues(messageWidget, args, i);
2446 if (appData.showButtonBar) {
2448 XtSetArg(args[i], XtNwidth, &w); i++;
2449 XtGetValues(buttonBarWidget, args, i);
2450 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2452 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2455 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2456 if (gres != XtGeometryYes && appData.debugMode) {
2457 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2458 programName, gres, w, h, wr, hr);
2461 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2462 /* The size used for the child widget in layout lags one resize behind
2463 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2465 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2466 if (gres != XtGeometryYes && appData.debugMode) {
2467 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2468 programName, gres, w, h, wr, hr);
2471 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2472 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2473 XtSetArg(args[1], XtNright, XtChainRight);
2474 XtSetValues(messageWidget, args, 2);
2476 if (appData.titleInWindow) {
2478 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2479 XtSetArg(args[i], XtNheight, &h); i++;
2480 XtGetValues(titleWidget, args, i);
2482 w = boardWidth - 2*bor;
2484 XtSetArg(args[0], XtNwidth, &w);
2485 XtGetValues(menuBarWidget, args, 1);
2486 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2489 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2490 if (gres != XtGeometryYes && appData.debugMode) {
2492 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2493 programName, gres, w, h, wr, hr);
2496 XawFormDoLayout(formWidget, True);
2498 xBoardWindow = XtWindow(boardWidget);
2500 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2501 // not need to go into InitDrawingSizes().
2505 * Create X checkmark bitmap and initialize option menu checks.
2507 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2508 checkmark_bits, checkmark_width, checkmark_height);
2509 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2510 #ifndef OPTIONSDIALOG
2511 if (appData.alwaysPromoteToQueen) {
2512 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2515 if (appData.animateDragging) {
2516 XtSetValues(XtNameToWidget(menuBarWidget,
2517 "menuOptions.Animate Dragging"),
2520 if (appData.animate) {
2521 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2524 if (appData.autoCallFlag) {
2525 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2528 if (appData.autoFlipView) {
2529 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2532 if (appData.blindfold) {
2533 XtSetValues(XtNameToWidget(menuBarWidget,
2534 "menuOptions.Blindfold"), args, 1);
2536 if (appData.flashCount > 0) {
2537 XtSetValues(XtNameToWidget(menuBarWidget,
2538 "menuOptions.Flash Moves"),
2542 if (appData.highlightDragging) {
2543 XtSetValues(XtNameToWidget(menuBarWidget,
2544 "menuOptions.Highlight Dragging"),
2548 if (appData.highlightLastMove) {
2549 XtSetValues(XtNameToWidget(menuBarWidget,
2550 "menuOptions.Highlight Last Move"),
2553 if (appData.highlightMoveWithArrow) {
2554 XtSetValues(XtNameToWidget(menuBarWidget,
2555 "menuOptions.Arrow"),
2558 // if (appData.icsAlarm) {
2559 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2562 if (appData.ringBellAfterMoves) {
2563 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2566 if (appData.oneClick) {
2567 XtSetValues(XtNameToWidget(menuBarWidget,
2568 "menuOptions.OneClick"), args, 1);
2570 if (appData.periodicUpdates) {
2571 XtSetValues(XtNameToWidget(menuBarWidget,
2572 "menuOptions.Periodic Updates"), args, 1);
2574 if (appData.ponderNextMove) {
2575 XtSetValues(XtNameToWidget(menuBarWidget,
2576 "menuOptions.Ponder Next Move"), args, 1);
2578 if (appData.popupExitMessage) {
2579 XtSetValues(XtNameToWidget(menuBarWidget,
2580 "menuOptions.Popup Exit Message"), args, 1);
2582 if (appData.popupMoveErrors) {
2583 XtSetValues(XtNameToWidget(menuBarWidget,
2584 "menuOptions.Popup Move Errors"), args, 1);
2586 // if (appData.premove) {
2587 // XtSetValues(XtNameToWidget(menuBarWidget,
2588 // "menuOptions.Premove"), args, 1);
2590 if (appData.showCoords) {
2591 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2594 if (appData.hideThinkingFromHuman) {
2595 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2598 if (appData.testLegality) {
2599 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2603 if (saveSettingsOnExit) {
2604 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2611 ReadBitmap(&wIconPixmap, "icon_white.bm",
2612 icon_white_bits, icon_white_width, icon_white_height);
2613 ReadBitmap(&bIconPixmap, "icon_black.bm",
2614 icon_black_bits, icon_black_width, icon_black_height);
2615 iconPixmap = wIconPixmap;
2617 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2618 XtSetValues(shellWidget, args, i);
2621 * Create a cursor for the board widget.
2623 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2624 XChangeWindowAttributes(xDisplay, xBoardWindow,
2625 CWCursor, &window_attributes);
2628 * Inhibit shell resizing.
2630 shellArgs[0].value = (XtArgVal) &w;
2631 shellArgs[1].value = (XtArgVal) &h;
2632 XtGetValues(shellWidget, shellArgs, 2);
2633 shellArgs[4].value = shellArgs[2].value = w;
2634 shellArgs[5].value = shellArgs[3].value = h;
2635 XtSetValues(shellWidget, &shellArgs[2], 4);
2636 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2637 marginH = h - boardHeight;
2639 CatchDeleteWindow(shellWidget, "QuitProc");
2647 if (appData.animate || appData.animateDragging)
2650 XtAugmentTranslations(formWidget,
2651 XtParseTranslationTable(globalTranslations));
2652 XtAugmentTranslations(boardWidget,
2653 XtParseTranslationTable(boardTranslations));
2654 XtAugmentTranslations(whiteTimerWidget,
2655 XtParseTranslationTable(whiteTranslations));
2656 XtAugmentTranslations(blackTimerWidget,
2657 XtParseTranslationTable(blackTranslations));
2659 /* Why is the following needed on some versions of X instead
2660 * of a translation? */
2661 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2662 (XtEventHandler) EventProc, NULL);
2664 XtAddEventHandler(formWidget, KeyPressMask, False,
2665 (XtEventHandler) MoveTypeInProc, NULL);
2666 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2667 (XtEventHandler) EventProc, NULL);
2669 /* [AS] Restore layout */
2670 if( wpMoveHistory.visible ) {
2674 if( wpEvalGraph.visible )
2679 if( wpEngineOutput.visible ) {
2680 EngineOutputPopUp();
2685 if (errorExitStatus == -1) {
2686 if (appData.icsActive) {
2687 /* We now wait until we see "login:" from the ICS before
2688 sending the logon script (problems with timestamp otherwise) */
2689 /*ICSInitScript();*/
2690 if (appData.icsInputBox) ICSInputBoxPopUp();
2694 signal(SIGWINCH, TermSizeSigHandler);
2696 signal(SIGINT, IntSigHandler);
2697 signal(SIGTERM, IntSigHandler);
2698 if (*appData.cmailGameName != NULLCHAR) {
2699 signal(SIGUSR1, CmailSigHandler);
2702 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2704 // XtSetKeyboardFocus(shellWidget, formWidget);
2705 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2707 XtAppMainLoop(appContext);
2708 if (appData.debugMode) fclose(debugFP); // [DM] debug
2712 static Boolean noEcho;
2717 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2718 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2720 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2721 unlink(gameCopyFilename);
2722 unlink(gamePasteFilename);
2723 if(noEcho) EchoOn();
2727 TermSizeSigHandler (int sig)
2733 IntSigHandler (int sig)
2739 CmailSigHandler (int sig)
2744 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2746 /* Activate call-back function CmailSigHandlerCallBack() */
2747 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2749 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2753 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2756 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2758 /**** end signal code ****/
2764 /* try to open the icsLogon script, either in the location given
2765 * or in the users HOME directory
2772 f = fopen(appData.icsLogon, "r");
2775 homedir = getenv("HOME");
2776 if (homedir != NULL)
2778 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2779 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2780 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2781 f = fopen(buf, "r");
2786 ProcessICSInitScript(f);
2788 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2807 GreyRevert (Boolean grey)
2810 if (!menuBarWidget) return;
2811 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2813 DisplayError("menuEdit.Revert", 0);
2815 XtSetSensitive(w, !grey);
2817 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2819 DisplayError("menuEdit.Annotate", 0);
2821 XtSetSensitive(w, !grey);
2826 SetMenuEnables (Enables *enab)
2829 if (!menuBarWidget) return;
2830 while (enab->name != NULL) {
2831 w = XtNameToWidget(menuBarWidget, enab->name);
2833 DisplayError(enab->name, 0);
2835 XtSetSensitive(w, enab->value);
2841 Enables icsEnables[] = {
2842 { "menuFile.Mail Move", False },
2843 { "menuFile.Reload CMail Message", False },
2844 { "menuMode.Machine Black", False },
2845 { "menuMode.Machine White", False },
2846 { "menuMode.Analysis Mode", False },
2847 { "menuMode.Analyze File", False },
2848 { "menuMode.Two Machines", False },
2849 { "menuMode.Machine Match", False },
2851 { "menuEngine.Hint", False },
2852 { "menuEngine.Book", False },
2853 { "menuEngine.Move Now", False },
2854 #ifndef OPTIONSDIALOG
2855 { "menuOptions.Periodic Updates", False },
2856 { "menuOptions.Hide Thinking", False },
2857 { "menuOptions.Ponder Next Move", False },
2860 { "menuEngine.Engine #1 Settings", False },
2861 { "menuEngine.Engine #2 Settings", False },
2862 { "menuEngine.Load Engine", False },
2863 { "menuEdit.Annotate", False },
2864 { "menuOptions.Match", False },
2868 Enables ncpEnables[] = {
2869 { "menuFile.Mail Move", False },
2870 { "menuFile.Reload CMail Message", False },
2871 { "menuMode.Machine White", False },
2872 { "menuMode.Machine Black", False },
2873 { "menuMode.Analysis Mode", False },
2874 { "menuMode.Analyze File", False },
2875 { "menuMode.Two Machines", False },
2876 { "menuMode.Machine Match", False },
2877 { "menuMode.ICS Client", False },
2878 { "menuView.ICStex", False },
2879 { "menuView.ICS Input Box", False },
2880 { "Action", False },
2881 { "menuEdit.Revert", False },
2882 { "menuEdit.Annotate", False },
2883 { "menuEngine.Engine #1 Settings", False },
2884 { "menuEngine.Engine #2 Settings", False },
2885 { "menuEngine.Move Now", False },
2886 { "menuEngine.Retract Move", False },
2887 { "menuOptions.ICS", False },
2888 #ifndef OPTIONSDIALOG
2889 { "menuOptions.Auto Flag", False },
2890 { "menuOptions.Auto Flip View", False },
2891 // { "menuOptions.ICS Alarm", False },
2892 { "menuOptions.Move Sound", False },
2893 { "menuOptions.Hide Thinking", False },
2894 { "menuOptions.Periodic Updates", False },
2895 { "menuOptions.Ponder Next Move", False },
2897 { "menuEngine.Hint", False },
2898 { "menuEngine.Book", False },
2902 Enables gnuEnables[] = {
2903 { "menuMode.ICS Client", False },
2904 { "menuView.ICStex", False },
2905 { "menuView.ICS Input Box", False },
2906 { "menuAction.Accept", False },
2907 { "menuAction.Decline", False },
2908 { "menuAction.Rematch", False },
2909 { "menuAction.Adjourn", False },
2910 { "menuAction.Stop Examining", False },
2911 { "menuAction.Stop Observing", False },
2912 { "menuAction.Upload to Examine", False },
2913 { "menuEdit.Revert", False },
2914 { "menuEdit.Annotate", False },
2915 { "menuOptions.ICS", False },
2917 /* The next two options rely on SetCmailMode being called *after* */
2918 /* SetGNUMode so that when GNU is being used to give hints these */
2919 /* menu options are still available */
2921 { "menuFile.Mail Move", False },
2922 { "menuFile.Reload CMail Message", False },
2923 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2924 { "menuMode.Machine White", True },
2925 { "menuMode.Machine Black", True },
2926 { "menuMode.Analysis Mode", True },
2927 { "menuMode.Analyze File", True },
2928 { "menuMode.Two Machines", True },
2929 { "menuMode.Machine Match", True },
2930 { "menuEngine.Engine #1 Settings", True },
2931 { "menuEngine.Engine #2 Settings", True },
2932 { "menuEngine.Hint", True },
2933 { "menuEngine.Book", True },
2934 { "menuEngine.Move Now", True },
2935 { "menuEngine.Retract Move", True },
2940 Enables cmailEnables[] = {
2942 { "menuAction.Call Flag", False },
2943 { "menuAction.Draw", True },
2944 { "menuAction.Adjourn", False },
2945 { "menuAction.Abort", False },
2946 { "menuAction.Stop Observing", False },
2947 { "menuAction.Stop Examining", False },
2948 { "menuFile.Mail Move", True },
2949 { "menuFile.Reload CMail Message", True },
2953 Enables trainingOnEnables[] = {
2954 { "menuMode.Edit Comment", False },
2955 { "menuMode.Pause", False },
2956 { "menuEdit.Forward", False },
2957 { "menuEdit.Backward", False },
2958 { "menuEdit.Forward to End", False },
2959 { "menuEdit.Back to Start", False },
2960 { "menuEngine.Move Now", False },
2961 { "menuEdit.Truncate Game", False },
2965 Enables trainingOffEnables[] = {
2966 { "menuMode.Edit Comment", True },
2967 { "menuMode.Pause", True },
2968 { "menuEdit.Forward", True },
2969 { "menuEdit.Backward", True },
2970 { "menuEdit.Forward to End", True },
2971 { "menuEdit.Back to Start", True },
2972 { "menuEngine.Move Now", True },
2973 { "menuEdit.Truncate Game", True },
2977 Enables machineThinkingEnables[] = {
2978 { "menuFile.Load Game", False },
2979 // { "menuFile.Load Next Game", False },
2980 // { "menuFile.Load Previous Game", False },
2981 // { "menuFile.Reload Same Game", False },
2982 { "menuEdit.Paste Game", False },
2983 { "menuFile.Load Position", False },
2984 // { "menuFile.Load Next Position", False },
2985 // { "menuFile.Load Previous Position", False },
2986 // { "menuFile.Reload Same Position", False },
2987 { "menuEdit.Paste Position", False },
2988 { "menuMode.Machine White", False },
2989 { "menuMode.Machine Black", False },
2990 { "menuMode.Two Machines", False },
2991 // { "menuMode.Machine Match", False },
2992 { "menuEngine.Retract Move", False },
2996 Enables userThinkingEnables[] = {
2997 { "menuFile.Load Game", True },
2998 // { "menuFile.Load Next Game", True },
2999 // { "menuFile.Load Previous Game", True },
3000 // { "menuFile.Reload Same Game", True },
3001 { "menuEdit.Paste Game", True },
3002 { "menuFile.Load Position", True },
3003 // { "menuFile.Load Next Position", True },
3004 // { "menuFile.Load Previous Position", True },
3005 // { "menuFile.Reload Same Position", True },
3006 { "menuEdit.Paste Position", True },
3007 { "menuMode.Machine White", True },
3008 { "menuMode.Machine Black", True },
3009 { "menuMode.Two Machines", True },
3010 // { "menuMode.Machine Match", True },
3011 { "menuEngine.Retract Move", True },
3018 SetMenuEnables(icsEnables);
3021 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3022 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3023 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3031 SetMenuEnables(ncpEnables);
3037 SetMenuEnables(gnuEnables);
3043 SetMenuEnables(cmailEnables);
3047 SetTrainingModeOn ()
3049 SetMenuEnables(trainingOnEnables);
3050 if (appData.showButtonBar) {
3051 XtSetSensitive(buttonBarWidget, False);
3057 SetTrainingModeOff ()
3059 SetMenuEnables(trainingOffEnables);
3060 if (appData.showButtonBar) {
3061 XtSetSensitive(buttonBarWidget, True);
3066 SetUserThinkingEnables ()
3068 if (appData.noChessProgram) return;
3069 SetMenuEnables(userThinkingEnables);
3073 SetMachineThinkingEnables ()
3075 if (appData.noChessProgram) return;
3076 SetMenuEnables(machineThinkingEnables);
3078 case MachinePlaysBlack:
3079 case MachinePlaysWhite:
3080 case TwoMachinesPlay:
3081 XtSetSensitive(XtNameToWidget(menuBarWidget,
3082 ModeToWidgetName(gameMode)), True);
3089 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3090 #define HISTORY_SIZE 64
3091 static char *history[HISTORY_SIZE];
3092 int histIn = 0, histP = 0;
3095 SaveInHistory (char *cmd)
3097 if (history[histIn] != NULL) {
3098 free(history[histIn]);
3099 history[histIn] = NULL;
3101 if (*cmd == NULLCHAR) return;
3102 history[histIn] = StrSave(cmd);
3103 histIn = (histIn + 1) % HISTORY_SIZE;
3104 if (history[histIn] != NULL) {
3105 free(history[histIn]);
3106 history[histIn] = NULL;
3112 PrevInHistory (char *cmd)
3115 if (histP == histIn) {
3116 if (history[histIn] != NULL) free(history[histIn]);
3117 history[histIn] = StrSave(cmd);
3119 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3120 if (newhp == histIn || history[newhp] == NULL) return NULL;
3122 return history[histP];
3128 if (histP == histIn) return NULL;
3129 histP = (histP + 1) % HISTORY_SIZE;
3130 return history[histP];
3132 // end of borrowed code
3134 #define Abs(n) ((n)<0 ? -(n) : (n))
3138 InsertPxlSize (char *pattern, int targetPxlSize)
3140 char *base_fnt_lst, strInt[12], *p, *q;
3141 int alternatives, i, len, strIntLen;
3144 * Replace the "*" (if present) in the pixel-size slot of each
3145 * alternative with the targetPxlSize.
3149 while ((p = strchr(p, ',')) != NULL) {
3153 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3154 strIntLen = strlen(strInt);
3155 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3159 while (alternatives--) {
3160 char *comma = strchr(p, ',');
3161 for (i=0; i<14; i++) {
3162 char *hyphen = strchr(p, '-');
3164 if (comma && hyphen > comma) break;
3165 len = hyphen + 1 - p;
3166 if (i == 7 && *p == '*' && len == 2) {
3168 memcpy(q, strInt, strIntLen);
3178 len = comma + 1 - p;
3185 return base_fnt_lst;
3189 CreateFontSet (char *base_fnt_lst)
3192 char **missing_list;
3196 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3197 &missing_list, &missing_count, &def_string);
3198 if (appData.debugMode) {
3200 XFontStruct **font_struct_list;
3201 char **font_name_list;
3202 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3204 fprintf(debugFP, " got list %s, locale %s\n",
3205 XBaseFontNameListOfFontSet(fntSet),
3206 XLocaleOfFontSet(fntSet));
3207 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3208 for (i = 0; i < count; i++) {
3209 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3212 for (i = 0; i < missing_count; i++) {
3213 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3216 if (fntSet == NULL) {
3217 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3222 #else // not ENABLE_NLS
3224 * Find a font that matches "pattern" that is as close as
3225 * possible to the targetPxlSize. Prefer fonts that are k
3226 * pixels smaller to fonts that are k pixels larger. The
3227 * pattern must be in the X Consortium standard format,
3228 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3229 * The return value should be freed with XtFree when no
3233 FindFont (char *pattern, int targetPxlSize)
3235 char **fonts, *p, *best, *scalable, *scalableTail;
3236 int i, j, nfonts, minerr, err, pxlSize;
3238 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3240 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3241 programName, pattern);
3248 for (i=0; i<nfonts; i++) {
3251 if (*p != '-') continue;
3253 if (*p == NULLCHAR) break;
3254 if (*p++ == '-') j++;
3256 if (j < 7) continue;
3259 scalable = fonts[i];
3262 err = pxlSize - targetPxlSize;
3263 if (Abs(err) < Abs(minerr) ||
3264 (minerr > 0 && err < 0 && -err == minerr)) {
3270 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3271 /* If the error is too big and there is a scalable font,
3272 use the scalable font. */
3273 int headlen = scalableTail - scalable;
3274 p = (char *) XtMalloc(strlen(scalable) + 10);
3275 while (isdigit(*scalableTail)) scalableTail++;
3276 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3278 p = (char *) XtMalloc(strlen(best) + 2);
3279 safeStrCpy(p, best, strlen(best)+1 );
3281 if (appData.debugMode) {
3282 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3283 pattern, targetPxlSize, p);
3285 XFreeFontNames(fonts);
3292 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3293 // must be called before all non-first callse to CreateGCs()
3294 XtReleaseGC(shellWidget, highlineGC);
3295 XtReleaseGC(shellWidget, lightSquareGC);
3296 XtReleaseGC(shellWidget, darkSquareGC);
3297 XtReleaseGC(shellWidget, lineGC);
3298 if (appData.monoMode) {
3299 if (DefaultDepth(xDisplay, xScreen) == 1) {
3300 XtReleaseGC(shellWidget, wbPieceGC);
3302 XtReleaseGC(shellWidget, bwPieceGC);
3305 XtReleaseGC(shellWidget, prelineGC);
3306 XtReleaseGC(shellWidget, jailSquareGC);
3307 XtReleaseGC(shellWidget, wdPieceGC);
3308 XtReleaseGC(shellWidget, wlPieceGC);
3309 XtReleaseGC(shellWidget, wjPieceGC);
3310 XtReleaseGC(shellWidget, bdPieceGC);
3311 XtReleaseGC(shellWidget, blPieceGC);
3312 XtReleaseGC(shellWidget, bjPieceGC);
3317 CreateGCs (int redo)
3319 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3320 | GCBackground | GCFunction | GCPlaneMask;
3321 XGCValues gc_values;
3324 gc_values.plane_mask = AllPlanes;
3325 gc_values.line_width = lineGap;
3326 gc_values.line_style = LineSolid;
3327 gc_values.function = GXcopy;
3330 DeleteGCs(); // called a second time; clean up old GCs first
3331 } else { // [HGM] grid and font GCs created on first call only
3332 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3333 gc_values.background = XWhitePixel(xDisplay, xScreen);
3334 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3335 XSetFont(xDisplay, coordGC, coordFontID);
3337 // [HGM] make font for holdings counts (white on black)
3338 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3339 gc_values.background = XBlackPixel(xDisplay, xScreen);
3340 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3341 XSetFont(xDisplay, countGC, countFontID);
3343 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3344 gc_values.background = XBlackPixel(xDisplay, xScreen);
3345 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3347 if (appData.monoMode) {
3348 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3349 gc_values.background = XWhitePixel(xDisplay, xScreen);
3350 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3352 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3353 gc_values.background = XBlackPixel(xDisplay, xScreen);
3354 lightSquareGC = wbPieceGC
3355 = XtGetGC(shellWidget, value_mask, &gc_values);
3357 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3358 gc_values.background = XWhitePixel(xDisplay, xScreen);
3359 darkSquareGC = bwPieceGC
3360 = XtGetGC(shellWidget, value_mask, &gc_values);
3362 if (DefaultDepth(xDisplay, xScreen) == 1) {
3363 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3364 gc_values.function = GXcopyInverted;
3365 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3366 gc_values.function = GXcopy;
3367 if (XBlackPixel(xDisplay, xScreen) == 1) {
3368 bwPieceGC = darkSquareGC;
3369 wbPieceGC = copyInvertedGC;
3371 bwPieceGC = copyInvertedGC;
3372 wbPieceGC = lightSquareGC;
3376 gc_values.foreground = highlightSquareColor;
3377 gc_values.background = highlightSquareColor;
3378 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3380 gc_values.foreground = premoveHighlightColor;
3381 gc_values.background = premoveHighlightColor;
3382 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3384 gc_values.foreground = lightSquareColor;
3385 gc_values.background = darkSquareColor;
3386 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3388 gc_values.foreground = darkSquareColor;
3389 gc_values.background = lightSquareColor;
3390 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3392 gc_values.foreground = jailSquareColor;
3393 gc_values.background = jailSquareColor;
3394 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3396 gc_values.foreground = whitePieceColor;
3397 gc_values.background = darkSquareColor;
3398 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3400 gc_values.foreground = whitePieceColor;
3401 gc_values.background = lightSquareColor;
3402 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3404 gc_values.foreground = whitePieceColor;
3405 gc_values.background = jailSquareColor;
3406 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3408 gc_values.foreground = blackPieceColor;
3409 gc_values.background = darkSquareColor;
3410 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3412 gc_values.foreground = blackPieceColor;
3413 gc_values.background = lightSquareColor;
3414 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3416 gc_values.foreground = blackPieceColor;
3417 gc_values.background = jailSquareColor;
3418 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3423 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3431 fp = fopen(filename, "rb");
3433 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3440 for (y=0; y<h; ++y) {
3441 for (x=0; x<h; ++x) {
3446 XPutPixel(xim, x, y, blackPieceColor);
3448 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3451 XPutPixel(xim, x, y, darkSquareColor);
3453 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3456 XPutPixel(xim, x, y, whitePieceColor);
3458 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3461 XPutPixel(xim, x, y, lightSquareColor);
3463 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3471 /* create Pixmap of piece */
3472 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3474 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3477 /* create Pixmap of clipmask
3478 Note: We assume the white/black pieces have the same
3479 outline, so we make only 6 masks. This is okay
3480 since the XPM clipmask routines do the same. */
3482 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3484 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3487 /* now create the 1-bit version */
3488 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3491 values.foreground = 1;
3492 values.background = 0;
3494 /* Don't use XtGetGC, not read only */
3495 maskGC = XCreateGC(xDisplay, *mask,
3496 GCForeground | GCBackground, &values);
3497 XCopyPlane(xDisplay, temp, *mask, maskGC,
3498 0, 0, squareSize, squareSize, 0, 0, 1);
3499 XFreePixmap(xDisplay, temp);
3504 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3512 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3517 /* The XSynchronize calls were copied from CreatePieces.
3518 Not sure if needed, but can't hurt */
3519 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3522 /* temp needed by loadXIM() */
3523 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3524 0, 0, ss, ss, AllPlanes, XYPixmap);
3526 if (strlen(appData.pixmapDirectory) == 0) {
3530 if (appData.monoMode) {
3531 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3535 fprintf(stderr, _("\nLoading XIMs...\n"));
3537 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3538 fprintf(stderr, "%d", piece+1);
3539 for (kind=0; kind<4; kind++) {
3540 fprintf(stderr, ".");
3541 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3542 ExpandPathName(appData.pixmapDirectory),
3543 piece <= (int) WhiteKing ? "" : "w",
3544 pieceBitmapNames[piece],
3546 ximPieceBitmap[kind][piece] =
3547 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3548 0, 0, ss, ss, AllPlanes, XYPixmap);
3549 if (appData.debugMode)
3550 fprintf(stderr, _("(File:%s:) "), buf);
3551 loadXIM(ximPieceBitmap[kind][piece],
3553 &(xpmPieceBitmap2[kind][piece]),
3554 &(ximMaskPm2[piece]));
3555 if(piece <= (int)WhiteKing)
3556 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3558 fprintf(stderr," ");
3560 /* Load light and dark squares */
3561 /* If the LSQ and DSQ pieces don't exist, we will
3562 draw them with solid squares. */
3563 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3564 if (access(buf, 0) != 0) {
3568 fprintf(stderr, _("light square "));
3570 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3571 0, 0, ss, ss, AllPlanes, XYPixmap);
3572 if (appData.debugMode)
3573 fprintf(stderr, _("(File:%s:) "), buf);
3575 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3576 fprintf(stderr, _("dark square "));
3577 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3578 ExpandPathName(appData.pixmapDirectory), ss);
3579 if (appData.debugMode)
3580 fprintf(stderr, _("(File:%s:) "), buf);
3582 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3583 0, 0, ss, ss, AllPlanes, XYPixmap);
3584 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3585 xpmJailSquare = xpmLightSquare;
3587 fprintf(stderr, _("Done.\n"));
3589 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3592 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3596 CreateXPMBoard (char *s, int kind)
3600 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3601 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3602 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3608 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3609 // thisroutine has to be called t free the old piece pixmaps
3611 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3612 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3614 XFreePixmap(xDisplay, xpmLightSquare);
3615 XFreePixmap(xDisplay, xpmDarkSquare);
3624 u_int ss = squareSize;
3626 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3627 XpmColorSymbol symbols[4];
3628 static int redo = False;
3630 if(redo) FreeXPMPieces(); else redo = 1;
3632 /* The XSynchronize calls were copied from CreatePieces.
3633 Not sure if needed, but can't hurt */
3634 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3636 /* Setup translations so piece colors match square colors */
3637 symbols[0].name = "light_piece";
3638 symbols[0].value = appData.whitePieceColor;
3639 symbols[1].name = "dark_piece";
3640 symbols[1].value = appData.blackPieceColor;
3641 symbols[2].name = "light_square";
3642 symbols[2].value = appData.lightSquareColor;
3643 symbols[3].name = "dark_square";
3644 symbols[3].value = appData.darkSquareColor;
3646 attr.valuemask = XpmColorSymbols;
3647 attr.colorsymbols = symbols;
3648 attr.numsymbols = 4;
3650 if (appData.monoMode) {
3651 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3655 if (strlen(appData.pixmapDirectory) == 0) {
3656 XpmPieces* pieces = builtInXpms;
3659 while (pieces->size != squareSize && pieces->size) pieces++;
3660 if (!pieces->size) {
3661 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3664 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3665 for (kind=0; kind<4; kind++) {
3667 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3668 pieces->xpm[piece][kind],
3669 &(xpmPieceBitmap2[kind][piece]),
3670 NULL, &attr)) != 0) {
3671 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3675 if(piece <= (int) WhiteKing)
3676 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3680 xpmJailSquare = xpmLightSquare;
3684 fprintf(stderr, _("\nLoading XPMs...\n"));
3687 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3688 fprintf(stderr, "%d ", piece+1);
3689 for (kind=0; kind<4; kind++) {
3690 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3691 ExpandPathName(appData.pixmapDirectory),
3692 piece > (int) WhiteKing ? "w" : "",
3693 pieceBitmapNames[piece],
3695 if (appData.debugMode) {
3696 fprintf(stderr, _("(File:%s:) "), buf);
3698 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3699 &(xpmPieceBitmap2[kind][piece]),
3700 NULL, &attr)) != 0) {
3701 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3702 // [HGM] missing: read of unorthodox piece failed; substitute King.
3703 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3704 ExpandPathName(appData.pixmapDirectory),
3706 if (appData.debugMode) {
3707 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3709 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3710 &(xpmPieceBitmap2[kind][piece]),
3714 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3719 if(piece <= (int) WhiteKing)
3720 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3723 /* Load light and dark squares */
3724 /* If the LSQ and DSQ pieces don't exist, we will
3725 draw them with solid squares. */
3726 fprintf(stderr, _("light square "));
3727 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3728 if (access(buf, 0) != 0) {
3732 if (appData.debugMode)
3733 fprintf(stderr, _("(File:%s:) "), buf);
3735 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3736 &xpmLightSquare, NULL, &attr)) != 0) {
3737 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3740 fprintf(stderr, _("dark square "));
3741 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3742 ExpandPathName(appData.pixmapDirectory), ss);
3743 if (appData.debugMode) {
3744 fprintf(stderr, _("(File:%s:) "), buf);
3746 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3747 &xpmDarkSquare, NULL, &attr)) != 0) {
3748 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3752 xpmJailSquare = xpmLightSquare;
3753 fprintf(stderr, _("Done.\n"));
3755 oldVariant = -1; // kludge to force re-makig of animation masks
3756 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3759 #endif /* HAVE_LIBXPM */
3762 /* No built-in bitmaps */
3767 u_int ss = squareSize;
3769 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3772 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3773 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3774 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3775 pieceBitmapNames[piece],
3776 ss, kind == SOLID ? 's' : 'o');
3777 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3778 if(piece <= (int)WhiteKing)
3779 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3783 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3787 /* With built-in bitmaps */
3791 BuiltInBits* bib = builtInBits;
3794 u_int ss = squareSize;
3796 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3799 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3801 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3802 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3803 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3804 pieceBitmapNames[piece],
3805 ss, kind == SOLID ? 's' : 'o');
3806 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3807 bib->bits[kind][piece], ss, ss);
3808 if(piece <= (int)WhiteKing)
3809 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3813 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3819 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3824 char msg[MSG_SIZ], fullname[MSG_SIZ];
3826 if (*appData.bitmapDirectory != NULLCHAR) {
3827 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3828 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3829 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3830 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3831 &w, &h, pm, &x_hot, &y_hot);
3832 fprintf(stderr, "load %s\n", name);
3833 if (errcode != BitmapSuccess) {
3835 case BitmapOpenFailed:
3836 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3838 case BitmapFileInvalid:
3839 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3841 case BitmapNoMemory:
3842 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3846 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3850 fprintf(stderr, _("%s: %s...using built-in\n"),
3852 } else if (w != wreq || h != hreq) {
3854 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3855 programName, fullname, w, h, wreq, hreq);
3861 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3871 if (lineGap == 0) return;
3873 /* [HR] Split this into 2 loops for non-square boards. */
3875 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3876 gridSegments[i].x1 = 0;
3877 gridSegments[i].x2 =
3878 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3879 gridSegments[i].y1 = gridSegments[i].y2
3880 = lineGap / 2 + (i * (squareSize + lineGap));
3883 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3884 gridSegments[j + i].y1 = 0;
3885 gridSegments[j + i].y2 =
3886 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3887 gridSegments[j + i].x1 = gridSegments[j + i].x2
3888 = lineGap / 2 + (j * (squareSize + lineGap));
3893 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3895 XtActionProc proc = (XtActionProc) addr;
3897 (proc)(NULL, NULL, NULL, NULL);
3901 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3903 RecentEngineEvent((int) addr);
3907 AppendEnginesToMenu (Widget menu, char *list)
3915 if(appData.recentEngines <= 0) return;
3916 recentEngines = strdup(list);
3918 XtSetArg(args[j], XtNleftMargin, 20); j++;
3919 XtSetArg(args[j], XtNrightMargin, 20); j++;
3921 p = strchr(list, '\n'); if(p == NULL) break;
3922 if(i == 0) XtCreateManagedWidget(_("----"), smeLineObjectClass, menu, args, j); // at least one valid item to add
3924 XtSetArg(args[j], XtNlabel, XtNewString(list));
3925 entry = XtCreateManagedWidget("engine", smeBSBObjectClass, menu, args, j+1);
3926 XtAddCallback(entry, XtNcallback,
3927 (XtCallbackProc) MenuEngineSelect,
3929 i++; *p = '\n'; list = p + 1;
3934 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3941 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3944 XtSetArg(args[j], XtNleftMargin, 20); j++;
3945 XtSetArg(args[j], XtNrightMargin, 20); j++;
3947 while (mi->string != NULL) {
3948 if (strcmp(mi->string, "----") == 0) {
3949 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3952 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3953 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3955 XtAddCallback(entry, XtNcallback,
3956 (XtCallbackProc) MenuBarSelect,
3957 (caddr_t) mi->proc);
3961 if(!strcmp(mb->name, "Engine")) AppendEnginesToMenu(menu, appData.recentEngineList);
3965 CreateMenuBar (Menu *mb, int boardWidth)
3967 int i, j, nr = 0, wtot = 0, widths[10];
3970 char menuName[MSG_SIZ];
3975 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3976 XtSetArg(args[j], XtNvSpace, 0); j++;
3977 XtSetArg(args[j], XtNborderWidth, 0); j++;
3978 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3979 formWidget, args, j);
3981 while (mb->name != NULL) {
3982 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3983 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3985 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3986 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3987 XtSetArg(args[j], XtNborderWidth, 0); j++;
3988 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3990 CreateMenuBarPopup(menuBar, menuName, mb);
3992 XtSetArg(args[j], XtNwidth, &w); j++;
3993 XtGetValues(mb->subMenu, args, j);
3994 wtot += mb->textWidth = widths[nr++] = w;
3997 while(wtot > boardWidth - 40) {
3999 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
4003 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
4005 XtSetArg(args[j], XtNwidth, widths[i]); j++;
4006 XtSetValues(ma[i].subMenu, args, j);
4012 CreateButtonBar (MenuItem *mi)
4015 Widget button, buttonBar;
4019 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4021 XtSetArg(args[j], XtNhSpace, 0); j++;
4023 XtSetArg(args[j], XtNborderWidth, 0); j++;
4024 XtSetArg(args[j], XtNvSpace, 0); j++;
4025 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4026 formWidget, args, j);
4028 while (mi->string != NULL) {
4031 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4032 XtSetArg(args[j], XtNborderWidth, 0); j++;
4034 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4035 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4036 buttonBar, args, j);
4037 XtAddCallback(button, XtNcallback,
4038 (XtCallbackProc) MenuBarSelect,
4039 (caddr_t) mi->proc);
4046 CreatePieceMenu (char *name, int color)
4051 ChessSquare selection;
4053 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4054 boardWidget, args, 0);
4056 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4057 String item = pieceMenuStrings[color][i];
4059 if (strcmp(item, "----") == 0) {
4060 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4063 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4064 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4066 selection = pieceMenuTranslation[color][i];
4067 XtAddCallback(entry, XtNcallback,
4068 (XtCallbackProc) PieceMenuSelect,
4069 (caddr_t) selection);
4070 if (selection == WhitePawn || selection == BlackPawn) {
4071 XtSetArg(args[0], XtNpopupOnEntry, entry);
4072 XtSetValues(menu, args, 1);
4085 ChessSquare selection;
4087 whitePieceMenu = CreatePieceMenu("menuW", 0);
4088 blackPieceMenu = CreatePieceMenu("menuB", 1);
4090 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
4091 XtRegisterGrabAction(PieceMenuPopup, True,
4092 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4093 GrabModeAsync, GrabModeAsync);
4095 XtSetArg(args[0], XtNlabel, _("Drop"));
4096 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4097 boardWidget, args, 1);
4098 for (i = 0; i < DROP_MENU_SIZE; i++) {
4099 String item = dropMenuStrings[i];
4101 if (strcmp(item, "----") == 0) {
4102 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4105 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4106 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4108 selection = dropMenuTranslation[i];
4109 XtAddCallback(entry, XtNcallback,
4110 (XtCallbackProc) DropMenuSelect,
4111 (caddr_t) selection);
4125 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4126 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4127 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4128 dmEnables[i].piece);
4129 XtSetSensitive(entry, p != NULL || !appData.testLegality
4130 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4131 && !appData.icsActive));
4133 while (p && *p++ == dmEnables[i].piece) count++;
4134 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4136 XtSetArg(args[j], XtNlabel, label); j++;
4137 XtSetValues(entry, args, j);
4142 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4144 String whichMenu; int menuNr = -2;
4145 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4146 if (event->type == ButtonRelease)
4147 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4148 else if (event->type == ButtonPress)
4149 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4151 case 0: whichMenu = params[0]; break;
4152 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4154 case -1: if (errorUp) ErrorPopDown();
4157 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4161 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4163 if (pmFromX < 0 || pmFromY < 0) return;
4164 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4168 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4170 if (pmFromX < 0 || pmFromY < 0) return;
4171 DropMenuEvent(piece, pmFromX, pmFromY);
4175 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4177 shiftKey = prms[0][0] & 1;
4182 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4184 shiftKey = prms[0][0] & 1;
4190 * If the user selects on a border boundary, return -1; if off the board,
4191 * return -2. Otherwise map the event coordinate to the square.
4194 EventToSquare (int x, int limit)
4201 if ((x % (squareSize + lineGap)) >= squareSize)
4203 x /= (squareSize + lineGap);
4210 do_flash_delay (unsigned long msec)
4216 drawHighlight (int file, int rank, GC gc)
4220 if (lineGap == 0) return;
4223 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4224 (squareSize + lineGap);
4225 y = lineGap/2 + rank * (squareSize + lineGap);
4227 x = lineGap/2 + file * (squareSize + lineGap);
4228 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4229 (squareSize + lineGap);
4232 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4233 squareSize+lineGap, squareSize+lineGap);
4236 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4237 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4240 SetHighlights (int fromX, int fromY, int toX, int toY)
4242 if (hi1X != fromX || hi1Y != fromY) {
4243 if (hi1X >= 0 && hi1Y >= 0) {
4244 drawHighlight(hi1X, hi1Y, lineGC);
4246 } // [HGM] first erase both, then draw new!
4247 if (hi2X != toX || hi2Y != toY) {
4248 if (hi2X >= 0 && hi2Y >= 0) {
4249 drawHighlight(hi2X, hi2Y, lineGC);
4252 if (hi1X != fromX || hi1Y != fromY) {
4253 if (fromX >= 0 && fromY >= 0) {
4254 drawHighlight(fromX, fromY, highlineGC);
4257 if (hi2X != toX || hi2Y != toY) {
4258 if (toX >= 0 && toY >= 0) {
4259 drawHighlight(toX, toY, highlineGC);
4262 if(toX<0) // clearing the highlights must have damaged arrow
4263 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4273 SetHighlights(-1, -1, -1, -1);
4278 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4280 if (pm1X != fromX || pm1Y != fromY) {
4281 if (pm1X >= 0 && pm1Y >= 0) {
4282 drawHighlight(pm1X, pm1Y, lineGC);
4284 if (fromX >= 0 && fromY >= 0) {
4285 drawHighlight(fromX, fromY, prelineGC);
4288 if (pm2X != toX || pm2Y != toY) {
4289 if (pm2X >= 0 && pm2Y >= 0) {
4290 drawHighlight(pm2X, pm2Y, lineGC);
4292 if (toX >= 0 && toY >= 0) {
4293 drawHighlight(toX, toY, prelineGC);
4303 ClearPremoveHighlights ()
4305 SetPremoveHighlights(-1, -1, -1, -1);
4309 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4311 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4312 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4314 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4315 if(textureW[kind] < W*squareSize)
4316 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4318 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4319 if(textureH[kind] < H*squareSize)
4320 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4322 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4327 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4328 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4330 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4331 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4332 squareSize, squareSize, x*fac, y*fac);
4334 if (useImages && useImageSqs) {
4338 pm = xpmLightSquare;
4343 case 2: /* neutral */
4348 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4349 squareSize, squareSize, x*fac, y*fac);
4359 case 2: /* neutral */
4364 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4369 I split out the routines to draw a piece so that I could
4370 make a generic flash routine.
4373 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4375 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4376 switch (square_color) {
4378 case 2: /* neutral */
4380 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4381 ? *pieceToOutline(piece)
4382 : *pieceToSolid(piece),
4383 dest, bwPieceGC, 0, 0,
4384 squareSize, squareSize, x, y);
4387 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4388 ? *pieceToSolid(piece)
4389 : *pieceToOutline(piece),
4390 dest, wbPieceGC, 0, 0,
4391 squareSize, squareSize, x, y);
4397 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4399 switch (square_color) {
4401 case 2: /* neutral */
4403 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4404 ? *pieceToOutline(piece)
4405 : *pieceToSolid(piece),
4406 dest, bwPieceGC, 0, 0,
4407 squareSize, squareSize, x, y, 1);
4410 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4411 ? *pieceToSolid(piece)
4412 : *pieceToOutline(piece),
4413 dest, wbPieceGC, 0, 0,
4414 squareSize, squareSize, x, y, 1);
4420 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4422 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4423 switch (square_color) {
4425 XCopyPlane(xDisplay, *pieceToSolid(piece),
4426 dest, (int) piece < (int) BlackPawn
4427 ? wlPieceGC : blPieceGC, 0, 0,
4428 squareSize, squareSize, x, y, 1);
4431 XCopyPlane(xDisplay, *pieceToSolid(piece),
4432 dest, (int) piece < (int) BlackPawn
4433 ? wdPieceGC : bdPieceGC, 0, 0,
4434 squareSize, squareSize, x, y, 1);
4436 case 2: /* neutral */
4438 XCopyPlane(xDisplay, *pieceToSolid(piece),
4439 dest, (int) piece < (int) BlackPawn
4440 ? wjPieceGC : bjPieceGC, 0, 0,
4441 squareSize, squareSize, x, y, 1);
4447 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4449 int kind, p = piece;
4451 switch (square_color) {
4453 case 2: /* neutral */
4455 if ((int)piece < (int) BlackPawn) {
4463 if ((int)piece < (int) BlackPawn) {
4471 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4472 if(useTexture & square_color+1) {
4473 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4474 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4475 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4476 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4477 XSetClipMask(xDisplay, wlPieceGC, None);
4478 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4480 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4481 dest, wlPieceGC, 0, 0,
4482 squareSize, squareSize, x, y);
4485 typedef void (*DrawFunc)();
4490 if (appData.monoMode) {
4491 if (DefaultDepth(xDisplay, xScreen) == 1) {
4492 return monoDrawPiece_1bit;
4494 return monoDrawPiece;
4498 return colorDrawPieceImage;
4500 return colorDrawPiece;
4504 /* [HR] determine square color depending on chess variant. */
4506 SquareColor (int row, int column)
4510 if (gameInfo.variant == VariantXiangqi) {
4511 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4513 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4515 } else if (row <= 4) {
4521 square_color = ((column + row) % 2) == 1;
4524 /* [hgm] holdings: next line makes all holdings squares light */
4525 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4527 return square_color;
4531 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4533 int square_color, x, y, direction, font_ascent, font_descent;
4536 XCharStruct overall;
4540 /* Calculate delay in milliseconds (2-delays per complete flash) */
4541 flash_delay = 500 / appData.flashRate;
4544 x = lineGap + ((BOARD_WIDTH-1)-column) *
4545 (squareSize + lineGap);
4546 y = lineGap + row * (squareSize + lineGap);
4548 x = lineGap + column * (squareSize + lineGap);
4549 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4550 (squareSize + lineGap);
4553 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4555 square_color = SquareColor(row, column);
4557 if ( // [HGM] holdings: blank out area between board and holdings
4558 column == BOARD_LEFT-1 || column == BOARD_RGHT
4559 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4560 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4561 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4563 // [HGM] print piece counts next to holdings
4564 string[1] = NULLCHAR;
4565 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4566 string[0] = '0' + piece;
4567 XTextExtents(countFontStruct, string, 1, &direction,
4568 &font_ascent, &font_descent, &overall);
4569 if (appData.monoMode) {
4570 XDrawImageString(xDisplay, xBoardWindow, countGC,
4571 x + squareSize - overall.width - 2,
4572 y + font_ascent + 1, string, 1);
4574 XDrawString(xDisplay, xBoardWindow, countGC,
4575 x + squareSize - overall.width - 2,
4576 y + font_ascent + 1, string, 1);
4579 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4580 string[0] = '0' + piece;
4581 XTextExtents(countFontStruct, string, 1, &direction,
4582 &font_ascent, &font_descent, &overall);
4583 if (appData.monoMode) {
4584 XDrawImageString(xDisplay, xBoardWindow, countGC,
4585 x + 2, y + font_ascent + 1, string, 1);
4587 XDrawString(xDisplay, xBoardWindow, countGC,
4588 x + 2, y + font_ascent + 1, string, 1);
4592 if (piece == EmptySquare || appData.blindfold) {
4593 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4595 drawfunc = ChooseDrawFunc();
4597 if (do_flash && appData.flashCount > 0) {
4598 for (i=0; i<appData.flashCount; ++i) {
4599 drawfunc(piece, square_color, x, y, xBoardWindow);
4600 XSync(xDisplay, False);
4601 do_flash_delay(flash_delay);
4603 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4604 XSync(xDisplay, False);
4605 do_flash_delay(flash_delay);
4608 drawfunc(piece, square_color, x, y, xBoardWindow);
4612 string[1] = NULLCHAR;
4613 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4614 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4615 string[0] = 'a' + column - BOARD_LEFT;
4616 XTextExtents(coordFontStruct, string, 1, &direction,
4617 &font_ascent, &font_descent, &overall);
4618 if (appData.monoMode) {
4619 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4620 x + squareSize - overall.width - 2,
4621 y + squareSize - font_descent - 1, string, 1);
4623 XDrawString(xDisplay, xBoardWindow, coordGC,
4624 x + squareSize - overall.width - 2,
4625 y + squareSize - font_descent - 1, string, 1);
4628 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4629 string[0] = ONE + row;
4630 XTextExtents(coordFontStruct, string, 1, &direction,
4631 &font_ascent, &font_descent, &overall);
4632 if (appData.monoMode) {
4633 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4634 x + 2, y + font_ascent + 1, string, 1);
4636 XDrawString(xDisplay, xBoardWindow, coordGC,
4637 x + 2, y + font_ascent + 1, string, 1);
4640 if(!partnerUp && marker[row][column]) {
4641 if(appData.monoMode) {
4642 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4643 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4644 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4645 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4647 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4648 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4652 static WindowPlacement wpNew;
4655 CoDrag (Widget sh, WindowPlacement *wp)
4658 int j=0, touch=0, fudge = 2;
4659 GetActualPlacement(sh, wp);
4660 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
4661 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
4662 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
4663 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
4664 if(!touch ) return; // only windows that touch co-move
4665 wp->x += wpNew.x - wpMain.x;
4666 wp->y += wpNew.y - wpMain.y;
4667 XtSetArg(args[j], XtNx, wp->x); j++;
4668 XtSetArg(args[j], XtNy, wp->y); j++;
4669 XtSetValues(sh, args, j);
4675 GetActualPlacement(shellWidget, &wpNew);
4676 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y) return; // not moved, false alarm
4677 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
4678 if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
4679 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
4680 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
4681 wpMain.x = wpNew.x; wpMain.y = wpNew.y;
4682 XDrawPosition(boardWidget, True, NULL);
4689 static XtIntervalId delayedDragID = 0;
4690 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
4692 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
4695 /* Why is this needed on some versions of X? */
4697 EventProc (Widget widget, caddr_t unused, XEvent *event)
4699 if (!XtIsRealized(widget))
4701 switch (event->type) {
4702 case ConfigureNotify: // main window is being dragged: drag attached windows with it
4703 if(appData.useStickyWindows)
4704 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
4707 if (event->xexpose.count > 0) return; /* no clipping is done */
4708 XDrawPosition(widget, True, NULL);
4709 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4710 flipView = !flipView; partnerUp = !partnerUp;
4711 XDrawPosition(widget, True, NULL);
4712 flipView = !flipView; partnerUp = !partnerUp;
4716 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4724 DrawPosition (int fullRedraw, Board board)
4726 XDrawPosition(boardWidget, fullRedraw, board);
4729 /* Returns 1 if there are "too many" differences between b1 and b2
4730 (i.e. more than 1 move was made) */
4732 too_many_diffs (Board b1, Board b2)
4737 for (i=0; i<BOARD_HEIGHT; ++i) {
4738 for (j=0; j<BOARD_WIDTH; ++j) {
4739 if (b1[i][j] != b2[i][j]) {
4740 if (++c > 4) /* Castling causes 4 diffs */
4748 /* Matrix describing castling maneuvers */
4749 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4750 static int castling_matrix[4][5] = {
4751 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4752 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4753 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4754 { 7, 7, 4, 5, 6 } /* 0-0, black */
4757 /* Checks whether castling occurred. If it did, *rrow and *rcol
4758 are set to the destination (row,col) of the rook that moved.
4760 Returns 1 if castling occurred, 0 if not.
4762 Note: Only handles a max of 1 castling move, so be sure
4763 to call too_many_diffs() first.
4766 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4771 /* For each type of castling... */
4772 for (i=0; i<4; ++i) {
4773 r = castling_matrix[i];
4775 /* Check the 4 squares involved in the castling move */
4777 for (j=1; j<=4; ++j) {
4778 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4785 /* All 4 changed, so it must be a castling move */
4794 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4796 DrawSeekAxis (int x, int y, int xTo, int yTo)
4798 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4802 DrawSeekBackground (int left, int top, int right, int bottom)
4804 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4808 DrawSeekText (char *buf, int x, int y)
4810 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4814 DrawSeekDot (int x, int y, int colorNr)
4816 int square = colorNr & 0x80;
4819 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4821 XFillRectangle(xDisplay, xBoardWindow, color,
4822 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4824 XFillArc(xDisplay, xBoardWindow, color,
4825 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4828 static int damage[2][BOARD_RANKS][BOARD_FILES];
4831 * event handler for redrawing the board
4834 XDrawPosition (Widget w, int repaint, Board board)
4837 static int lastFlipView = 0;
4838 static int lastBoardValid[2] = {0, 0};
4839 static Board lastBoard[2];
4842 int nr = twoBoards*partnerUp;
4844 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4846 if (board == NULL) {
4847 if (!lastBoardValid[nr]) return;
4848 board = lastBoard[nr];
4850 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4851 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4852 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4857 * It would be simpler to clear the window with XClearWindow()
4858 * but this causes a very distracting flicker.
4861 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4863 if ( lineGap && IsDrawArrowEnabled())
4864 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4865 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4867 /* If too much changes (begin observing new game, etc.), don't
4869 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4871 /* Special check for castling so we don't flash both the king
4872 and the rook (just flash the king). */
4874 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4875 /* Draw rook with NO flashing. King will be drawn flashing later */
4876 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4877 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4881 /* First pass -- Draw (newly) empty squares and repair damage.
4882 This prevents you from having a piece show up twice while it
4883 is flashing on its new square */
4884 for (i = 0; i < BOARD_HEIGHT; i++)
4885 for (j = 0; j < BOARD_WIDTH; j++)
4886 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4887 || damage[nr][i][j]) {
4888 DrawSquare(i, j, board[i][j], 0);
4889 damage[nr][i][j] = False;
4892 /* Second pass -- Draw piece(s) in new position and flash them */
4893 for (i = 0; i < BOARD_HEIGHT; i++)
4894 for (j = 0; j < BOARD_WIDTH; j++)
4895 if (board[i][j] != lastBoard[nr][i][j]) {
4896 DrawSquare(i, j, board[i][j], do_flash);
4900 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4901 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4902 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4904 for (i = 0; i < BOARD_HEIGHT; i++)
4905 for (j = 0; j < BOARD_WIDTH; j++) {
4906 DrawSquare(i, j, board[i][j], 0);
4907 damage[nr][i][j] = False;
4911 CopyBoard(lastBoard[nr], board);
4912 lastBoardValid[nr] = 1;
4913 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4914 lastFlipView = flipView;
4916 /* Draw highlights */
4917 if (pm1X >= 0 && pm1Y >= 0) {
4918 drawHighlight(pm1X, pm1Y, prelineGC);
4920 if (pm2X >= 0 && pm2Y >= 0) {
4921 drawHighlight(pm2X, pm2Y, prelineGC);
4923 if (hi1X >= 0 && hi1Y >= 0) {
4924 drawHighlight(hi1X, hi1Y, highlineGC);
4926 if (hi2X >= 0 && hi2Y >= 0) {
4927 drawHighlight(hi2X, hi2Y, highlineGC);
4929 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4931 /* If piece being dragged around board, must redraw that too */
4934 XSync(xDisplay, False);
4939 * event handler for redrawing the board
4942 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4944 XDrawPosition(w, True, NULL);
4949 * event handler for parsing user moves
4951 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4952 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4953 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4954 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4955 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4956 // and at the end FinishMove() to perform the move after optional promotion popups.
4957 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4959 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4961 if (w != boardWidget || errorExitStatus != -1) return;
4962 if(nprms) shiftKey = !strcmp(prms[0], "1");
4965 if (event->type == ButtonPress) {
4966 XtPopdown(promotionShell);
4967 XtDestroyWidget(promotionShell);
4968 promotionUp = False;
4976 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4977 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4978 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4982 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4984 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4985 DragPieceMove(event->xmotion.x, event->xmotion.y);
4989 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4990 { // [HGM] pv: walk PV
4991 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4994 static int savedIndex; /* gross that this is global */
4997 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
5000 XawTextPosition index, dummy;
5003 XawTextGetSelectionPos(w, &index, &dummy);
5004 XtSetArg(arg, XtNstring, &val);
5005 XtGetValues(w, &arg, 1);
5006 ReplaceComment(savedIndex, val);
5007 if(savedIndex != currentMove) ToNrEvent(savedIndex);
5008 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
5012 EditCommentPopUp (int index, char *title, char *text)
5015 if (text == NULL) text = "";
5016 NewCommentPopup(title, text, index);
5025 extern Option boxOptions[];
5035 edit = boxOptions[0].handle;
5037 XtSetArg(args[j], XtNstring, &val); j++;
5038 XtGetValues(edit, args, j);
5040 SendMultiLineToICS(val);
5041 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5042 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5046 ICSInputBoxPopDown ()
5052 CommentPopUp (char *title, char *text)
5054 savedIndex = currentMove; // [HGM] vari
5055 NewCommentPopup(title, text, currentMove);
5064 static char *openName;
5070 (void) (*fileProc)(openFP, 0, openName);
5074 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
5076 fileProc = proc; /* I can't see a way not */
5077 fileOpenMode = openMode; /* to use globals here */
5078 { // [HGM] use file-selector dialog stolen from Ghostview
5079 int index; // this is not supported yet
5080 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5081 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5082 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5083 ScheduleDelayedEvent(&DelayedLoad, 50);
5090 if (!filenameUp) return;
5091 XtPopdown(fileNameShell);
5092 XtDestroyWidget(fileNameShell);
5098 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5103 XtSetArg(args[0], XtNlabel, &name);
5104 XtGetValues(w, args, 1);
5106 if (strcmp(name, _("cancel")) == 0) {
5111 FileNameAction(w, NULL, NULL, NULL);
5115 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5123 name = XawDialogGetValueString(w = XtParent(w));
5125 if ((name != NULL) && (*name != NULLCHAR)) {
5126 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5127 XtPopdown(w = XtParent(XtParent(w)));
5131 p = strrchr(buf, ' ');
5138 fullname = ExpandPathName(buf);
5140 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5143 f = fopen(fullname, fileOpenMode);
5145 DisplayError(_("Failed to open file"), errno);
5147 (void) (*fileProc)(f, index, buf);
5154 XtPopdown(w = XtParent(XtParent(w)));
5164 Widget dialog, layout;
5166 Dimension bw_width, pw_width;
5168 char *PromoChars = "wglcqrbnkac+=\0";
5171 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5172 XtGetValues(boardWidget, args, j);
5175 XtSetArg(args[j], XtNresizable, True); j++;
5176 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5178 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5179 shellWidget, args, j);
5181 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5182 layoutArgs, XtNumber(layoutArgs));
5185 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5186 XtSetArg(args[j], XtNborderWidth, 0); j++;
5187 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5190 if(gameInfo.variant != VariantShogi) {
5191 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5192 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5193 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5194 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5195 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5197 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5198 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5199 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5200 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5202 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5203 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5204 gameInfo.variant == VariantGiveaway) {
5205 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5207 if(gameInfo.variant == VariantCapablanca ||
5208 gameInfo.variant == VariantGothic ||
5209 gameInfo.variant == VariantCapaRandom) {
5210 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5211 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5213 } else // [HGM] shogi
5215 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5216 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5218 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5220 XtRealizeWidget(promotionShell);
5221 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5224 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5225 XtGetValues(promotionShell, args, j);
5227 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5228 lineGap + squareSize/3 +
5229 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5230 0 : 6*(squareSize + lineGap)), &x, &y);
5233 XtSetArg(args[j], XtNx, x); j++;
5234 XtSetArg(args[j], XtNy, y); j++;
5235 XtSetValues(promotionShell, args, j);
5237 XtPopup(promotionShell, XtGrabNone);
5245 if (!promotionUp) return;
5246 XtPopdown(promotionShell);
5247 XtDestroyWidget(promotionShell);
5248 promotionUp = False;
5252 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5254 int promoChar = * (const char *) client_data;
5258 if (fromX == -1) return;
5265 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5267 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5268 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5274 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5276 dialogError = errorUp = False;
5277 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5279 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5286 if (!errorUp) return;
5287 dialogError = errorUp = False;
5288 XtPopdown(errorShell);
5289 XtDestroyWidget(errorShell);
5290 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5294 ErrorPopUp (char *title, char *label, int modal)
5297 Widget dialog, layout;
5301 Dimension bw_width, pw_width;
5302 Dimension pw_height;
5306 XtSetArg(args[i], XtNresizable, True); i++;
5307 XtSetArg(args[i], XtNtitle, title); i++;
5309 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5310 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
5312 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5313 layoutArgs, XtNumber(layoutArgs));
5316 XtSetArg(args[i], XtNlabel, label); i++;
5317 XtSetArg(args[i], XtNborderWidth, 0); i++;
5318 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5321 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5323 XtRealizeWidget(errorShell);
5324 CatchDeleteWindow(errorShell, "ErrorPopDown");
5327 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5328 XtGetValues(boardWidget, args, i);
5330 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5331 XtSetArg(args[i], XtNheight, &pw_height); i++;
5332 XtGetValues(errorShell, args, i);
5335 /* This code seems to tickle an X bug if it is executed too soon
5336 after xboard starts up. The coordinates get transformed as if
5337 the main window was positioned at (0, 0).
5339 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5340 0 - pw_height + squareSize / 3, &x, &y);
5342 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5343 RootWindowOfScreen(XtScreen(boardWidget)),
5344 (bw_width - pw_width) / 2,
5345 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5349 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5352 XtSetArg(args[i], XtNx, x); i++;
5353 XtSetArg(args[i], XtNy, y); i++;
5354 XtSetValues(errorShell, args, i);
5357 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5360 /* Disable all user input other than deleting the window */
5361 static int frozen = 0;
5367 /* Grab by a widget that doesn't accept input */
5368 XtAddGrab(messageWidget, TRUE, FALSE);
5372 /* Undo a FreezeUI */
5376 if (!frozen) return;
5377 XtRemoveGrab(messageWidget);
5382 ModeToWidgetName (GameMode mode)
5385 case BeginningOfGame:
5386 if (appData.icsActive)
5387 return "menuMode.ICS Client";
5388 else if (appData.noChessProgram ||
5389 *appData.cmailGameName != NULLCHAR)
5390 return "menuMode.Edit Game";
5392 return "menuMode.Machine Black";
5393 case MachinePlaysBlack:
5394 return "menuMode.Machine Black";
5395 case MachinePlaysWhite:
5396 return "menuMode.Machine White";
5398 return "menuMode.Analysis Mode";
5400 return "menuMode.Analyze File";
5401 case TwoMachinesPlay:
5402 return "menuMode.Two Machines";
5404 return "menuMode.Edit Game";
5405 case PlayFromGameFile:
5406 return "menuFile.Load Game";
5408 return "menuMode.Edit Position";
5410 return "menuMode.Training";
5411 case IcsPlayingWhite:
5412 case IcsPlayingBlack:
5416 return "menuMode.ICS Client";
5427 static int oldPausing = FALSE;
5428 static GameMode oldmode = (GameMode) -1;
5431 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5433 if (pausing != oldPausing) {
5434 oldPausing = pausing;
5436 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5438 XtSetArg(args[0], XtNleftBitmap, None);
5440 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5443 if (appData.showButtonBar) {
5444 /* Always toggle, don't set. Previous code messes up when
5445 invoked while the button is pressed, as releasing it
5446 toggles the state again. */
5449 XtSetArg(args[0], XtNbackground, &oldbg);
5450 XtSetArg(args[1], XtNforeground, &oldfg);
5451 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5453 XtSetArg(args[0], XtNbackground, oldfg);
5454 XtSetArg(args[1], XtNforeground, oldbg);
5456 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5460 wname = ModeToWidgetName(oldmode);
5461 if (wname != NULL) {
5462 XtSetArg(args[0], XtNleftBitmap, None);
5463 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5465 wname = ModeToWidgetName(gameMode);
5466 if (wname != NULL) {
5467 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5468 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5471 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5472 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5474 /* Maybe all the enables should be handled here, not just this one */
5475 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5476 gameMode == Training || gameMode == PlayFromGameFile);
5481 * Button/menu procedures
5484 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5490 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5492 cmailMsgLoaded = FALSE;
5493 if (gameNumber == 0) {
5494 int error = GameListBuild(f);
5496 DisplayError(_("Cannot build game list"), error);
5497 } else if (!ListEmpty(&gameList) &&
5498 ((ListGame *) gameList.tailPred)->number > 1) {
5499 GameListPopUp(f, title);
5505 return LoadGame(f, gameNumber, title, FALSE);
5509 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5511 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5514 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5518 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5524 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5530 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5536 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5542 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5548 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5554 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5556 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5559 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5563 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5565 FileNamePopUp(_("Save game file name?"),
5566 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5567 appData.oldSaveStyle ? ".game" : ".pgn",
5572 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5574 FileNamePopUp(_("Save position file name?"),
5575 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5576 appData.oldSaveStyle ? ".pos" : ".fen",
5581 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5583 ReloadCmailMsgEvent(FALSE);
5587 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5592 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5593 char *selected_fen_position=NULL;
5596 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5597 Atom *type_return, XtPointer *value_return,
5598 unsigned long *length_return, int *format_return)
5600 char *selection_tmp;
5602 if (!selected_fen_position) return False; /* should never happen */
5603 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5604 /* note: since no XtSelectionDoneProc was registered, Xt will
5605 * automatically call XtFree on the value returned. So have to
5606 * make a copy of it allocated with XtMalloc */
5607 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5608 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5610 *value_return=selection_tmp;
5611 *length_return=strlen(selection_tmp);
5612 *type_return=*target;
5613 *format_return = 8; /* bits per byte */
5615 } else if (*target == XA_TARGETS(xDisplay)) {
5616 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5617 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5618 targets_tmp[1] = XA_STRING;
5619 *value_return = targets_tmp;
5620 *type_return = XA_ATOM;
5623 // This code leads to a read of value_return out of bounds on 64-bit systems.
5624 // Other code which I have seen always sets *format_return to 32 independent of
5625 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5626 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5627 *format_return = 8 * sizeof(Atom);
5628 if (*format_return > 32) {
5629 *length_return *= *format_return / 32;
5630 *format_return = 32;
5633 *format_return = 32;
5641 /* note: when called from menu all parameters are NULL, so no clue what the
5642 * Widget which was clicked on was, or what the click event was
5645 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5648 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5649 * have a notion of a position that is selected but not copied.
5650 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5652 if(gameMode == EditPosition) EditPositionDone(TRUE);
5653 if (selected_fen_position) free(selected_fen_position);
5654 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5655 if (!selected_fen_position) return;
5656 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5658 SendPositionSelection,
5659 NULL/* lose_ownership_proc */ ,
5660 NULL/* transfer_done_proc */);
5661 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5663 SendPositionSelection,
5664 NULL/* lose_ownership_proc */ ,
5665 NULL/* transfer_done_proc */);
5669 CopyFENToClipboard ()
5670 { // wrapper to make call from back-end possible
5671 CopyPositionProc(NULL, NULL, NULL, NULL);
5674 /* function called when the data to Paste is ready */
5676 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5677 Atom *type, XtPointer value, unsigned long *len, int *format)
5680 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5681 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5682 EditPositionPasteFEN(fenstr);
5686 /* called when Paste Position button is pressed,
5687 * all parameters will be NULL */
5688 void PastePositionProc(w, event, prms, nprms)
5694 XtGetSelectionValue(menuBarWidget,
5695 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5696 /* (XtSelectionCallbackProc) */ PastePositionCB,
5697 NULL, /* client_data passed to PastePositionCB */
5699 /* better to use the time field from the event that triggered the
5700 * call to this function, but that isn't trivial to get
5708 SendGameSelection (Widget w, Atom *selection, Atom *target,
5709 Atom *type_return, XtPointer *value_return,
5710 unsigned long *length_return, int *format_return)
5712 char *selection_tmp;
5714 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5715 FILE* f = fopen(gameCopyFilename, "r");
5718 if (f == NULL) return False;
5722 selection_tmp = XtMalloc(len + 1);
5723 count = fread(selection_tmp, 1, len, f);
5726 XtFree(selection_tmp);
5729 selection_tmp[len] = NULLCHAR;
5730 *value_return = selection_tmp;
5731 *length_return = len;
5732 *type_return = *target;
5733 *format_return = 8; /* bits per byte */
5735 } else if (*target == XA_TARGETS(xDisplay)) {
5736 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5737 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5738 targets_tmp[1] = XA_STRING;
5739 *value_return = targets_tmp;
5740 *type_return = XA_ATOM;
5743 // This code leads to a read of value_return out of bounds on 64-bit systems.
5744 // Other code which I have seen always sets *format_return to 32 independent of
5745 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5746 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5747 *format_return = 8 * sizeof(Atom);
5748 if (*format_return > 32) {
5749 *length_return *= *format_return / 32;
5750 *format_return = 32;
5753 *format_return = 32;
5765 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5766 * have a notion of a game that is selected but not copied.
5767 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5769 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5772 NULL/* lose_ownership_proc */ ,
5773 NULL/* transfer_done_proc */);
5774 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5777 NULL/* lose_ownership_proc */ ,
5778 NULL/* transfer_done_proc */);
5781 /* note: when called from menu all parameters are NULL, so no clue what the
5782 * Widget which was clicked on was, or what the click event was
5785 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5789 ret = SaveGameToFile(gameCopyFilename, FALSE);
5796 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5798 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5802 /* function called when the data to Paste is ready */
5804 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5805 Atom *type, XtPointer value, unsigned long *len, int *format)
5808 if (value == NULL || *len == 0) {
5809 return; /* nothing had been selected to copy */
5811 f = fopen(gamePasteFilename, "w");
5813 DisplayError(_("Can't open temp file"), errno);
5816 fwrite(value, 1, *len, f);
5819 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5822 /* called when Paste Game button is pressed,
5823 * all parameters will be NULL */
5825 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5827 XtGetSelectionValue(menuBarWidget,
5828 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5829 /* (XtSelectionCallbackProc) */ PasteGameCB,
5830 NULL, /* client_data passed to PasteGameCB */
5832 /* better to use the time field from the event that triggered the
5833 * call to this function, but that isn't trivial to get
5844 SaveGameProc(NULL, NULL, NULL, NULL);
5849 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5855 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5861 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5863 MachineBlackEvent();
5867 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5869 MachineWhiteEvent();
5873 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5877 if (!first.analysisSupport) {
5878 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5879 DisplayError(buf, 0);
5882 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5883 if (appData.icsActive) {
5884 if (gameMode != IcsObserving) {
5885 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5886 DisplayError(buf, 0);
5888 if (appData.icsEngineAnalyze) {
5889 if (appData.debugMode)
5890 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5896 /* if enable, use want disable icsEngineAnalyze */
5897 if (appData.icsEngineAnalyze) {
5902 appData.icsEngineAnalyze = TRUE;
5903 if (appData.debugMode)
5904 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5906 #ifndef OPTIONSDIALOG
5907 if (!appData.showThinking)
5908 ShowThinkingProc(w,event,prms,nprms);
5915 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5917 if (!first.analysisSupport) {
5919 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5920 DisplayError(buf, 0);
5923 // Reset(FALSE, TRUE);
5924 #ifndef OPTIONSDIALOG
5925 if (!appData.showThinking)
5926 ShowThinkingProc(w,event,prms,nprms);
5929 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5930 AnalysisPeriodicEvent(1);
5934 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5940 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5946 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5952 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5958 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5960 EditPositionEvent();
5964 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5970 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5974 if (PopDown(1)) { // popdown succesful
5976 XtSetArg(args[j], XtNleftBitmap, None); j++;
5977 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5978 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5979 } else // was not up
5984 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5986 if (!PopDown(4)) ICSInputBoxPopUp();
5990 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5996 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6002 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6008 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6014 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6020 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6026 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6032 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6038 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6040 UserAdjudicationEvent(+1);
6044 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6046 UserAdjudicationEvent(-1);
6050 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6052 UserAdjudicationEvent(0);
6056 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6058 if (shellUp[4] == True)
6063 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6064 { // [HGM] input: let up-arrow recall previous line from history
6071 if (!shellUp[4]) return;
6072 edit = boxOptions[0].handle;
6074 XtSetArg(args[j], XtNstring, &val); j++;
6075 XtGetValues(edit, args, j);
6076 val = PrevInHistory(val);
6077 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6078 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6080 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6081 XawTextReplace(edit, 0, 0, &t);
6082 XawTextSetInsertionPoint(edit, 9999);
6087 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6088 { // [HGM] input: let down-arrow recall next line from history
6093 if (!shellUp[4]) return;
6094 edit = boxOptions[0].handle;
6095 val = NextInHistory();
6096 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6097 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6099 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6100 XawTextReplace(edit, 0, 0, &t);
6101 XawTextSetInsertionPoint(edit, 9999);
6106 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6108 StopObservingEvent();
6112 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6114 StopExaminingEvent();
6118 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6125 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6132 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6138 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6140 if (!TempBackwardActive) {
6141 TempBackwardActive = True;
6147 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6149 /* Check to see if triggered by a key release event for a repeating key.
6150 * If so the next queued event will be a key press of the same key at the same time */
6151 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6153 XPeekEvent(xDisplay, &next);
6154 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6155 next.xkey.keycode == event->xkey.keycode)
6159 TempBackwardActive = False;
6163 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6169 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6175 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6181 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6187 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6189 TruncateGameEvent();
6193 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6199 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6205 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6207 flipView = !flipView;
6208 DrawPosition(True, NULL);
6212 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6216 PonderNextMoveEvent(!appData.ponderNextMove);
6217 #ifndef OPTIONSDIALOG
6218 if (appData.ponderNextMove) {
6219 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6221 XtSetArg(args[0], XtNleftBitmap, None);
6223 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6228 #ifndef OPTIONSDIALOG
6230 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6234 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6236 if (appData.alwaysPromoteToQueen) {
6237 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6239 XtSetArg(args[0], XtNleftBitmap, None);
6241 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6246 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6250 appData.animateDragging = !appData.animateDragging;
6252 if (appData.animateDragging) {
6253 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6256 XtSetArg(args[0], XtNleftBitmap, None);
6258 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6263 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6267 appData.animate = !appData.animate;
6269 if (appData.animate) {
6270 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6273 XtSetArg(args[0], XtNleftBitmap, None);
6275 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6280 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6284 appData.autoCallFlag = !appData.autoCallFlag;
6286 if (appData.autoCallFlag) {
6287 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6289 XtSetArg(args[0], XtNleftBitmap, None);
6291 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6296 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6300 appData.autoFlipView = !appData.autoFlipView;
6302 if (appData.autoFlipView) {
6303 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6305 XtSetArg(args[0], XtNleftBitmap, None);
6307 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6312 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6316 appData.blindfold = !appData.blindfold;
6318 if (appData.blindfold) {
6319 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6321 XtSetArg(args[0], XtNleftBitmap, None);
6323 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6326 DrawPosition(True, NULL);
6330 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6334 appData.testLegality = !appData.testLegality;
6336 if (appData.testLegality) {
6337 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6339 XtSetArg(args[0], XtNleftBitmap, None);
6341 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6347 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6351 if (appData.flashCount == 0) {
6352 appData.flashCount = 3;
6354 appData.flashCount = -appData.flashCount;
6357 if (appData.flashCount > 0) {
6358 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6360 XtSetArg(args[0], XtNleftBitmap, None);
6362 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6368 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6372 appData.highlightDragging = !appData.highlightDragging;
6374 if (appData.highlightDragging) {
6375 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6377 XtSetArg(args[0], XtNleftBitmap, None);
6379 XtSetValues(XtNameToWidget(menuBarWidget,
6380 "menuOptions.Highlight Dragging"), args, 1);
6385 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6389 appData.highlightLastMove = !appData.highlightLastMove;
6391 if (appData.highlightLastMove) {
6392 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6394 XtSetArg(args[0], XtNleftBitmap, None);
6396 XtSetValues(XtNameToWidget(menuBarWidget,
6397 "menuOptions.Highlight Last Move"), args, 1);
6401 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6405 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6407 if (appData.highlightMoveWithArrow) {
6408 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6410 XtSetArg(args[0], XtNleftBitmap, None);
6412 XtSetValues(XtNameToWidget(menuBarWidget,
6413 "menuOptions.Arrow"), args, 1);
6418 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6422 appData.icsAlarm = !appData.icsAlarm;
6424 if (appData.icsAlarm) {
6425 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6427 XtSetArg(args[0], XtNleftBitmap, None);
6429 XtSetValues(XtNameToWidget(menuBarWidget,
6430 "menuOptions.ICS Alarm"), args, 1);
6435 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6439 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6441 if (appData.ringBellAfterMoves) {
6442 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6444 XtSetArg(args[0], XtNleftBitmap, None);
6446 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6451 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6455 appData.oneClick = !appData.oneClick;
6457 if (appData.oneClick) {
6458 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6460 XtSetArg(args[0], XtNleftBitmap, None);
6462 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6467 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6471 PeriodicUpdatesEvent(!appData.periodicUpdates);
6473 if (appData.periodicUpdates) {
6474 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6476 XtSetArg(args[0], XtNleftBitmap, None);
6478 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6483 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6487 appData.popupExitMessage = !appData.popupExitMessage;
6489 if (appData.popupExitMessage) {
6490 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6492 XtSetArg(args[0], XtNleftBitmap, None);
6494 XtSetValues(XtNameToWidget(menuBarWidget,
6495 "menuOptions.Popup Exit Message"), args, 1);
6499 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6503 appData.popupMoveErrors = !appData.popupMoveErrors;
6505 if (appData.popupMoveErrors) {
6506 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6508 XtSetArg(args[0], XtNleftBitmap, None);
6510 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6516 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6520 appData.premove = !appData.premove;
6522 if (appData.premove) {
6523 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6525 XtSetArg(args[0], XtNleftBitmap, None);
6527 XtSetValues(XtNameToWidget(menuBarWidget,
6528 "menuOptions.Premove"), args, 1);
6533 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6537 appData.showCoords = !appData.showCoords;
6539 if (appData.showCoords) {
6540 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6542 XtSetArg(args[0], XtNleftBitmap, None);
6544 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6547 DrawPosition(True, NULL);
6551 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6553 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6554 ShowThinkingEvent();
6558 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6562 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6563 ShowThinkingEvent();
6565 if (appData.hideThinkingFromHuman) {
6566 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6568 XtSetArg(args[0], XtNleftBitmap, None);
6570 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6576 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6580 saveSettingsOnExit = !saveSettingsOnExit;
6582 if (saveSettingsOnExit) {
6583 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6585 XtSetArg(args[0], XtNleftBitmap, None);
6587 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6592 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6594 SaveSettings(settingsFileName);
6598 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6601 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6607 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6611 if (nprms && *nprms > 0)
6615 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6620 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6626 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6632 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6636 char *zippy = _(" (with Zippy code)");
6640 snprintf(buf, sizeof(buf),
6642 "Copyright 1991 Digital Equipment Corporation\n"
6643 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6644 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6645 "%s is free software and carries NO WARRANTY;"
6646 "see the file COPYING for more information."),
6647 programVersion, zippy, PACKAGE);
6648 ErrorPopUp(_("About XBoard"), buf, FALSE);
6652 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6654 appData.debugMode = !appData.debugMode;
6658 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6664 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6670 DisplayMessage (char *message, char *extMessage)
6672 /* display a message in the message widget */
6681 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6686 message = extMessage;
6690 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6692 /* need to test if messageWidget already exists, since this function
6693 can also be called during the startup, if for example a Xresource
6694 is not set up correctly */
6697 XtSetArg(arg, XtNlabel, message);
6698 XtSetValues(messageWidget, &arg, 1);
6705 DisplayTitle (char *text)
6709 char title[MSG_SIZ];
6712 if (text == NULL) text = "";
6714 if (appData.titleInWindow) {
6716 XtSetArg(args[i], XtNlabel, text); i++;
6717 XtSetValues(titleWidget, args, i);
6720 if (*text != NULLCHAR) {
6721 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6722 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6723 } else if (appData.icsActive) {
6724 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6725 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6726 } else if (appData.cmailGameName[0] != NULLCHAR) {
6727 snprintf(icon, sizeof(icon), "%s", "CMail");
6728 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6730 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6731 } else if (gameInfo.variant == VariantGothic) {
6732 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6733 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6736 } else if (gameInfo.variant == VariantFalcon) {
6737 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6738 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6740 } else if (appData.noChessProgram) {
6741 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6742 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6744 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6745 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6748 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6749 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6750 XtSetValues(shellWidget, args, i);
6751 XSync(xDisplay, False);
6756 DisplayError (String message, int error)
6761 if (appData.debugMode || appData.matchMode) {
6762 fprintf(stderr, "%s: %s\n", programName, message);
6765 if (appData.debugMode || appData.matchMode) {
6766 fprintf(stderr, "%s: %s: %s\n",
6767 programName, message, strerror(error));
6769 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6772 ErrorPopUp(_("Error"), message, FALSE);
6777 DisplayMoveError (String message)
6781 DrawPosition(FALSE, NULL);
6782 if (appData.debugMode || appData.matchMode) {
6783 fprintf(stderr, "%s: %s\n", programName, message);
6785 if (appData.popupMoveErrors) {
6786 ErrorPopUp(_("Error"), message, FALSE);
6788 DisplayMessage(message, "");
6794 DisplayFatalError (String message, int error, int status)
6798 errorExitStatus = status;
6800 fprintf(stderr, "%s: %s\n", programName, message);
6802 fprintf(stderr, "%s: %s: %s\n",
6803 programName, message, strerror(error));
6804 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6807 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6808 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6815 DisplayInformation (String message)
6818 ErrorPopUp(_("Information"), message, TRUE);
6822 DisplayNote (String message)
6825 ErrorPopUp(_("Note"), message, FALSE);
6829 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6835 DisplayIcsInteractionTitle (String message)
6837 if (oldICSInteractionTitle == NULL) {
6838 /* Magic to find the old window title, adapted from vim */
6839 char *wina = getenv("WINDOWID");
6841 Window win = (Window) atoi(wina);
6842 Window root, parent, *children;
6843 unsigned int nchildren;
6844 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6846 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6847 if (!XQueryTree(xDisplay, win, &root, &parent,
6848 &children, &nchildren)) break;
6849 if (children) XFree((void *)children);
6850 if (parent == root || parent == 0) break;
6853 XSetErrorHandler(oldHandler);
6855 if (oldICSInteractionTitle == NULL) {
6856 oldICSInteractionTitle = "xterm";
6859 printf("\033]0;%s\007", message);
6863 char pendingReplyPrefix[MSG_SIZ];
6864 ProcRef pendingReplyPR;
6867 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6870 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6874 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6878 AskQuestionPopDown ()
6880 if (!askQuestionUp) return;
6881 XtPopdown(askQuestionShell);
6882 XtDestroyWidget(askQuestionShell);
6883 askQuestionUp = False;
6887 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6893 reply = XawDialogGetValueString(w = XtParent(w));
6894 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6895 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6896 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6897 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6898 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6899 AskQuestionPopDown();
6901 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6905 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6910 XtSetArg(args[0], XtNlabel, &name);
6911 XtGetValues(w, args, 1);
6913 if (strcmp(name, _("cancel")) == 0) {
6914 AskQuestionPopDown();
6916 AskQuestionReplyAction(w, NULL, NULL, NULL);
6921 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6924 Widget popup, layout, dialog, edit;
6930 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6931 pendingReplyPR = pr;
6934 XtSetArg(args[i], XtNresizable, True); i++;
6935 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6936 askQuestionShell = popup =
6937 XtCreatePopupShell(title, transientShellWidgetClass,
6938 shellWidget, args, i);
6941 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6942 layoutArgs, XtNumber(layoutArgs));
6945 XtSetArg(args[i], XtNlabel, question); i++;
6946 XtSetArg(args[i], XtNvalue, ""); i++;
6947 XtSetArg(args[i], XtNborderWidth, 0); i++;
6948 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6951 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6952 (XtPointer) dialog);
6953 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6954 (XtPointer) dialog);
6956 XtRealizeWidget(popup);
6957 CatchDeleteWindow(popup, "AskQuestionPopDown");
6959 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6960 &x, &y, &win_x, &win_y, &mask);
6962 XtSetArg(args[0], XtNx, x - 10);
6963 XtSetArg(args[1], XtNy, y - 30);
6964 XtSetValues(popup, args, 2);
6966 XtPopup(popup, XtGrabExclusive);
6967 askQuestionUp = True;
6969 edit = XtNameToWidget(dialog, "*value");
6970 XtSetKeyboardFocus(popup, edit);
6975 PlaySound (char *name)
6977 if (*name == NULLCHAR) {
6979 } else if (strcmp(name, "$") == 0) {
6980 putc(BELLCHAR, stderr);
6983 char *prefix = "", *sep = "";
6984 if(appData.soundProgram[0] == NULLCHAR) return;
6985 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6986 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6994 PlaySound(appData.soundMove);
7000 PlaySound(appData.soundIcsWin);
7006 PlaySound(appData.soundIcsLoss);
7012 PlaySound(appData.soundIcsDraw);
7016 PlayIcsUnfinishedSound ()
7018 PlaySound(appData.soundIcsUnfinished);
7024 PlaySound(appData.soundIcsAlarm);
7030 PlaySound(appData.soundTell);
7036 system("stty echo");
7043 system("stty -echo");
7048 RunCommand (char *buf)
7054 Colorize (ColorClass cc, int continuation)
7057 int count, outCount, error;
7059 if (textColors[(int)cc].bg > 0) {
7060 if (textColors[(int)cc].fg > 0) {
7061 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7062 textColors[(int)cc].fg, textColors[(int)cc].bg);
7064 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7065 textColors[(int)cc].bg);
7068 if (textColors[(int)cc].fg > 0) {
7069 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7070 textColors[(int)cc].fg);
7072 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7075 count = strlen(buf);
7076 outCount = OutputToProcess(NoProc, buf, count, &error);
7077 if (outCount < count) {
7078 DisplayFatalError(_("Error writing to display"), error, 1);
7081 if (continuation) return;
7084 PlaySound(appData.soundShout);
7087 PlaySound(appData.soundSShout);
7090 PlaySound(appData.soundChannel1);
7093 PlaySound(appData.soundChannel);
7096 PlaySound(appData.soundKibitz);
7099 PlaySound(appData.soundTell);
7101 case ColorChallenge:
7102 PlaySound(appData.soundChallenge);
7105 PlaySound(appData.soundRequest);
7108 PlaySound(appData.soundSeek);
7120 return getpwuid(getuid())->pw_name;
7124 ExpandPathName (char *path)
7126 static char static_buf[4*MSG_SIZ];
7127 char *d, *s, buf[4*MSG_SIZ];
7133 while (*s && isspace(*s))
7142 if (*(s+1) == '/') {
7143 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7147 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7148 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7149 pwd = getpwnam(buf);
7152 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7156 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7157 strcat(d, strchr(s+1, '/'));
7161 safeStrCpy(d, s, 4*MSG_SIZ );
7169 static char host_name[MSG_SIZ];
7171 #if HAVE_GETHOSTNAME
7172 gethostname(host_name, MSG_SIZ);
7174 #else /* not HAVE_GETHOSTNAME */
7175 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7176 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7178 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7180 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7181 #endif /* not HAVE_GETHOSTNAME */
7184 XtIntervalId delayedEventTimerXID = 0;
7185 DelayedEventCallback delayedEventCallback = 0;
7190 delayedEventTimerXID = 0;
7191 delayedEventCallback();
7195 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7197 if(delayedEventTimerXID && delayedEventCallback == cb)
7198 // [HGM] alive: replace, rather than add or flush identical event
7199 XtRemoveTimeOut(delayedEventTimerXID);
7200 delayedEventCallback = cb;
7201 delayedEventTimerXID =
7202 XtAppAddTimeOut(appContext, millisec,
7203 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7206 DelayedEventCallback
7209 if (delayedEventTimerXID) {
7210 return delayedEventCallback;
7217 CancelDelayedEvent ()
7219 if (delayedEventTimerXID) {
7220 XtRemoveTimeOut(delayedEventTimerXID);
7221 delayedEventTimerXID = 0;
7225 XtIntervalId loadGameTimerXID = 0;
7228 LoadGameTimerRunning ()
7230 return loadGameTimerXID != 0;
7234 StopLoadGameTimer ()
7236 if (loadGameTimerXID != 0) {
7237 XtRemoveTimeOut(loadGameTimerXID);
7238 loadGameTimerXID = 0;
7246 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7248 loadGameTimerXID = 0;
7253 StartLoadGameTimer (long millisec)
7256 XtAppAddTimeOut(appContext, millisec,
7257 (XtTimerCallbackProc) LoadGameTimerCallback,
7261 XtIntervalId analysisClockXID = 0;
7264 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7266 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7267 || appData.icsEngineAnalyze) { // [DM]
7268 AnalysisPeriodicEvent(0);
7269 StartAnalysisClock();
7274 StartAnalysisClock ()
7277 XtAppAddTimeOut(appContext, 2000,
7278 (XtTimerCallbackProc) AnalysisClockCallback,
7282 XtIntervalId clockTimerXID = 0;
7285 ClockTimerRunning ()
7287 return clockTimerXID != 0;
7293 if (clockTimerXID != 0) {
7294 XtRemoveTimeOut(clockTimerXID);
7303 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7310 StartClockTimer (long millisec)
7313 XtAppAddTimeOut(appContext, millisec,
7314 (XtTimerCallbackProc) ClockTimerCallback,
7319 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7324 /* check for low time warning */
7325 Pixel foregroundOrWarningColor = timerForegroundPixel;
7328 appData.lowTimeWarning &&
7329 (timer / 1000) < appData.icsAlarmTime)
7330 foregroundOrWarningColor = lowTimeWarningColor;
7332 if (appData.clockMode) {
7333 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7334 XtSetArg(args[0], XtNlabel, buf);
7336 snprintf(buf, MSG_SIZ, "%s ", color);
7337 XtSetArg(args[0], XtNlabel, buf);
7342 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7343 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7345 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7346 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7349 XtSetValues(w, args, 3);
7353 DisplayWhiteClock (long timeRemaining, int highlight)
7357 if(appData.noGUI) return;
7358 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7359 if (highlight && iconPixmap == bIconPixmap) {
7360 iconPixmap = wIconPixmap;
7361 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7362 XtSetValues(shellWidget, args, 1);
7367 DisplayBlackClock (long timeRemaining, int highlight)
7371 if(appData.noGUI) return;
7372 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7373 if (highlight && iconPixmap == wIconPixmap) {
7374 iconPixmap = bIconPixmap;
7375 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7376 XtSetValues(shellWidget, args, 1);
7395 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7399 int to_prog[2], from_prog[2];
7403 if (appData.debugMode) {
7404 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7407 /* We do NOT feed the cmdLine to the shell; we just
7408 parse it into blank-separated arguments in the
7409 most simple-minded way possible.
7412 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7415 while(*p == ' ') p++;
7417 if(*p == '"' || *p == '\'')
7418 p = strchr(++argv[i-1], *p);
7419 else p = strchr(p, ' ');
7420 if (p == NULL) break;
7425 SetUpChildIO(to_prog, from_prog);
7427 if ((pid = fork()) == 0) {
7429 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7430 close(to_prog[1]); // first close the unused pipe ends
7431 close(from_prog[0]);
7432 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7433 dup2(from_prog[1], 1);
7434 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7435 close(from_prog[1]); // and closing again loses one of the pipes!
7436 if(fileno(stderr) >= 2) // better safe than sorry...
7437 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7439 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7444 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7446 execvp(argv[0], argv);
7448 /* If we get here, exec failed */
7453 /* Parent process */
7455 close(from_prog[1]);
7457 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7460 cp->fdFrom = from_prog[0];
7461 cp->fdTo = to_prog[1];
7466 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7468 AlarmCallBack (int n)
7474 DestroyChildProcess (ProcRef pr, int signalType)
7476 ChildProc *cp = (ChildProc *) pr;
7478 if (cp->kind != CPReal) return;
7480 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7481 signal(SIGALRM, AlarmCallBack);
7483 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7484 kill(cp->pid, SIGKILL); // kill it forcefully
7485 wait((int *) 0); // and wait again
7489 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7491 /* Process is exiting either because of the kill or because of
7492 a quit command sent by the backend; either way, wait for it to die.
7501 InterruptChildProcess (ProcRef pr)
7503 ChildProc *cp = (ChildProc *) pr;
7505 if (cp->kind != CPReal) return;
7506 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7510 OpenTelnet (char *host, char *port, ProcRef *pr)
7512 char cmdLine[MSG_SIZ];
7514 if (port[0] == NULLCHAR) {
7515 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7517 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7519 return StartChildProcess(cmdLine, "", pr);
7523 OpenTCP (char *host, char *port, ProcRef *pr)
7526 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7527 #else /* !OMIT_SOCKETS */
7528 struct addrinfo hints;
7529 struct addrinfo *ais, *ai;
7534 memset(&hints, 0, sizeof(hints));
7535 hints.ai_family = AF_UNSPEC;
7536 hints.ai_socktype = SOCK_STREAM;
7538 error = getaddrinfo(host, port, &hints, &ais);
7540 /* a getaddrinfo error is not an errno, so can't return it */
7541 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7542 host, port, gai_strerror(error));
7546 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7547 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7551 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7564 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7570 #endif /* !OMIT_SOCKETS */
7576 OpenCommPort (char *name, ProcRef *pr)
7581 fd = open(name, 2, 0);
7582 if (fd < 0) return errno;
7584 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7595 OpenLoopback (ProcRef *pr)
7600 SetUpChildIO(to, from);
7602 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7605 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7613 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7615 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7619 #define INPUT_SOURCE_BUF_SIZE 8192
7628 char buf[INPUT_SOURCE_BUF_SIZE];
7633 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7635 InputSource *is = (InputSource *) closure;
7640 if (is->lineByLine) {
7641 count = read(is->fd, is->unused,
7642 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7644 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7647 is->unused += count;
7649 while (p < is->unused) {
7650 q = memchr(p, '\n', is->unused - p);
7651 if (q == NULL) break;
7653 (is->func)(is, is->closure, p, q - p, 0);
7657 while (p < is->unused) {
7662 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7667 (is->func)(is, is->closure, is->buf, count, error);
7672 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7675 ChildProc *cp = (ChildProc *) pr;
7677 is = (InputSource *) calloc(1, sizeof(InputSource));
7678 is->lineByLine = lineByLine;
7682 is->fd = fileno(stdin);
7684 is->kind = cp->kind;
7685 is->fd = cp->fdFrom;
7688 is->unused = is->buf;
7691 is->xid = XtAppAddInput(appContext, is->fd,
7692 (XtPointer) (XtInputReadMask),
7693 (XtInputCallbackProc) DoInputCallback,
7695 is->closure = closure;
7696 return (InputSourceRef) is;
7700 RemoveInputSource (InputSourceRef isr)
7702 InputSource *is = (InputSource *) isr;
7704 if (is->xid == 0) return;
7705 XtRemoveInput(is->xid);
7710 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7712 static int line = 0;
7713 ChildProc *cp = (ChildProc *) pr;
7718 if (appData.noJoin || !appData.useInternalWrap)
7719 outCount = fwrite(message, 1, count, stdout);
7722 int width = get_term_width();
7723 int len = wrap(NULL, message, count, width, &line);
7724 char *msg = malloc(len);
7728 outCount = fwrite(message, 1, count, stdout);
7731 dbgchk = wrap(msg, message, count, width, &line);
7732 if (dbgchk != len && appData.debugMode)
7733 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7734 outCount = fwrite(msg, 1, dbgchk, stdout);
7740 outCount = write(cp->fdTo, message, count);
7750 /* Output message to process, with "ms" milliseconds of delay
7751 between each character. This is needed when sending the logon
7752 script to ICC, which for some reason doesn't like the
7753 instantaneous send. */
7755 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7757 ChildProc *cp = (ChildProc *) pr;
7762 r = write(cp->fdTo, message++, 1);
7775 /**** Animation code by Hugh Fisher, DCS, ANU.
7777 Known problem: if a window overlapping the board is
7778 moved away while a piece is being animated underneath,
7779 the newly exposed area won't be updated properly.
7780 I can live with this.
7782 Known problem: if you look carefully at the animation
7783 of pieces in mono mode, they are being drawn as solid
7784 shapes without interior detail while moving. Fixing
7785 this would be a major complication for minimal return.
7788 /* Masks for XPM pieces. Black and white pieces can have
7789 different shapes, but in the interest of retaining my
7790 sanity pieces must have the same outline on both light
7791 and dark squares, and all pieces must use the same
7792 background square colors/images. */
7794 static int xpmDone = 0;
7797 CreateAnimMasks (int pieceDepth)
7803 unsigned long plane;
7806 /* Need a bitmap just to get a GC with right depth */
7807 buf = XCreatePixmap(xDisplay, xBoardWindow,
7809 values.foreground = 1;
7810 values.background = 0;
7811 /* Don't use XtGetGC, not read only */
7812 maskGC = XCreateGC(xDisplay, buf,
7813 GCForeground | GCBackground, &values);
7814 XFreePixmap(xDisplay, buf);
7816 buf = XCreatePixmap(xDisplay, xBoardWindow,
7817 squareSize, squareSize, pieceDepth);
7818 values.foreground = XBlackPixel(xDisplay, xScreen);
7819 values.background = XWhitePixel(xDisplay, xScreen);
7820 bufGC = XCreateGC(xDisplay, buf,
7821 GCForeground | GCBackground, &values);
7823 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7824 /* Begin with empty mask */
7825 if(!xpmDone) // [HGM] pieces: keep using existing
7826 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7827 squareSize, squareSize, 1);
7828 XSetFunction(xDisplay, maskGC, GXclear);
7829 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7830 0, 0, squareSize, squareSize);
7832 /* Take a copy of the piece */
7837 XSetFunction(xDisplay, bufGC, GXcopy);
7838 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7840 0, 0, squareSize, squareSize, 0, 0);
7842 /* XOR the background (light) over the piece */
7843 XSetFunction(xDisplay, bufGC, GXxor);
7845 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7846 0, 0, squareSize, squareSize, 0, 0);
7848 XSetForeground(xDisplay, bufGC, lightSquareColor);
7849 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7852 /* We now have an inverted piece image with the background
7853 erased. Construct mask by just selecting all the non-zero
7854 pixels - no need to reconstruct the original image. */
7855 XSetFunction(xDisplay, maskGC, GXor);
7857 /* Might be quicker to download an XImage and create bitmap
7858 data from it rather than this N copies per piece, but it
7859 only takes a fraction of a second and there is a much
7860 longer delay for loading the pieces. */
7861 for (n = 0; n < pieceDepth; n ++) {
7862 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7863 0, 0, squareSize, squareSize,
7869 XFreePixmap(xDisplay, buf);
7870 XFreeGC(xDisplay, bufGC);
7871 XFreeGC(xDisplay, maskGC);
7875 InitAnimState (AnimState *anim, XWindowAttributes *info)
7880 /* Each buffer is square size, same depth as window */
7881 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7882 squareSize, squareSize, info->depth);
7883 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7884 squareSize, squareSize, info->depth);
7886 /* Create a plain GC for blitting */
7887 mask = GCForeground | GCBackground | GCFunction |
7888 GCPlaneMask | GCGraphicsExposures;
7889 values.foreground = XBlackPixel(xDisplay, xScreen);
7890 values.background = XWhitePixel(xDisplay, xScreen);
7891 values.function = GXcopy;
7892 values.plane_mask = AllPlanes;
7893 values.graphics_exposures = False;
7894 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7896 /* Piece will be copied from an existing context at
7897 the start of each new animation/drag. */
7898 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7900 /* Outline will be a read-only copy of an existing */
7901 anim->outlineGC = None;
7907 XWindowAttributes info;
7909 if (xpmDone && gameInfo.variant == oldVariant) return;
7910 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7911 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7913 InitAnimState(&game, &info);
7914 InitAnimState(&player, &info);
7916 /* For XPM pieces, we need bitmaps to use as masks. */
7918 CreateAnimMasks(info.depth), xpmDone = 1;
7923 static Boolean frameWaiting;
7926 FrameAlarm (int sig)
7928 frameWaiting = False;
7929 /* In case System-V style signals. Needed?? */
7930 signal(SIGALRM, FrameAlarm);
7934 FrameDelay (int time)
7936 struct itimerval delay;
7938 XSync(xDisplay, False);
7941 frameWaiting = True;
7942 signal(SIGALRM, FrameAlarm);
7943 delay.it_interval.tv_sec =
7944 delay.it_value.tv_sec = time / 1000;
7945 delay.it_interval.tv_usec =
7946 delay.it_value.tv_usec = (time % 1000) * 1000;
7947 setitimer(ITIMER_REAL, &delay, NULL);
7948 while (frameWaiting) pause();
7949 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7950 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7951 setitimer(ITIMER_REAL, &delay, NULL);
7958 FrameDelay (int time)
7960 XSync(xDisplay, False);
7962 usleep(time * 1000);
7973 /* Convert board position to corner of screen rect and color */
7976 ScreenSquare (int column, int row, XPoint *pt, int *color)
7979 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7980 pt->y = lineGap + row * (squareSize + lineGap);
7982 pt->x = lineGap + column * (squareSize + lineGap);
7983 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7985 *color = SquareColor(row, column);
7988 /* Convert window coords to square */
7991 BoardSquare (int x, int y, int *column, int *row)
7993 *column = EventToSquare(x, BOARD_WIDTH);
7994 if (flipView && *column >= 0)
7995 *column = BOARD_WIDTH - 1 - *column;
7996 *row = EventToSquare(y, BOARD_HEIGHT);
7997 if (!flipView && *row >= 0)
7998 *row = BOARD_HEIGHT - 1 - *row;
8003 #undef Max /* just in case */
8005 #define Max(a, b) ((a) > (b) ? (a) : (b))
8006 #define Min(a, b) ((a) < (b) ? (a) : (b))
8009 SetRect (XRectangle *rect, int x, int y, int width, int height)
8013 rect->width = width;
8014 rect->height = height;
8017 /* Test if two frames overlap. If they do, return
8018 intersection rect within old and location of
8019 that rect within new. */
8022 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
8024 if (old->x > new->x + size || new->x > old->x + size ||
8025 old->y > new->y + size || new->y > old->y + size) {
8028 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8029 size - abs(old->x - new->x), size - abs(old->y - new->y));
8030 pt->x = Max(old->x - new->x, 0);
8031 pt->y = Max(old->y - new->y, 0);
8036 /* For two overlapping frames, return the rect(s)
8037 in the old that do not intersect with the new. */
8040 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
8044 /* If old = new (shouldn't happen) then nothing to draw */
8045 if (old->x == new->x && old->y == new->y) {
8049 /* Work out what bits overlap. Since we know the rects
8050 are the same size we don't need a full intersect calc. */
8052 /* Top or bottom edge? */
8053 if (new->y > old->y) {
8054 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8056 } else if (old->y > new->y) {
8057 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8058 size, old->y - new->y);
8061 /* Left or right edge - don't overlap any update calculated above. */
8062 if (new->x > old->x) {
8063 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8064 new->x - old->x, size - abs(new->y - old->y));
8066 } else if (old->x > new->x) {
8067 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8068 old->x - new->x, size - abs(new->y - old->y));
8075 /* Generate a series of frame coords from start->mid->finish.
8076 The movement rate doubles until the half way point is
8077 reached, then halves back down to the final destination,
8078 which gives a nice slow in/out effect. The algorithmn
8079 may seem to generate too many intermediates for short
8080 moves, but remember that the purpose is to attract the
8081 viewers attention to the piece about to be moved and
8082 then to where it ends up. Too few frames would be less
8086 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8088 int fraction, n, count;
8092 /* Slow in, stepping 1/16th, then 1/8th, ... */
8094 for (n = 0; n < factor; n++)
8096 for (n = 0; n < factor; n++) {
8097 frames[count].x = start->x + (mid->x - start->x) / fraction;
8098 frames[count].y = start->y + (mid->y - start->y) / fraction;
8100 fraction = fraction / 2;
8104 frames[count] = *mid;
8107 /* Slow out, stepping 1/2, then 1/4, ... */
8109 for (n = 0; n < factor; n++) {
8110 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8111 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8113 fraction = fraction * 2;
8118 /* Draw a piece on the screen without disturbing what's there */
8121 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8125 /* Bitmap for piece being moved. */
8126 if (appData.monoMode) {
8127 *mask = *pieceToSolid(piece);
8128 } else if (useImages) {
8130 *mask = xpmMask[piece];
8132 *mask = ximMaskPm[piece];
8135 *mask = *pieceToSolid(piece);
8138 /* GC for piece being moved. Square color doesn't matter, but
8139 since it gets modified we make a copy of the original. */
8141 if (appData.monoMode)
8146 if (appData.monoMode)
8151 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8153 /* Outline only used in mono mode and is not modified */
8155 *outline = bwPieceGC;
8157 *outline = wbPieceGC;
8161 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8166 /* Draw solid rectangle which will be clipped to shape of piece */
8167 XFillRectangle(xDisplay, dest, clip,
8168 0, 0, squareSize, squareSize);
8169 if (appData.monoMode)
8170 /* Also draw outline in contrasting color for black
8171 on black / white on white cases */
8172 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8173 0, 0, squareSize, squareSize, 0, 0, 1);
8175 /* Copy the piece */
8180 if(appData.upsideDown && flipView) kind ^= 2;
8181 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8183 0, 0, squareSize, squareSize,
8188 /* Animate the movement of a single piece */
8191 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8195 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8196 /* The old buffer is initialised with the start square (empty) */
8197 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8198 anim->prevFrame = *start;
8200 /* The piece will be drawn using its own bitmap as a matte */
8201 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8202 XSetClipMask(xDisplay, anim->pieceGC, mask);
8206 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8208 XRectangle updates[4];
8213 /* Save what we are about to draw into the new buffer */
8214 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8215 frame->x, frame->y, squareSize, squareSize,
8218 /* Erase bits of the previous frame */
8219 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8220 /* Where the new frame overlapped the previous,
8221 the contents in newBuf are wrong. */
8222 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8223 overlap.x, overlap.y,
8224 overlap.width, overlap.height,
8226 /* Repaint the areas in the old that don't overlap new */
8227 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8228 for (i = 0; i < count; i++)
8229 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8230 updates[i].x - anim->prevFrame.x,
8231 updates[i].y - anim->prevFrame.y,
8232 updates[i].width, updates[i].height,
8233 updates[i].x, updates[i].y);
8235 /* Easy when no overlap */
8236 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8237 0, 0, squareSize, squareSize,
8238 anim->prevFrame.x, anim->prevFrame.y);
8241 /* Save this frame for next time round */
8242 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8243 0, 0, squareSize, squareSize,
8245 anim->prevFrame = *frame;
8247 /* Draw piece over original screen contents, not current,
8248 and copy entire rect. Wipes out overlapping piece images. */
8249 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8250 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8251 0, 0, squareSize, squareSize,
8252 frame->x, frame->y);
8256 EndAnimation (AnimState *anim, XPoint *finish)
8258 XRectangle updates[4];
8263 /* The main code will redraw the final square, so we
8264 only need to erase the bits that don't overlap. */
8265 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8266 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8267 for (i = 0; i < count; i++)
8268 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8269 updates[i].x - anim->prevFrame.x,
8270 updates[i].y - anim->prevFrame.y,
8271 updates[i].width, updates[i].height,
8272 updates[i].x, updates[i].y);
8274 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8275 0, 0, squareSize, squareSize,
8276 anim->prevFrame.x, anim->prevFrame.y);
8281 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8285 BeginAnimation(anim, piece, startColor, start);
8286 for (n = 0; n < nFrames; n++) {
8287 AnimationFrame(anim, &(frames[n]), piece);
8288 FrameDelay(appData.animSpeed);
8290 EndAnimation(anim, finish);
8294 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8297 ChessSquare piece = board[fromY][toY];
8298 board[fromY][toY] = EmptySquare;
8299 DrawPosition(FALSE, board);
8301 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8302 y = lineGap + toY * (squareSize + lineGap);
8304 x = lineGap + toX * (squareSize + lineGap);
8305 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8307 for(i=1; i<4*kFactor; i++) {
8308 int r = squareSize * 9 * i/(20*kFactor - 5);
8309 XFillArc(xDisplay, xBoardWindow, highlineGC,
8310 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8311 FrameDelay(appData.animSpeed);
8313 board[fromY][toY] = piece;
8316 /* Main control logic for deciding what to animate and how */
8319 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8323 XPoint start, finish, mid;
8324 XPoint frames[kFactor * 2 + 1];
8325 int nFrames, startColor, endColor;
8327 /* Are we animating? */
8328 if (!appData.animate || appData.blindfold)
8331 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8332 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8333 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8335 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8336 piece = board[fromY][fromX];
8337 if (piece >= EmptySquare) return;
8342 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8345 ScreenSquare(fromX, fromY, &start, &startColor);
8346 ScreenSquare(toX, toY, &finish, &endColor);
8349 /* Knight: make straight movement then diagonal */
8350 if (abs(toY - fromY) < abs(toX - fromX)) {
8351 mid.x = start.x + (finish.x - start.x) / 2;
8355 mid.y = start.y + (finish.y - start.y) / 2;
8358 mid.x = start.x + (finish.x - start.x) / 2;
8359 mid.y = start.y + (finish.y - start.y) / 2;
8362 /* Don't use as many frames for very short moves */
8363 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8364 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8366 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8367 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8368 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8370 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8371 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8374 /* Be sure end square is redrawn */
8375 damage[0][toY][toX] = True;
8379 DragPieceBegin (int x, int y, Boolean instantly)
8381 int boardX, boardY, color;
8384 /* Are we animating? */
8385 if (!appData.animateDragging || appData.blindfold)
8388 /* Figure out which square we start in and the
8389 mouse position relative to top left corner. */
8390 BoardSquare(x, y, &boardX, &boardY);
8391 player.startBoardX = boardX;
8392 player.startBoardY = boardY;
8393 ScreenSquare(boardX, boardY, &corner, &color);
8394 player.startSquare = corner;
8395 player.startColor = color;
8396 /* As soon as we start dragging, the piece will jump slightly to
8397 be centered over the mouse pointer. */
8398 player.mouseDelta.x = squareSize/2;
8399 player.mouseDelta.y = squareSize/2;
8400 /* Initialise animation */
8401 player.dragPiece = PieceForSquare(boardX, boardY);
8403 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8404 player.dragActive = True;
8405 BeginAnimation(&player, player.dragPiece, color, &corner);
8406 /* Mark this square as needing to be redrawn. Note that
8407 we don't remove the piece though, since logically (ie
8408 as seen by opponent) the move hasn't been made yet. */
8409 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8410 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8411 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8412 corner.x, corner.y, squareSize, squareSize,
8413 0, 0); // [HGM] zh: unstack in stead of grab
8414 if(gatingPiece != EmptySquare) {
8415 /* Kludge alert: When gating we want the introduced
8416 piece to appear on the from square. To generate an
8417 image of it, we draw it on the board, copy the image,
8418 and draw the original piece again. */
8419 ChessSquare piece = boards[currentMove][boardY][boardX];
8420 DrawSquare(boardY, boardX, gatingPiece, 0);
8421 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8422 corner.x, corner.y, squareSize, squareSize, 0, 0);
8423 DrawSquare(boardY, boardX, piece, 0);
8425 damage[0][boardY][boardX] = True;
8427 player.dragActive = False;
8432 ChangeDragPiece (ChessSquare piece)
8435 player.dragPiece = piece;
8436 /* The piece will be drawn using its own bitmap as a matte */
8437 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8438 XSetClipMask(xDisplay, player.pieceGC, mask);
8442 DragPieceMove (int x, int y)
8446 /* Are we animating? */
8447 if (!appData.animateDragging || appData.blindfold)
8451 if (! player.dragActive)
8453 /* Move piece, maintaining same relative position
8454 of mouse within square */
8455 corner.x = x - player.mouseDelta.x;
8456 corner.y = y - player.mouseDelta.y;
8457 AnimationFrame(&player, &corner, player.dragPiece);
8459 if (appData.highlightDragging) {
8461 BoardSquare(x, y, &boardX, &boardY);
8462 SetHighlights(fromX, fromY, boardX, boardY);
8468 DragPieceEnd (int x, int y)
8470 int boardX, boardY, color;
8473 /* Are we animating? */
8474 if (!appData.animateDragging || appData.blindfold)
8478 if (! player.dragActive)
8480 /* Last frame in sequence is square piece is
8481 placed on, which may not match mouse exactly. */
8482 BoardSquare(x, y, &boardX, &boardY);
8483 ScreenSquare(boardX, boardY, &corner, &color);
8484 EndAnimation(&player, &corner);
8486 /* Be sure end square is redrawn */
8487 damage[0][boardY][boardX] = True;
8489 /* This prevents weird things happening with fast successive
8490 clicks which on my Sun at least can cause motion events
8491 without corresponding press/release. */
8492 player.dragActive = False;
8495 /* Handle expose event while piece being dragged */
8500 if (!player.dragActive || appData.blindfold)
8503 /* What we're doing: logically, the move hasn't been made yet,
8504 so the piece is still in it's original square. But visually
8505 it's being dragged around the board. So we erase the square
8506 that the piece is on and draw it at the last known drag point. */
8507 BlankSquare(player.startSquare.x, player.startSquare.y,
8508 player.startColor, EmptySquare, xBoardWindow, 1);
8509 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8510 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8513 #include <sys/ioctl.h>
8517 int fd, default_width;
8520 default_width = 79; // this is FICS default anyway...
8522 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8524 if (!ioctl(fd, TIOCGSIZE, &win))
8525 default_width = win.ts_cols;
8526 #elif defined(TIOCGWINSZ)
8528 if (!ioctl(fd, TIOCGWINSZ, &win))
8529 default_width = win.ws_col;
8531 return default_width;
8537 static int old_width = 0;
8538 int new_width = get_term_width();
8540 if (old_width != new_width)
8541 ics_printf("set width %d\n", new_width);
8542 old_width = new_width;
8546 NotifyFrontendLogin ()
8551 /* [AS] Arrow highlighting support */
8553 static double A_WIDTH = 5; /* Width of arrow body */
8555 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8556 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8567 return (int) (x + 0.5);
8571 SquareToPos (int rank, int file, int *x, int *y)
8574 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8575 *y = lineGap + rank * (squareSize + lineGap);
8577 *x = lineGap + file * (squareSize + lineGap);
8578 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8582 /* Draw an arrow between two points using current settings */
8584 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8587 double dx, dy, j, k, x, y;
8590 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8592 arrow[0].x = s_x + A_WIDTH + 0.5;
8595 arrow[1].x = s_x + A_WIDTH + 0.5;
8596 arrow[1].y = d_y - h;
8598 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8599 arrow[2].y = d_y - h;
8604 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8605 arrow[5].y = d_y - h;
8607 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8608 arrow[4].y = d_y - h;
8610 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8613 else if( d_y == s_y ) {
8614 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8617 arrow[0].y = s_y + A_WIDTH + 0.5;
8619 arrow[1].x = d_x - w;
8620 arrow[1].y = s_y + A_WIDTH + 0.5;
8622 arrow[2].x = d_x - w;
8623 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8628 arrow[5].x = d_x - w;
8629 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8631 arrow[4].x = d_x - w;
8632 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8635 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8638 /* [AS] Needed a lot of paper for this! :-) */
8639 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8640 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8642 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8644 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8649 arrow[0].x = Round(x - j);
8650 arrow[0].y = Round(y + j*dx);
8652 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8653 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8656 x = (double) d_x - k;
8657 y = (double) d_y - k*dy;
8660 x = (double) d_x + k;
8661 y = (double) d_y + k*dy;
8664 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8666 arrow[6].x = Round(x - j);
8667 arrow[6].y = Round(y + j*dx);
8669 arrow[2].x = Round(arrow[6].x + 2*j);
8670 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8672 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8673 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8678 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8679 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8682 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8683 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8684 // Polygon( hdc, arrow, 7 );
8688 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8691 hor = 64*s_col + 32; vert = 64*s_row + 32;
8692 for(i=0; i<= 64; i++) {
8693 damage[0][vert+6>>6][hor+6>>6] = True;
8694 damage[0][vert-6>>6][hor+6>>6] = True;
8695 damage[0][vert+6>>6][hor-6>>6] = True;
8696 damage[0][vert-6>>6][hor-6>>6] = True;
8697 hor += d_col - s_col; vert += d_row - s_row;
8701 /* [AS] Draw an arrow between two squares */
8703 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8705 int s_x, s_y, d_x, d_y;
8707 if( s_col == d_col && s_row == d_row ) {
8711 /* Get source and destination points */
8712 SquareToPos( s_row, s_col, &s_x, &s_y);
8713 SquareToPos( d_row, d_col, &d_x, &d_y);
8716 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8718 else if( d_y < s_y ) {
8719 d_y += squareSize / 2 + squareSize / 4;
8722 d_y += squareSize / 2;
8726 d_x += squareSize / 2 - squareSize / 4;
8728 else if( d_x < s_x ) {
8729 d_x += squareSize / 2 + squareSize / 4;
8732 d_x += squareSize / 2;
8735 s_x += squareSize / 2;
8736 s_y += squareSize / 2;
8739 A_WIDTH = squareSize / 14.; //[HGM] make float
8741 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8742 ArrowDamage(s_col, s_row, d_col, d_row);
8746 IsDrawArrowEnabled ()
8748 return appData.highlightMoveWithArrow && squareSize >= 32;
8752 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8754 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8755 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8759 UpdateLogos (int displ)
8761 return; // no logos in XBoard yet