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 DelayedDrag P((void));
277 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
278 void HandleUserMove P((Widget w, XEvent *event,
279 String *prms, Cardinal *nprms));
280 void AnimateUserMove P((Widget w, XEvent * event,
281 String * params, Cardinal * nParams));
282 void HandlePV P((Widget w, XEvent * event,
283 String * params, Cardinal * nParams));
284 void SelectPV P((Widget w, XEvent * event,
285 String * params, Cardinal * nParams));
286 void StopPV P((Widget w, XEvent * event,
287 String * params, Cardinal * nParams));
288 void WhiteClock P((Widget w, XEvent *event,
289 String *prms, Cardinal *nprms));
290 void BlackClock P((Widget w, XEvent *event,
291 String *prms, Cardinal *nprms));
292 void DrawPositionProc P((Widget w, XEvent *event,
293 String *prms, Cardinal *nprms));
294 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
296 void CommentClick P((Widget w, XEvent * event,
297 String * params, Cardinal * nParams));
298 void CommentPopUp P((char *title, char *label));
299 void CommentPopDown P((void));
300 void ICSInputBoxPopUp P((void));
301 void ICSInputBoxPopDown P((void));
302 void FileNamePopUp P((char *label, char *def, char *filter,
303 FileProc proc, char *openMode));
304 void FileNamePopDown P((void));
305 void FileNameCallback P((Widget w, XtPointer client_data,
306 XtPointer call_data));
307 void FileNameAction P((Widget w, XEvent *event,
308 String *prms, Cardinal *nprms));
309 void AskQuestionReplyAction P((Widget w, XEvent *event,
310 String *prms, Cardinal *nprms));
311 void AskQuestionProc P((Widget w, XEvent *event,
312 String *prms, Cardinal *nprms));
313 void AskQuestionPopDown P((void));
314 void PromotionPopDown P((void));
315 void PromotionCallback P((Widget w, XtPointer client_data,
316 XtPointer call_data));
317 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
318 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
320 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
322 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
324 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
326 void LoadPositionProc P((Widget w, XEvent *event,
327 String *prms, Cardinal *nprms));
328 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
330 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
332 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
334 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
336 void PastePositionProc P((Widget w, XEvent *event, String *prms,
338 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
340 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
341 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void SavePositionProc P((Widget w, XEvent *event,
343 String *prms, Cardinal *nprms));
344 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
347 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
349 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
351 void MachineWhiteProc P((Widget w, XEvent *event,
352 String *prms, Cardinal *nprms));
353 void AnalyzeModeProc P((Widget w, XEvent *event,
354 String *prms, Cardinal *nprms));
355 void AnalyzeFileProc P((Widget w, XEvent *event,
356 String *prms, Cardinal *nprms));
357 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
359 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void IcsClientProc P((Widget w, XEvent *event, String *prms,
363 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void EditPositionProc P((Widget w, XEvent *event,
365 String *prms, Cardinal *nprms));
366 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void EditCommentProc P((Widget w, XEvent *event,
368 String *prms, Cardinal *nprms));
369 void IcsInputBoxProc P((Widget w, XEvent *event,
370 String *prms, Cardinal *nprms));
371 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
386 void StopObservingProc P((Widget w, XEvent *event, String *prms,
388 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
390 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
395 Boolean TempBackwardActive = False;
396 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
400 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
402 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
404 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
405 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
407 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
409 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
411 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
412 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
413 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
414 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
416 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
417 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
419 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
421 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
423 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
424 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
425 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
426 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
428 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
430 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
432 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
434 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
437 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
439 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
441 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
443 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
453 void DisplayMove P((int moveNumber));
454 void DisplayTitle P((char *title));
455 void ICSInitScript P((void));
456 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
457 void ErrorPopUp P((char *title, char *text, int modal));
458 void ErrorPopDown P((void));
459 static char *ExpandPathName P((char *path));
460 static void CreateAnimVars P((void));
461 static void DragPieceMove P((int x, int y));
462 static void DrawDragPiece P((void));
463 char *ModeToWidgetName P((GameMode mode));
464 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
477 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
478 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
479 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
480 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
481 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
482 void GameListOptionsPopDown P(());
483 void GenericPopDown P(());
484 void update_ics_width P(());
485 int get_term_width P(());
486 int CopyMemoProc P(());
487 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
488 Boolean IsDrawArrowEnabled P(());
491 * XBoard depends on Xt R4 or higher
493 int xtVersion = XtSpecificationRelease;
498 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
499 jailSquareColor, highlightSquareColor, premoveHighlightColor;
500 Pixel lowTimeWarningColor;
501 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
502 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
503 wjPieceGC, bjPieceGC, prelineGC, countGC;
504 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
505 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
506 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
507 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
508 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
509 ICSInputShell, fileNameShell, askQuestionShell;
510 Widget historyShell, evalGraphShell, gameListShell;
511 int hOffset; // [HGM] dual
512 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
513 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
514 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
516 XFontSet fontSet, clockFontSet;
519 XFontStruct *clockFontStruct;
521 Font coordFontID, countFontID;
522 XFontStruct *coordFontStruct, *countFontStruct;
523 XtAppContext appContext;
525 char *oldICSInteractionTitle;
529 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
531 Position commentX = -1, commentY = -1;
532 Dimension commentW, commentH;
533 typedef unsigned int BoardSize;
535 Boolean chessProgram;
537 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
538 int squareSize, smallLayout = 0, tinyLayout = 0,
539 marginW, marginH, // [HGM] for run-time resizing
540 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
541 ICSInputBoxUp = False, askQuestionUp = False,
542 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
543 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
544 Dimension textHeight;
545 Pixel timerForegroundPixel, timerBackgroundPixel;
546 Pixel buttonForegroundPixel, buttonBackgroundPixel;
547 char *chessDir, *programName, *programVersion,
548 *gameCopyFilename, *gamePasteFilename;
549 Boolean alwaysOnTop = False;
550 Boolean saveSettingsOnExit;
551 char *settingsFileName;
552 char *icsTextMenuString;
554 char *firstChessProgramNames;
555 char *secondChessProgramNames;
557 WindowPlacement wpMain;
558 WindowPlacement wpConsole;
559 WindowPlacement wpComment;
560 WindowPlacement wpMoveHistory;
561 WindowPlacement wpEvalGraph;
562 WindowPlacement wpEngineOutput;
563 WindowPlacement wpGameList;
564 WindowPlacement wpTags;
566 extern Widget shells[];
567 extern Boolean shellUp[];
571 Pixmap pieceBitmap[2][(int)BlackPawn];
572 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
573 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
574 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
575 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
576 Pixmap xpmBoardBitmap[2];
577 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
578 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
579 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
580 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
581 XImage *ximLightSquare, *ximDarkSquare;
584 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
585 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
587 #define White(piece) ((int)(piece) < (int)BlackPawn)
589 /* Variables for doing smooth animation. This whole thing
590 would be much easier if the board was double-buffered,
591 but that would require a fairly major rewrite. */
596 GC blitGC, pieceGC, outlineGC;
597 XPoint startSquare, prevFrame, mouseDelta;
601 int startBoardX, startBoardY;
604 /* There can be two pieces being animated at once: a player
605 can begin dragging a piece before the remote opponent has moved. */
607 static AnimState game, player;
609 /* Bitmaps for use as masks when drawing XPM pieces.
610 Need one for each black and white piece. */
611 static Pixmap xpmMask[BlackKing + 1];
613 /* This magic number is the number of intermediate frames used
614 in each half of the animation. For short moves it's reduced
615 by 1. The total number of frames will be factor * 2 + 1. */
618 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
620 MenuItem fileMenu[] = {
621 {N_("New Game Ctrl+N"), "New Game", ResetProc},
622 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
623 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
624 {"----", NULL, NothingProc},
625 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
626 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
627 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
628 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
629 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
630 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
631 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
632 {"----", NULL, NothingProc},
633 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
634 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
635 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
636 {"----", NULL, NothingProc},
637 {N_("Mail Move"), "Mail Move", MailMoveProc},
638 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
639 {"----", NULL, NothingProc},
640 {N_("Quit Ctr+Q"), "Exit", QuitProc},
644 MenuItem editMenu[] = {
645 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
646 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
647 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
648 {"----", NULL, NothingProc},
649 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
650 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
651 {"----", NULL, NothingProc},
652 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
653 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
654 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
655 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
656 {N_("Edit Book"), "Edit Book", EditBookProc},
657 {"----", NULL, NothingProc},
658 {N_("Revert Home"), "Revert", RevertProc},
659 {N_("Annotate"), "Annotate", AnnotateProc},
660 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
661 {"----", NULL, NothingProc},
662 {N_("Backward Alt+Left"), "Backward", BackwardProc},
663 {N_("Forward Alt+Right"), "Forward", ForwardProc},
664 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
665 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
669 MenuItem viewMenu[] = {
670 {N_("Flip View F2"), "Flip View", FlipViewProc},
671 {"----", NULL, NothingProc},
672 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
673 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
674 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
675 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
676 {N_("ICS text menu"), "ICStex", IcsTextProc},
677 {"----", NULL, NothingProc},
678 {N_("Tags"), "Show Tags", EditTagsProc},
679 {N_("Comments"), "Show Comments", EditCommentProc},
680 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
681 {"----", NULL, NothingProc},
682 {N_("Board..."), "Board Options", BoardOptionsProc},
683 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
687 MenuItem modeMenu[] = {
688 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
689 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
690 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
691 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
692 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
693 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
694 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
695 {N_("Training"), "Training", TrainingProc},
696 {N_("ICS Client"), "ICS Client", IcsClientProc},
697 {"----", NULL, NothingProc},
698 {N_("Machine Match"), "Machine Match", MatchProc},
699 {N_("Pause Pause"), "Pause", PauseProc},
703 MenuItem actionMenu[] = {
704 {N_("Accept F3"), "Accept", AcceptProc},
705 {N_("Decline F4"), "Decline", DeclineProc},
706 {N_("Rematch F12"), "Rematch", RematchProc},
707 {"----", NULL, NothingProc},
708 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
709 {N_("Draw F6"), "Draw", DrawProc},
710 {N_("Adjourn F7"), "Adjourn", AdjournProc},
711 {N_("Abort F8"),"Abort", AbortProc},
712 {N_("Resign F9"), "Resign", ResignProc},
713 {"----", NULL, NothingProc},
714 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
715 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
716 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
717 {"----", NULL, NothingProc},
718 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
719 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
720 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
724 MenuItem engineMenu[] = {
725 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
726 {"----", NULL, NothingProc},
727 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
728 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
729 {"----", NULL, NothingProc},
730 {N_("Hint"), "Hint", HintProc},
731 {N_("Book"), "Book", BookProc},
732 {"----", NULL, NothingProc},
733 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
734 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
738 MenuItem optionsMenu[] = {
739 #define OPTIONSDIALOG
741 {N_("General ..."), "General", OptionsProc},
743 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
744 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
745 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
746 {N_("ICS ..."), "ICS", IcsOptionsProc},
747 {N_("Match ..."), "Match", MatchOptionsProc},
748 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
749 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
750 // {N_(" ..."), "", OptionsProc},
751 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
752 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
753 {"----", NULL, NothingProc},
754 #ifndef OPTIONSDIALOG
755 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
756 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
757 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
758 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
759 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
760 {N_("Blindfold"), "Blindfold", BlindfoldProc},
761 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
763 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
765 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
766 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
767 {N_("Move Sound"), "Move Sound", MoveSoundProc},
768 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
769 {N_("One-Click Moving"), "OneClick", OneClickProc},
770 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
771 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
772 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
773 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
774 // {N_("Premove"), "Premove", PremoveProc},
775 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
776 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
777 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
778 {"----", NULL, NothingProc},
780 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
781 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
785 MenuItem helpMenu[] = {
786 {N_("Info XBoard"), "Info XBoard", InfoProc},
787 {N_("Man XBoard F1"), "Man XBoard", ManProc},
788 {"----", NULL, NothingProc},
789 {N_("About XBoard"), "About XBoard", AboutProc},
794 {N_("File"), "File", fileMenu},
795 {N_("Edit"), "Edit", editMenu},
796 {N_("View"), "View", viewMenu},
797 {N_("Mode"), "Mode", modeMenu},
798 {N_("Action"), "Action", actionMenu},
799 {N_("Engine"), "Engine", engineMenu},
800 {N_("Options"), "Options", optionsMenu},
801 {N_("Help"), "Help", helpMenu},
805 #define PAUSE_BUTTON "P"
806 MenuItem buttonBar[] = {
807 {"<<", "<<", ToStartProc},
808 {"<", "<", BackwardProc},
809 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
810 {">", ">", ForwardProc},
811 {">>", ">>", ToEndProc},
815 #define PIECE_MENU_SIZE 18
816 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
817 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
818 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
819 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
820 N_("Empty square"), N_("Clear board") },
821 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
822 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
823 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
824 N_("Empty square"), N_("Clear board") }
826 /* must be in same order as pieceMenuStrings! */
827 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
828 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
829 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
830 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
831 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
832 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
833 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
834 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
835 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
838 #define DROP_MENU_SIZE 6
839 String dropMenuStrings[DROP_MENU_SIZE] = {
840 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
842 /* must be in same order as dropMenuStrings! */
843 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
844 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
845 WhiteRook, WhiteQueen
853 DropMenuEnables dmEnables[] = {
871 { XtNborderWidth, 0 },
872 { XtNdefaultDistance, 0 },
876 { XtNborderWidth, 0 },
877 { XtNresizable, (XtArgVal) True },
881 { XtNborderWidth, 0 },
887 { XtNjustify, (XtArgVal) XtJustifyRight },
888 { XtNlabel, (XtArgVal) "..." },
889 { XtNresizable, (XtArgVal) True },
890 { XtNresize, (XtArgVal) False }
893 Arg messageArgs[] = {
894 { XtNjustify, (XtArgVal) XtJustifyLeft },
895 { XtNlabel, (XtArgVal) "..." },
896 { XtNresizable, (XtArgVal) True },
897 { XtNresize, (XtArgVal) False }
901 { XtNborderWidth, 0 },
902 { XtNjustify, (XtArgVal) XtJustifyLeft }
905 XtResource clientResources[] = {
906 { "flashCount", "flashCount", XtRInt, sizeof(int),
907 XtOffset(AppDataPtr, flashCount), XtRImmediate,
908 (XtPointer) FLASH_COUNT },
911 XrmOptionDescRec shellOptions[] = {
912 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
913 { "-flash", "flashCount", XrmoptionNoArg, "3" },
914 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
917 XtActionsRec boardActions[] = {
918 { "DrawPosition", DrawPositionProc },
919 { "HandleUserMove", HandleUserMove },
920 { "AnimateUserMove", AnimateUserMove },
921 { "HandlePV", HandlePV },
922 { "SelectPV", SelectPV },
923 { "StopPV", StopPV },
924 { "FileNameAction", FileNameAction },
925 { "AskQuestionProc", AskQuestionProc },
926 { "AskQuestionReplyAction", AskQuestionReplyAction },
927 { "PieceMenuPopup", PieceMenuPopup },
928 { "WhiteClock", WhiteClock },
929 { "BlackClock", BlackClock },
930 { "ResetProc", ResetProc },
931 { "NewVariantProc", NewVariantProc },
932 { "LoadGameProc", LoadGameProc },
933 { "LoadNextGameProc", LoadNextGameProc },
934 { "LoadPrevGameProc", LoadPrevGameProc },
935 { "LoadSelectedProc", LoadSelectedProc },
936 { "SetFilterProc", SetFilterProc },
937 { "ReloadGameProc", ReloadGameProc },
938 { "LoadPositionProc", LoadPositionProc },
939 { "LoadNextPositionProc", LoadNextPositionProc },
940 { "LoadPrevPositionProc", LoadPrevPositionProc },
941 { "ReloadPositionProc", ReloadPositionProc },
942 { "CopyPositionProc", CopyPositionProc },
943 { "PastePositionProc", PastePositionProc },
944 { "CopyGameProc", CopyGameProc },
945 { "CopyGameListProc", CopyGameListProc },
946 { "PasteGameProc", PasteGameProc },
947 { "SaveGameProc", SaveGameProc },
948 { "SavePositionProc", SavePositionProc },
949 { "MailMoveProc", MailMoveProc },
950 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
951 { "QuitProc", QuitProc },
952 { "MachineWhiteProc", MachineWhiteProc },
953 { "MachineBlackProc", MachineBlackProc },
954 { "AnalysisModeProc", AnalyzeModeProc },
955 { "AnalyzeFileProc", AnalyzeFileProc },
956 { "TwoMachinesProc", TwoMachinesProc },
957 { "IcsClientProc", IcsClientProc },
958 { "EditGameProc", EditGameProc },
959 { "EditPositionProc", EditPositionProc },
960 { "TrainingProc", EditPositionProc },
961 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
962 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
963 { "ShowGameListProc", ShowGameListProc },
964 { "ShowMoveListProc", HistoryShowProc},
965 { "EditTagsProc", EditTagsProc },
966 { "EditBookProc", EditBookProc },
967 { "EditCommentProc", EditCommentProc },
968 { "IcsInputBoxProc", IcsInputBoxProc },
969 { "PauseProc", PauseProc },
970 { "AcceptProc", AcceptProc },
971 { "DeclineProc", DeclineProc },
972 { "RematchProc", RematchProc },
973 { "CallFlagProc", CallFlagProc },
974 { "DrawProc", DrawProc },
975 { "AdjournProc", AdjournProc },
976 { "AbortProc", AbortProc },
977 { "ResignProc", ResignProc },
978 { "AdjuWhiteProc", AdjuWhiteProc },
979 { "AdjuBlackProc", AdjuBlackProc },
980 { "AdjuDrawProc", AdjuDrawProc },
981 { "TypeInProc", TypeInProc },
982 { "EnterKeyProc", EnterKeyProc },
983 { "UpKeyProc", UpKeyProc },
984 { "DownKeyProc", DownKeyProc },
985 { "StopObservingProc", StopObservingProc },
986 { "StopExaminingProc", StopExaminingProc },
987 { "UploadProc", UploadProc },
988 { "BackwardProc", BackwardProc },
989 { "ForwardProc", ForwardProc },
990 { "TempBackwardProc", TempBackwardProc },
991 { "TempForwardProc", TempForwardProc },
992 { "ToStartProc", ToStartProc },
993 { "ToEndProc", ToEndProc },
994 { "RevertProc", RevertProc },
995 { "AnnotateProc", AnnotateProc },
996 { "TruncateGameProc", TruncateGameProc },
997 { "MoveNowProc", MoveNowProc },
998 { "RetractMoveProc", RetractMoveProc },
999 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
1000 { "UciMenuProc", (XtActionProc) UciMenuProc },
1001 { "TimeControlProc", (XtActionProc) TimeControlProc },
1002 { "FlipViewProc", FlipViewProc },
1003 { "PonderNextMoveProc", PonderNextMoveProc },
1004 #ifndef OPTIONSDIALOG
1005 { "AlwaysQueenProc", AlwaysQueenProc },
1006 { "AnimateDraggingProc", AnimateDraggingProc },
1007 { "AnimateMovingProc", AnimateMovingProc },
1008 { "AutoflagProc", AutoflagProc },
1009 { "AutoflipProc", AutoflipProc },
1010 { "BlindfoldProc", BlindfoldProc },
1011 { "FlashMovesProc", FlashMovesProc },
1013 { "HighlightDraggingProc", HighlightDraggingProc },
1015 { "HighlightLastMoveProc", HighlightLastMoveProc },
1016 // { "IcsAlarmProc", IcsAlarmProc },
1017 { "MoveSoundProc", MoveSoundProc },
1018 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1019 { "PopupExitMessageProc", PopupExitMessageProc },
1020 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1021 // { "PremoveProc", PremoveProc },
1022 { "ShowCoordsProc", ShowCoordsProc },
1023 { "ShowThinkingProc", ShowThinkingProc },
1024 { "HideThinkingProc", HideThinkingProc },
1025 { "TestLegalityProc", TestLegalityProc },
1027 { "SaveSettingsProc", SaveSettingsProc },
1028 { "SaveOnExitProc", SaveOnExitProc },
1029 { "InfoProc", InfoProc },
1030 { "ManProc", ManProc },
1031 { "HintProc", HintProc },
1032 { "BookProc", BookProc },
1033 { "AboutGameProc", AboutGameProc },
1034 { "AboutProc", AboutProc },
1035 { "DebugProc", DebugProc },
1036 { "NothingProc", NothingProc },
1037 { "CommentClick", (XtActionProc) CommentClick },
1038 { "CommentPopDown", (XtActionProc) CommentPopDown },
1039 { "TagsPopDown", (XtActionProc) TagsPopDown },
1040 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1041 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1042 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1043 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1044 { "GameListPopDown", (XtActionProc) GameListPopDown },
1045 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1046 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1047 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1048 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1049 { "GenericPopDown", (XtActionProc) GenericPopDown },
1050 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1051 { "SelectMove", (XtActionProc) SelectMove },
1054 char globalTranslations[] =
1055 ":<Key>F9: ResignProc() \n \
1056 :Ctrl<Key>n: ResetProc() \n \
1057 :Meta<Key>V: NewVariantProc() \n \
1058 :Ctrl<Key>o: LoadGameProc() \n \
1059 :Meta<Key>Next: LoadNextGameProc() \n \
1060 :Meta<Key>Prior: LoadPrevGameProc() \n \
1061 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1062 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1063 :Ctrl<Key>s: SaveGameProc() \n \
1064 :Ctrl<Key>c: CopyGameProc() \n \
1065 :Ctrl<Key>v: PasteGameProc() \n \
1066 :Ctrl<Key>O: LoadPositionProc() \n \
1067 :Shift<Key>Next: LoadNextPositionProc() \n \
1068 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1069 :Ctrl<Key>S: SavePositionProc() \n \
1070 :Ctrl<Key>C: CopyPositionProc() \n \
1071 :Ctrl<Key>V: PastePositionProc() \n \
1072 :Ctrl<Key>q: QuitProc() \n \
1073 :Ctrl<Key>w: MachineWhiteProc() \n \
1074 :Ctrl<Key>b: MachineBlackProc() \n \
1075 :Ctrl<Key>t: TwoMachinesProc() \n \
1076 :Ctrl<Key>a: AnalysisModeProc() \n \
1077 :Ctrl<Key>g: AnalyzeFileProc() \n \
1078 :Ctrl<Key>e: EditGameProc() \n \
1079 :Ctrl<Key>E: EditPositionProc() \n \
1080 :Meta<Key>O: EngineOutputProc() \n \
1081 :Meta<Key>E: EvalGraphProc() \n \
1082 :Meta<Key>G: ShowGameListProc() \n \
1083 :Meta<Key>H: ShowMoveListProc() \n \
1084 :<Key>Pause: PauseProc() \n \
1085 :<Key>F3: AcceptProc() \n \
1086 :<Key>F4: DeclineProc() \n \
1087 :<Key>F12: RematchProc() \n \
1088 :<Key>F5: CallFlagProc() \n \
1089 :<Key>F6: DrawProc() \n \
1090 :<Key>F7: AdjournProc() \n \
1091 :<Key>F8: AbortProc() \n \
1092 :<Key>F10: StopObservingProc() \n \
1093 :<Key>F11: StopExaminingProc() \n \
1094 :Meta Ctrl<Key>F12: DebugProc() \n \
1095 :Meta<Key>End: ToEndProc() \n \
1096 :Meta<Key>Right: ForwardProc() \n \
1097 :Meta<Key>Home: ToStartProc() \n \
1098 :Meta<Key>Left: BackwardProc() \n \
1099 :<Key>Left: BackwardProc() \n \
1100 :<Key>Right: ForwardProc() \n \
1101 :<Key>Home: RevertProc() \n \
1102 :<Key>End: TruncateGameProc() \n \
1103 :Ctrl<Key>m: MoveNowProc() \n \
1104 :Ctrl<Key>x: RetractMoveProc() \n \
1105 :Meta<Key>J: EngineMenuProc() \n \
1106 :Meta<Key>U: UciMenuProc() \n \
1107 :Meta<Key>T: TimeControlProc() \n \
1108 :Ctrl<Key>P: PonderNextMoveProc() \n "
1109 #ifndef OPTIONSDIALOG
1111 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1112 :Ctrl<Key>F: AutoflagProc() \n \
1113 :Ctrl<Key>A: AnimateMovingProc() \n \
1114 :Ctrl<Key>L: TestLegalityProc() \n \
1115 :Ctrl<Key>H: HideThinkingProc() \n "
1118 :<Key>F1: ManProc() \n \
1119 :<Key>F2: FlipViewProc() \n \
1120 :<KeyDown>Return: TempBackwardProc() \n \
1121 :<KeyUp>Return: TempForwardProc() \n";
1123 char boardTranslations[] =
1124 "<Btn1Down>: HandleUserMove(0) \n \
1125 Shift<Btn1Up>: HandleUserMove(1) \n \
1126 <Btn1Up>: HandleUserMove(0) \n \
1127 <Btn1Motion>: AnimateUserMove() \n \
1128 <Btn3Motion>: HandlePV() \n \
1129 <Btn2Motion>: HandlePV() \n \
1130 <Btn3Up>: PieceMenuPopup(menuB) \n \
1131 <Btn2Up>: PieceMenuPopup(menuB) \n \
1132 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1133 PieceMenuPopup(menuB) \n \
1134 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1135 PieceMenuPopup(menuW) \n \
1136 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1137 PieceMenuPopup(menuW) \n \
1138 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1139 PieceMenuPopup(menuB) \n";
1141 char whiteTranslations[] =
1142 "Shift<BtnDown>: WhiteClock(1)\n \
1143 <BtnDown>: WhiteClock(0)\n";
1144 char blackTranslations[] =
1145 "Shift<BtnDown>: BlackClock(1)\n \
1146 <BtnDown>: BlackClock(0)\n";
1148 char ICSInputTranslations[] =
1149 "<Key>Up: UpKeyProc() \n "
1150 "<Key>Down: DownKeyProc() \n "
1151 "<Key>Return: EnterKeyProc() \n";
1153 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1154 // as the widget is destroyed before the up-click can call extend-end
1155 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1157 String xboardResources[] = {
1158 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1159 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1160 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1165 /* Max possible square size */
1166 #define MAXSQSIZE 256
1168 static int xpm_avail[MAXSQSIZE];
1170 #ifdef HAVE_DIR_STRUCT
1172 /* Extract piece size from filename */
1174 xpm_getsize (char *name, int len, char *ext)
1182 if ((p=strchr(name, '.')) == NULL ||
1183 StrCaseCmp(p+1, ext) != 0)
1189 while (*p && isdigit(*p))
1196 /* Setup xpm_avail */
1198 xpm_getavail (char *dirname, char *ext)
1204 for (i=0; i<MAXSQSIZE; ++i)
1207 if (appData.debugMode)
1208 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1210 dir = opendir(dirname);
1213 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1214 programName, dirname);
1218 while ((ent=readdir(dir)) != NULL) {
1219 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1220 if (i > 0 && i < MAXSQSIZE)
1230 xpm_print_avail (FILE *fp, char *ext)
1234 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1235 for (i=1; i<MAXSQSIZE; ++i) {
1241 /* Return XPM piecesize closest to size */
1243 xpm_closest_to (char *dirname, int size, char *ext)
1246 int sm_diff = MAXSQSIZE;
1250 xpm_getavail(dirname, ext);
1252 if (appData.debugMode)
1253 xpm_print_avail(stderr, ext);
1255 for (i=1; i<MAXSQSIZE; ++i) {
1258 diff = (diff<0) ? -diff : diff;
1259 if (diff < sm_diff) {
1267 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1273 #else /* !HAVE_DIR_STRUCT */
1274 /* If we are on a system without a DIR struct, we can't
1275 read the directory, so we can't collect a list of
1276 filenames, etc., so we can't do any size-fitting. */
1278 xpm_closest_to (char *dirname, int size, char *ext)
1280 fprintf(stderr, _("\
1281 Warning: No DIR structure found on this system --\n\
1282 Unable to autosize for XPM/XIM pieces.\n\
1283 Please report this error to %s.\n\
1284 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1287 #endif /* HAVE_DIR_STRUCT */
1289 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1290 "magenta", "cyan", "white" };
1294 TextColors textColors[(int)NColorClasses];
1296 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1298 parse_color (char *str, int which)
1300 char *p, buf[100], *d;
1303 if (strlen(str) > 99) /* watch bounds on buf */
1308 for (i=0; i<which; ++i) {
1315 /* Could be looking at something like:
1317 .. in which case we want to stop on a comma also */
1318 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1322 return -1; /* Use default for empty field */
1325 if (which == 2 || isdigit(*p))
1328 while (*p && isalpha(*p))
1333 for (i=0; i<8; ++i) {
1334 if (!StrCaseCmp(buf, cnames[i]))
1335 return which? (i+40) : (i+30);
1337 if (!StrCaseCmp(buf, "default")) return -1;
1339 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1344 parse_cpair (ColorClass cc, char *str)
1346 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1347 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1352 /* bg and attr are optional */
1353 textColors[(int)cc].bg = parse_color(str, 1);
1354 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1355 textColors[(int)cc].attr = 0;
1361 /* Arrange to catch delete-window events */
1362 Atom wm_delete_window;
1364 CatchDeleteWindow (Widget w, String procname)
1367 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1368 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1369 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1376 XtSetArg(args[0], XtNiconic, False);
1377 XtSetValues(shellWidget, args, 1);
1379 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1382 //---------------------------------------------------------------------------------------------------------
1383 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1386 #define CW_USEDEFAULT (1<<31)
1387 #define ICS_TEXT_MENU_SIZE 90
1388 #define DEBUG_FILE "xboard.debug"
1389 #define SetCurrentDirectory chdir
1390 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1394 // these two must some day move to frontend.h, when they are implemented
1395 Boolean GameListIsUp();
1397 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1400 // front-end part of option handling
1402 // [HGM] This platform-dependent table provides the location for storing the color info
1403 extern char *crWhite, * crBlack;
1407 &appData.whitePieceColor,
1408 &appData.blackPieceColor,
1409 &appData.lightSquareColor,
1410 &appData.darkSquareColor,
1411 &appData.highlightSquareColor,
1412 &appData.premoveHighlightColor,
1413 &appData.lowTimeWarningColor,
1424 // [HGM] font: keep a font for each square size, even non-stndard ones
1425 #define NUM_SIZES 18
1426 #define MAX_SIZE 130
1427 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1428 char *fontTable[NUM_FONTS][MAX_SIZE];
1431 ParseFont (char *name, int number)
1432 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1434 if(sscanf(name, "size%d:", &size)) {
1435 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1436 // defer processing it until we know if it matches our board size
1437 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1438 fontTable[number][size] = strdup(strchr(name, ':')+1);
1439 fontValid[number][size] = True;
1444 case 0: // CLOCK_FONT
1445 appData.clockFont = strdup(name);
1447 case 1: // MESSAGE_FONT
1448 appData.font = strdup(name);
1450 case 2: // COORD_FONT
1451 appData.coordFont = strdup(name);
1456 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1461 { // only 2 fonts currently
1462 appData.clockFont = CLOCK_FONT_NAME;
1463 appData.coordFont = COORD_FONT_NAME;
1464 appData.font = DEFAULT_FONT_NAME;
1469 { // no-op, until we identify the code for this already in XBoard and move it here
1473 ParseColor (int n, char *name)
1474 { // in XBoard, just copy the color-name string
1475 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1479 ParseTextAttribs (ColorClass cc, char *s)
1481 (&appData.colorShout)[cc] = strdup(s);
1485 ParseBoardSize (void *addr, char *name)
1487 appData.boardSize = strdup(name);
1492 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1496 SetCommPortDefaults ()
1497 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1500 // [HGM] args: these three cases taken out to stay in front-end
1502 SaveFontArg (FILE *f, ArgDescriptor *ad)
1505 int i, n = (int)(intptr_t)ad->argLoc;
1507 case 0: // CLOCK_FONT
1508 name = appData.clockFont;
1510 case 1: // MESSAGE_FONT
1511 name = appData.font;
1513 case 2: // COORD_FONT
1514 name = appData.coordFont;
1519 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1520 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1521 fontTable[n][squareSize] = strdup(name);
1522 fontValid[n][squareSize] = True;
1525 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1526 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1531 { // nothing to do, as the sounds are at all times represented by their text-string names already
1535 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1536 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1537 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1541 SaveColor (FILE *f, ArgDescriptor *ad)
1542 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1543 if(colorVariable[(int)(intptr_t)ad->argLoc])
1544 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1548 SaveBoardSize (FILE *f, char *name, void *addr)
1549 { // wrapper to shield back-end from BoardSize & sizeInfo
1550 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1554 ParseCommPortSettings (char *s)
1555 { // no such option in XBoard (yet)
1558 extern Widget engineOutputShell;
1562 GetActualPlacement (Widget wg, WindowPlacement *wp)
1567 XWindowAttributes winAt;
1574 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1575 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1576 wp->x = rx - winAt.x;
1577 wp->y = ry - winAt.y;
1578 wp->height = winAt.height;
1579 wp->width = winAt.width;
1580 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1585 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1586 // In XBoard this will have to wait until awareness of window parameters is implemented
1587 GetActualPlacement(shellWidget, &wpMain);
1588 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1589 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1590 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1591 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1592 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1593 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1597 PrintCommPortSettings (FILE *f, char *name)
1598 { // This option does not exist in XBoard
1602 MySearchPath (char *installDir, char *name, char *fullname)
1603 { // just append installDir and name. Perhaps ExpandPath should be used here?
1604 name = ExpandPathName(name);
1605 if(name && name[0] == '/')
1606 safeStrCpy(fullname, name, MSG_SIZ );
1608 sprintf(fullname, "%s%c%s", installDir, '/', name);
1614 MyGetFullPathName (char *name, char *fullname)
1615 { // should use ExpandPath?
1616 name = ExpandPathName(name);
1617 safeStrCpy(fullname, name, MSG_SIZ );
1622 EnsureOnScreen (int *x, int *y, int minX, int minY)
1629 { // [HGM] args: allows testing if main window is realized from back-end
1630 return xBoardWindow != 0;
1634 PopUpStartupDialog ()
1635 { // start menu not implemented in XBoard
1639 ConvertToLine (int argc, char **argv)
1641 static char line[128*1024], buf[1024];
1645 for(i=1; i<argc; i++)
1647 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1648 && argv[i][0] != '{' )
1649 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1651 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1652 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1655 line[strlen(line)-1] = NULLCHAR;
1659 //--------------------------------------------------------------------------------------------
1661 extern Boolean twoBoards, partnerUp;
1664 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1666 #define BoardSize int
1668 InitDrawingSizes (BoardSize boardSize, int flags)
1669 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1670 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1672 XtGeometryResult gres;
1674 static Dimension oldWidth, oldHeight;
1675 static VariantClass oldVariant;
1676 static int oldDual = -1, oldMono = -1;
1678 if(!formWidget) return;
1680 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1681 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1682 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1684 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1686 * Enable shell resizing.
1688 shellArgs[0].value = (XtArgVal) &w;
1689 shellArgs[1].value = (XtArgVal) &h;
1690 XtGetValues(shellWidget, shellArgs, 2);
1692 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1693 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1694 XtSetValues(shellWidget, &shellArgs[2], 4);
1696 XtSetArg(args[0], XtNdefaultDistance, &sep);
1697 XtGetValues(formWidget, args, 1);
1699 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1701 hOffset = boardWidth + 10;
1702 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1703 secondSegments[i] = gridSegments[i];
1704 secondSegments[i].x1 += hOffset;
1705 secondSegments[i].x2 += hOffset;
1708 XtSetArg(args[0], XtNwidth, boardWidth);
1709 XtSetArg(args[1], XtNheight, boardHeight);
1710 XtSetValues(boardWidget, args, 2);
1712 timerWidth = (boardWidth - sep) / 2;
1713 XtSetArg(args[0], XtNwidth, timerWidth);
1714 XtSetValues(whiteTimerWidget, args, 1);
1715 XtSetValues(blackTimerWidget, args, 1);
1717 XawFormDoLayout(formWidget, False);
1719 if (appData.titleInWindow) {
1721 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1722 XtSetArg(args[i], XtNheight, &h); i++;
1723 XtGetValues(titleWidget, args, i);
1725 w = boardWidth - 2*bor;
1727 XtSetArg(args[0], XtNwidth, &w);
1728 XtGetValues(menuBarWidget, args, 1);
1729 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1732 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1733 if (gres != XtGeometryYes && appData.debugMode) {
1735 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1736 programName, gres, w, h, wr, hr);
1740 XawFormDoLayout(formWidget, True);
1743 * Inhibit shell resizing.
1745 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1746 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1747 shellArgs[4].value = shellArgs[2].value = w;
1748 shellArgs[5].value = shellArgs[3].value = h;
1749 XtSetValues(shellWidget, &shellArgs[0], 6);
1751 XSync(xDisplay, False);
1755 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1758 if(gameInfo.variant != oldVariant) { // and only if variant changed
1761 for(i=0; i<4; i++) {
1763 for(p=0; p<=(int)WhiteKing; p++)
1764 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1765 if(gameInfo.variant == VariantShogi) {
1766 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1767 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1768 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1769 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1770 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1773 if(gameInfo.variant == VariantGothic) {
1774 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1777 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1778 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1779 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1782 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1783 for(p=0; p<=(int)WhiteKing; p++)
1784 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1785 if(gameInfo.variant == VariantShogi) {
1786 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1787 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1788 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1789 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1790 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1793 if(gameInfo.variant == VariantGothic) {
1794 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1797 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1798 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1799 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1804 for(i=0; i<2; i++) {
1806 for(p=0; p<=(int)WhiteKing; p++)
1807 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1808 if(gameInfo.variant == VariantShogi) {
1809 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1810 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1811 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1812 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1813 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1816 if(gameInfo.variant == VariantGothic) {
1817 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1820 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1821 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1822 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1826 oldMono = -10; // kludge to force recreation of animation masks
1827 oldVariant = gameInfo.variant;
1830 if(appData.monoMode != oldMono)
1833 oldMono = appData.monoMode;
1838 ParseIcsTextColors ()
1839 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1840 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1841 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1842 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1843 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1844 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1845 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1846 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1847 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1848 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1849 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1851 if (appData.colorize) {
1853 _("%s: can't parse color names; disabling colorization\n"),
1856 appData.colorize = FALSE;
1862 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1863 XrmValue vFrom, vTo;
1864 int forceMono = False;
1866 if (!appData.monoMode) {
1867 vFrom.addr = (caddr_t) appData.lightSquareColor;
1868 vFrom.size = strlen(appData.lightSquareColor);
1869 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1870 if (vTo.addr == NULL) {
1871 appData.monoMode = True;
1874 lightSquareColor = *(Pixel *) vTo.addr;
1877 if (!appData.monoMode) {
1878 vFrom.addr = (caddr_t) appData.darkSquareColor;
1879 vFrom.size = strlen(appData.darkSquareColor);
1880 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1881 if (vTo.addr == NULL) {
1882 appData.monoMode = True;
1885 darkSquareColor = *(Pixel *) vTo.addr;
1888 if (!appData.monoMode) {
1889 vFrom.addr = (caddr_t) appData.whitePieceColor;
1890 vFrom.size = strlen(appData.whitePieceColor);
1891 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1892 if (vTo.addr == NULL) {
1893 appData.monoMode = True;
1896 whitePieceColor = *(Pixel *) vTo.addr;
1899 if (!appData.monoMode) {
1900 vFrom.addr = (caddr_t) appData.blackPieceColor;
1901 vFrom.size = strlen(appData.blackPieceColor);
1902 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1903 if (vTo.addr == NULL) {
1904 appData.monoMode = True;
1907 blackPieceColor = *(Pixel *) vTo.addr;
1911 if (!appData.monoMode) {
1912 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1913 vFrom.size = strlen(appData.highlightSquareColor);
1914 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1915 if (vTo.addr == NULL) {
1916 appData.monoMode = True;
1919 highlightSquareColor = *(Pixel *) vTo.addr;
1923 if (!appData.monoMode) {
1924 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1925 vFrom.size = strlen(appData.premoveHighlightColor);
1926 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1927 if (vTo.addr == NULL) {
1928 appData.monoMode = True;
1931 premoveHighlightColor = *(Pixel *) vTo.addr;
1939 { // [HGM] taken out of main
1941 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1942 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1943 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1945 if (appData.bitmapDirectory[0] != NULLCHAR) {
1949 CreateXPMBoard(appData.liteBackTextureFile, 1);
1950 CreateXPMBoard(appData.darkBackTextureFile, 0);
1954 /* Create regular pieces */
1955 if (!useImages) CreatePieces();
1960 main (int argc, char **argv)
1962 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1963 XSetWindowAttributes window_attributes;
1965 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1966 XrmValue vFrom, vTo;
1967 XtGeometryResult gres;
1970 int forceMono = False;
1972 srandom(time(0)); // [HGM] book: make random truly random
1974 setbuf(stdout, NULL);
1975 setbuf(stderr, NULL);
1978 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1979 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1983 programName = strrchr(argv[0], '/');
1984 if (programName == NULL)
1985 programName = argv[0];
1990 XtSetLanguageProc(NULL, NULL, NULL);
1991 bindtextdomain(PACKAGE, LOCALEDIR);
1992 textdomain(PACKAGE);
1996 XtAppInitialize(&appContext, "XBoard", shellOptions,
1997 XtNumber(shellOptions),
1998 &argc, argv, xboardResources, NULL, 0);
1999 appData.boardSize = "";
2000 InitAppData(ConvertToLine(argc, argv));
2002 if (p == NULL) p = "/tmp";
2003 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2004 gameCopyFilename = (char*) malloc(i);
2005 gamePasteFilename = (char*) malloc(i);
2006 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2007 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2009 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2010 clientResources, XtNumber(clientResources),
2013 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2014 static char buf[MSG_SIZ];
2015 EscapeExpand(buf, appData.firstInitString);
2016 appData.firstInitString = strdup(buf);
2017 EscapeExpand(buf, appData.secondInitString);
2018 appData.secondInitString = strdup(buf);
2019 EscapeExpand(buf, appData.firstComputerString);
2020 appData.firstComputerString = strdup(buf);
2021 EscapeExpand(buf, appData.secondComputerString);
2022 appData.secondComputerString = strdup(buf);
2025 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2028 if (chdir(chessDir) != 0) {
2029 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2035 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2036 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2037 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2038 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2041 setbuf(debugFP, NULL);
2045 if (appData.debugMode) {
2046 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2050 /* [HGM,HR] make sure board size is acceptable */
2051 if(appData.NrFiles > BOARD_FILES ||
2052 appData.NrRanks > BOARD_RANKS )
2053 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2056 /* This feature does not work; animation needs a rewrite */
2057 appData.highlightDragging = FALSE;
2061 xDisplay = XtDisplay(shellWidget);
2062 xScreen = DefaultScreen(xDisplay);
2063 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2065 gameInfo.variant = StringToVariant(appData.variant);
2066 InitPosition(FALSE);
2069 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2071 if (isdigit(appData.boardSize[0])) {
2072 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2073 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2074 &fontPxlSize, &smallLayout, &tinyLayout);
2076 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2077 programName, appData.boardSize);
2081 /* Find some defaults; use the nearest known size */
2082 SizeDefaults *szd, *nearest;
2083 int distance = 99999;
2084 nearest = szd = sizeDefaults;
2085 while (szd->name != NULL) {
2086 if (abs(szd->squareSize - squareSize) < distance) {
2088 distance = abs(szd->squareSize - squareSize);
2089 if (distance == 0) break;
2093 if (i < 2) lineGap = nearest->lineGap;
2094 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2095 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2096 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2097 if (i < 6) smallLayout = nearest->smallLayout;
2098 if (i < 7) tinyLayout = nearest->tinyLayout;
2101 SizeDefaults *szd = sizeDefaults;
2102 if (*appData.boardSize == NULLCHAR) {
2103 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2104 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2107 if (szd->name == NULL) szd--;
2108 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2110 while (szd->name != NULL &&
2111 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2112 if (szd->name == NULL) {
2113 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2114 programName, appData.boardSize);
2118 squareSize = szd->squareSize;
2119 lineGap = szd->lineGap;
2120 clockFontPxlSize = szd->clockFontPxlSize;
2121 coordFontPxlSize = szd->coordFontPxlSize;
2122 fontPxlSize = szd->fontPxlSize;
2123 smallLayout = szd->smallLayout;
2124 tinyLayout = szd->tinyLayout;
2125 // [HGM] font: use defaults from settings file if available and not overruled
2127 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2128 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2129 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2130 appData.font = fontTable[MESSAGE_FONT][squareSize];
2131 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2132 appData.coordFont = fontTable[COORD_FONT][squareSize];
2134 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2135 if (strlen(appData.pixmapDirectory) > 0) {
2136 p = ExpandPathName(appData.pixmapDirectory);
2138 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2139 appData.pixmapDirectory);
2142 if (appData.debugMode) {
2143 fprintf(stderr, _("\
2144 XBoard square size (hint): %d\n\
2145 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2147 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2148 if (appData.debugMode) {
2149 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2152 defaultLineGap = lineGap;
2153 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2155 /* [HR] height treated separately (hacked) */
2156 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2157 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2158 if (appData.showJail == 1) {
2159 /* Jail on top and bottom */
2160 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2161 XtSetArg(boardArgs[2], XtNheight,
2162 boardHeight + 2*(lineGap + squareSize));
2163 } else if (appData.showJail == 2) {
2165 XtSetArg(boardArgs[1], XtNwidth,
2166 boardWidth + 2*(lineGap + squareSize));
2167 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2170 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2171 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2175 * Determine what fonts to use.
2178 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2179 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2180 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2181 fontSet = CreateFontSet(appData.font);
2182 clockFontSet = CreateFontSet(appData.clockFont);
2184 /* For the coordFont, use the 0th font of the fontset. */
2185 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2186 XFontStruct **font_struct_list;
2187 XFontSetExtents *fontSize;
2188 char **font_name_list;
2189 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2190 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2191 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2192 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
2193 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
2196 appData.font = FindFont(appData.font, fontPxlSize);
2197 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2198 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2199 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2200 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2201 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2202 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2204 countFontID = coordFontID; // [HGM] holdings
2205 countFontStruct = coordFontStruct;
2207 xdb = XtDatabase(xDisplay);
2209 XrmPutLineResource(&xdb, "*international: True");
2210 vTo.size = sizeof(XFontSet);
2211 vTo.addr = (XtPointer) &fontSet;
2212 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2214 XrmPutStringResource(&xdb, "*font", appData.font);
2218 * Detect if there are not enough colors available and adapt.
2220 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2221 appData.monoMode = True;
2224 forceMono = MakeColors();
2227 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2229 appData.monoMode = True;
2232 if (appData.lowTimeWarning && !appData.monoMode) {
2233 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2234 vFrom.size = strlen(appData.lowTimeWarningColor);
2235 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2236 if (vTo.addr == NULL)
2237 appData.monoMode = True;
2239 lowTimeWarningColor = *(Pixel *) vTo.addr;
2242 if (appData.monoMode && appData.debugMode) {
2243 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2244 (unsigned long) XWhitePixel(xDisplay, xScreen),
2245 (unsigned long) XBlackPixel(xDisplay, xScreen));
2248 ParseIcsTextColors();
2249 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2250 textColors[ColorNone].attr = 0;
2252 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2258 layoutName = "tinyLayout";
2259 } else if (smallLayout) {
2260 layoutName = "smallLayout";
2262 layoutName = "normalLayout";
2264 /* Outer layoutWidget is there only to provide a name for use in
2265 resources that depend on the layout style */
2267 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2268 layoutArgs, XtNumber(layoutArgs));
2270 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2271 formArgs, XtNumber(formArgs));
2272 XtSetArg(args[0], XtNdefaultDistance, &sep);
2273 XtGetValues(formWidget, args, 1);
2276 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2277 XtSetArg(args[0], XtNtop, XtChainTop);
2278 XtSetArg(args[1], XtNbottom, XtChainTop);
2279 XtSetArg(args[2], XtNright, XtChainLeft);
2280 XtSetValues(menuBarWidget, args, 3);
2282 widgetList[j++] = whiteTimerWidget =
2283 XtCreateWidget("whiteTime", labelWidgetClass,
2284 formWidget, timerArgs, XtNumber(timerArgs));
2286 XtSetArg(args[0], XtNfontSet, clockFontSet);
2288 XtSetArg(args[0], XtNfont, clockFontStruct);
2290 XtSetArg(args[1], XtNtop, XtChainTop);
2291 XtSetArg(args[2], XtNbottom, XtChainTop);
2292 XtSetValues(whiteTimerWidget, args, 3);
2294 widgetList[j++] = blackTimerWidget =
2295 XtCreateWidget("blackTime", labelWidgetClass,
2296 formWidget, timerArgs, XtNumber(timerArgs));
2298 XtSetArg(args[0], XtNfontSet, clockFontSet);
2300 XtSetArg(args[0], XtNfont, clockFontStruct);
2302 XtSetArg(args[1], XtNtop, XtChainTop);
2303 XtSetArg(args[2], XtNbottom, XtChainTop);
2304 XtSetValues(blackTimerWidget, args, 3);
2306 if (appData.titleInWindow) {
2307 widgetList[j++] = titleWidget =
2308 XtCreateWidget("title", labelWidgetClass, formWidget,
2309 titleArgs, XtNumber(titleArgs));
2310 XtSetArg(args[0], XtNtop, XtChainTop);
2311 XtSetArg(args[1], XtNbottom, XtChainTop);
2312 XtSetValues(titleWidget, args, 2);
2315 if (appData.showButtonBar) {
2316 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2317 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2318 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2319 XtSetArg(args[2], XtNtop, XtChainTop);
2320 XtSetArg(args[3], XtNbottom, XtChainTop);
2321 XtSetValues(buttonBarWidget, args, 4);
2324 widgetList[j++] = messageWidget =
2325 XtCreateWidget("message", labelWidgetClass, formWidget,
2326 messageArgs, XtNumber(messageArgs));
2327 XtSetArg(args[0], XtNtop, XtChainTop);
2328 XtSetArg(args[1], XtNbottom, XtChainTop);
2329 XtSetValues(messageWidget, args, 2);
2331 widgetList[j++] = boardWidget =
2332 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2333 XtNumber(boardArgs));
2335 XtManageChildren(widgetList, j);
2337 timerWidth = (boardWidth - sep) / 2;
2338 XtSetArg(args[0], XtNwidth, timerWidth);
2339 XtSetValues(whiteTimerWidget, args, 1);
2340 XtSetValues(blackTimerWidget, args, 1);
2342 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2343 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2344 XtGetValues(whiteTimerWidget, args, 2);
2346 if (appData.showButtonBar) {
2347 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2348 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2349 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2353 * formWidget uses these constraints but they are stored
2357 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2358 XtSetValues(menuBarWidget, args, i);
2359 if (appData.titleInWindow) {
2362 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2363 XtSetValues(whiteTimerWidget, args, i);
2365 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2366 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2367 XtSetValues(blackTimerWidget, args, i);
2369 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2370 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2371 XtSetValues(titleWidget, args, i);
2373 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2374 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2375 XtSetValues(messageWidget, args, i);
2376 if (appData.showButtonBar) {
2378 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2379 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2380 XtSetValues(buttonBarWidget, args, i);
2384 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2385 XtSetValues(whiteTimerWidget, args, i);
2387 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2388 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2389 XtSetValues(blackTimerWidget, args, i);
2391 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2392 XtSetValues(titleWidget, args, i);
2394 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2395 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2396 XtSetValues(messageWidget, args, i);
2397 if (appData.showButtonBar) {
2399 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2400 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2401 XtSetValues(buttonBarWidget, args, i);
2406 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2407 XtSetValues(whiteTimerWidget, args, i);
2409 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2410 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2411 XtSetValues(blackTimerWidget, args, i);
2413 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2414 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2415 XtSetValues(messageWidget, args, i);
2416 if (appData.showButtonBar) {
2418 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2419 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2420 XtSetValues(buttonBarWidget, args, i);
2424 XtSetArg(args[0], XtNfromVert, messageWidget);
2425 XtSetArg(args[1], XtNtop, XtChainTop);
2426 XtSetArg(args[2], XtNbottom, XtChainBottom);
2427 XtSetArg(args[3], XtNleft, XtChainLeft);
2428 XtSetArg(args[4], XtNright, XtChainRight);
2429 XtSetValues(boardWidget, args, 5);
2431 XtRealizeWidget(shellWidget);
2434 XtSetArg(args[0], XtNx, wpMain.x);
2435 XtSetArg(args[1], XtNy, wpMain.y);
2436 XtSetValues(shellWidget, args, 2);
2440 * Correct the width of the message and title widgets.
2441 * It is not known why some systems need the extra fudge term.
2442 * The value "2" is probably larger than needed.
2444 XawFormDoLayout(formWidget, False);
2446 #define WIDTH_FUDGE 2
2448 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2449 XtSetArg(args[i], XtNheight, &h); i++;
2450 XtGetValues(messageWidget, args, i);
2451 if (appData.showButtonBar) {
2453 XtSetArg(args[i], XtNwidth, &w); i++;
2454 XtGetValues(buttonBarWidget, args, i);
2455 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2457 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2460 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2461 if (gres != XtGeometryYes && appData.debugMode) {
2462 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2463 programName, gres, w, h, wr, hr);
2466 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2467 /* The size used for the child widget in layout lags one resize behind
2468 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2470 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2471 if (gres != XtGeometryYes && appData.debugMode) {
2472 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2473 programName, gres, w, h, wr, hr);
2476 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2477 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2478 XtSetArg(args[1], XtNright, XtChainRight);
2479 XtSetValues(messageWidget, args, 2);
2481 if (appData.titleInWindow) {
2483 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2484 XtSetArg(args[i], XtNheight, &h); i++;
2485 XtGetValues(titleWidget, args, i);
2487 w = boardWidth - 2*bor;
2489 XtSetArg(args[0], XtNwidth, &w);
2490 XtGetValues(menuBarWidget, args, 1);
2491 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2494 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2495 if (gres != XtGeometryYes && appData.debugMode) {
2497 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2498 programName, gres, w, h, wr, hr);
2501 XawFormDoLayout(formWidget, True);
2503 xBoardWindow = XtWindow(boardWidget);
2505 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2506 // not need to go into InitDrawingSizes().
2510 * Create X checkmark bitmap and initialize option menu checks.
2512 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2513 checkmark_bits, checkmark_width, checkmark_height);
2514 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2515 #ifndef OPTIONSDIALOG
2516 if (appData.alwaysPromoteToQueen) {
2517 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2520 if (appData.animateDragging) {
2521 XtSetValues(XtNameToWidget(menuBarWidget,
2522 "menuOptions.Animate Dragging"),
2525 if (appData.animate) {
2526 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2529 if (appData.autoCallFlag) {
2530 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2533 if (appData.autoFlipView) {
2534 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2537 if (appData.blindfold) {
2538 XtSetValues(XtNameToWidget(menuBarWidget,
2539 "menuOptions.Blindfold"), args, 1);
2541 if (appData.flashCount > 0) {
2542 XtSetValues(XtNameToWidget(menuBarWidget,
2543 "menuOptions.Flash Moves"),
2547 if (appData.highlightDragging) {
2548 XtSetValues(XtNameToWidget(menuBarWidget,
2549 "menuOptions.Highlight Dragging"),
2553 if (appData.highlightLastMove) {
2554 XtSetValues(XtNameToWidget(menuBarWidget,
2555 "menuOptions.Highlight Last Move"),
2558 if (appData.highlightMoveWithArrow) {
2559 XtSetValues(XtNameToWidget(menuBarWidget,
2560 "menuOptions.Arrow"),
2563 // if (appData.icsAlarm) {
2564 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2567 if (appData.ringBellAfterMoves) {
2568 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2571 if (appData.oneClick) {
2572 XtSetValues(XtNameToWidget(menuBarWidget,
2573 "menuOptions.OneClick"), args, 1);
2575 if (appData.periodicUpdates) {
2576 XtSetValues(XtNameToWidget(menuBarWidget,
2577 "menuOptions.Periodic Updates"), args, 1);
2579 if (appData.ponderNextMove) {
2580 XtSetValues(XtNameToWidget(menuBarWidget,
2581 "menuOptions.Ponder Next Move"), args, 1);
2583 if (appData.popupExitMessage) {
2584 XtSetValues(XtNameToWidget(menuBarWidget,
2585 "menuOptions.Popup Exit Message"), args, 1);
2587 if (appData.popupMoveErrors) {
2588 XtSetValues(XtNameToWidget(menuBarWidget,
2589 "menuOptions.Popup Move Errors"), args, 1);
2591 // if (appData.premove) {
2592 // XtSetValues(XtNameToWidget(menuBarWidget,
2593 // "menuOptions.Premove"), args, 1);
2595 if (appData.showCoords) {
2596 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2599 if (appData.hideThinkingFromHuman) {
2600 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2603 if (appData.testLegality) {
2604 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2608 if (saveSettingsOnExit) {
2609 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2616 ReadBitmap(&wIconPixmap, "icon_white.bm",
2617 icon_white_bits, icon_white_width, icon_white_height);
2618 ReadBitmap(&bIconPixmap, "icon_black.bm",
2619 icon_black_bits, icon_black_width, icon_black_height);
2620 iconPixmap = wIconPixmap;
2622 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2623 XtSetValues(shellWidget, args, i);
2626 * Create a cursor for the board widget.
2628 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2629 XChangeWindowAttributes(xDisplay, xBoardWindow,
2630 CWCursor, &window_attributes);
2633 * Inhibit shell resizing.
2635 shellArgs[0].value = (XtArgVal) &w;
2636 shellArgs[1].value = (XtArgVal) &h;
2637 XtGetValues(shellWidget, shellArgs, 2);
2638 shellArgs[4].value = shellArgs[2].value = w;
2639 shellArgs[5].value = shellArgs[3].value = h;
2640 XtSetValues(shellWidget, &shellArgs[2], 4);
2641 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2642 marginH = h - boardHeight;
2644 CatchDeleteWindow(shellWidget, "QuitProc");
2652 if (appData.animate || appData.animateDragging)
2655 XtAugmentTranslations(formWidget,
2656 XtParseTranslationTable(globalTranslations));
2657 XtAugmentTranslations(boardWidget,
2658 XtParseTranslationTable(boardTranslations));
2659 XtAugmentTranslations(whiteTimerWidget,
2660 XtParseTranslationTable(whiteTranslations));
2661 XtAugmentTranslations(blackTimerWidget,
2662 XtParseTranslationTable(blackTranslations));
2664 /* Why is the following needed on some versions of X instead
2665 * of a translation? */
2666 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2667 (XtEventHandler) EventProc, NULL);
2669 XtAddEventHandler(formWidget, KeyPressMask, False,
2670 (XtEventHandler) MoveTypeInProc, NULL);
2671 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2672 (XtEventHandler) EventProc, NULL);
2674 /* [AS] Restore layout */
2675 if( wpMoveHistory.visible ) {
2679 if( wpEvalGraph.visible )
2684 if( wpEngineOutput.visible ) {
2685 EngineOutputPopUp();
2690 if (errorExitStatus == -1) {
2691 if (appData.icsActive) {
2692 /* We now wait until we see "login:" from the ICS before
2693 sending the logon script (problems with timestamp otherwise) */
2694 /*ICSInitScript();*/
2695 if (appData.icsInputBox) ICSInputBoxPopUp();
2699 signal(SIGWINCH, TermSizeSigHandler);
2701 signal(SIGINT, IntSigHandler);
2702 signal(SIGTERM, IntSigHandler);
2703 if (*appData.cmailGameName != NULLCHAR) {
2704 signal(SIGUSR1, CmailSigHandler);
2707 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2709 // XtSetKeyboardFocus(shellWidget, formWidget);
2710 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2712 XtAppMainLoop(appContext);
2713 if (appData.debugMode) fclose(debugFP); // [DM] debug
2717 static Boolean noEcho;
2722 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2723 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2725 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2726 unlink(gameCopyFilename);
2727 unlink(gamePasteFilename);
2728 if(noEcho) EchoOn();
2732 TermSizeSigHandler (int sig)
2738 IntSigHandler (int sig)
2744 CmailSigHandler (int sig)
2749 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2751 /* Activate call-back function CmailSigHandlerCallBack() */
2752 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2754 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2758 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2761 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2763 /**** end signal code ****/
2769 /* try to open the icsLogon script, either in the location given
2770 * or in the users HOME directory
2777 f = fopen(appData.icsLogon, "r");
2780 homedir = getenv("HOME");
2781 if (homedir != NULL)
2783 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2784 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2785 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2786 f = fopen(buf, "r");
2791 ProcessICSInitScript(f);
2793 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2812 GreyRevert (Boolean grey)
2815 if (!menuBarWidget) return;
2816 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2818 DisplayError("menuEdit.Revert", 0);
2820 XtSetSensitive(w, !grey);
2822 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2824 DisplayError("menuEdit.Annotate", 0);
2826 XtSetSensitive(w, !grey);
2831 SetMenuEnables (Enables *enab)
2834 if (!menuBarWidget) return;
2835 while (enab->name != NULL) {
2836 w = XtNameToWidget(menuBarWidget, enab->name);
2838 DisplayError(enab->name, 0);
2840 XtSetSensitive(w, enab->value);
2846 Enables icsEnables[] = {
2847 { "menuFile.Mail Move", False },
2848 { "menuFile.Reload CMail Message", False },
2849 { "menuMode.Machine Black", False },
2850 { "menuMode.Machine White", False },
2851 { "menuMode.Analysis Mode", False },
2852 { "menuMode.Analyze File", False },
2853 { "menuMode.Two Machines", False },
2854 { "menuMode.Machine Match", False },
2856 { "menuEngine.Hint", False },
2857 { "menuEngine.Book", False },
2858 { "menuEngine.Move Now", False },
2859 #ifndef OPTIONSDIALOG
2860 { "menuOptions.Periodic Updates", False },
2861 { "menuOptions.Hide Thinking", False },
2862 { "menuOptions.Ponder Next Move", False },
2865 { "menuEngine.Engine #1 Settings", False },
2866 { "menuEngine.Engine #2 Settings", False },
2867 { "menuEngine.Load Engine", False },
2868 { "menuEdit.Annotate", False },
2869 { "menuOptions.Match", False },
2873 Enables ncpEnables[] = {
2874 { "menuFile.Mail Move", False },
2875 { "menuFile.Reload CMail Message", False },
2876 { "menuMode.Machine White", False },
2877 { "menuMode.Machine Black", False },
2878 { "menuMode.Analysis Mode", False },
2879 { "menuMode.Analyze File", False },
2880 { "menuMode.Two Machines", False },
2881 { "menuMode.Machine Match", False },
2882 { "menuMode.ICS Client", False },
2883 { "menuView.ICStex", False },
2884 { "menuView.ICS Input Box", False },
2885 { "Action", False },
2886 { "menuEdit.Revert", False },
2887 { "menuEdit.Annotate", False },
2888 { "menuEngine.Engine #1 Settings", False },
2889 { "menuEngine.Engine #2 Settings", False },
2890 { "menuEngine.Move Now", False },
2891 { "menuEngine.Retract Move", False },
2892 { "menuOptions.ICS", False },
2893 #ifndef OPTIONSDIALOG
2894 { "menuOptions.Auto Flag", False },
2895 { "menuOptions.Auto Flip View", False },
2896 // { "menuOptions.ICS Alarm", False },
2897 { "menuOptions.Move Sound", False },
2898 { "menuOptions.Hide Thinking", False },
2899 { "menuOptions.Periodic Updates", False },
2900 { "menuOptions.Ponder Next Move", False },
2902 { "menuEngine.Hint", False },
2903 { "menuEngine.Book", False },
2907 Enables gnuEnables[] = {
2908 { "menuMode.ICS Client", False },
2909 { "menuView.ICStex", False },
2910 { "menuView.ICS Input Box", False },
2911 { "menuAction.Accept", False },
2912 { "menuAction.Decline", False },
2913 { "menuAction.Rematch", False },
2914 { "menuAction.Adjourn", False },
2915 { "menuAction.Stop Examining", False },
2916 { "menuAction.Stop Observing", False },
2917 { "menuAction.Upload to Examine", False },
2918 { "menuEdit.Revert", False },
2919 { "menuEdit.Annotate", False },
2920 { "menuOptions.ICS", False },
2922 /* The next two options rely on SetCmailMode being called *after* */
2923 /* SetGNUMode so that when GNU is being used to give hints these */
2924 /* menu options are still available */
2926 { "menuFile.Mail Move", False },
2927 { "menuFile.Reload CMail Message", False },
2928 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2929 { "menuMode.Machine White", True },
2930 { "menuMode.Machine Black", True },
2931 { "menuMode.Analysis Mode", True },
2932 { "menuMode.Analyze File", True },
2933 { "menuMode.Two Machines", True },
2934 { "menuMode.Machine Match", True },
2935 { "menuEngine.Engine #1 Settings", True },
2936 { "menuEngine.Engine #2 Settings", True },
2937 { "menuEngine.Hint", True },
2938 { "menuEngine.Book", True },
2939 { "menuEngine.Move Now", True },
2940 { "menuEngine.Retract Move", True },
2945 Enables cmailEnables[] = {
2947 { "menuAction.Call Flag", False },
2948 { "menuAction.Draw", True },
2949 { "menuAction.Adjourn", False },
2950 { "menuAction.Abort", False },
2951 { "menuAction.Stop Observing", False },
2952 { "menuAction.Stop Examining", False },
2953 { "menuFile.Mail Move", True },
2954 { "menuFile.Reload CMail Message", True },
2958 Enables trainingOnEnables[] = {
2959 { "menuMode.Edit Comment", False },
2960 { "menuMode.Pause", False },
2961 { "menuEdit.Forward", False },
2962 { "menuEdit.Backward", False },
2963 { "menuEdit.Forward to End", False },
2964 { "menuEdit.Back to Start", False },
2965 { "menuEngine.Move Now", False },
2966 { "menuEdit.Truncate Game", False },
2970 Enables trainingOffEnables[] = {
2971 { "menuMode.Edit Comment", True },
2972 { "menuMode.Pause", True },
2973 { "menuEdit.Forward", True },
2974 { "menuEdit.Backward", True },
2975 { "menuEdit.Forward to End", True },
2976 { "menuEdit.Back to Start", True },
2977 { "menuEngine.Move Now", True },
2978 { "menuEdit.Truncate Game", True },
2982 Enables machineThinkingEnables[] = {
2983 { "menuFile.Load Game", False },
2984 // { "menuFile.Load Next Game", False },
2985 // { "menuFile.Load Previous Game", False },
2986 // { "menuFile.Reload Same Game", False },
2987 { "menuEdit.Paste Game", False },
2988 { "menuFile.Load Position", False },
2989 // { "menuFile.Load Next Position", False },
2990 // { "menuFile.Load Previous Position", False },
2991 // { "menuFile.Reload Same Position", False },
2992 { "menuEdit.Paste Position", False },
2993 { "menuMode.Machine White", False },
2994 { "menuMode.Machine Black", False },
2995 { "menuMode.Two Machines", False },
2996 // { "menuMode.Machine Match", False },
2997 { "menuEngine.Retract Move", False },
3001 Enables userThinkingEnables[] = {
3002 { "menuFile.Load Game", True },
3003 // { "menuFile.Load Next Game", True },
3004 // { "menuFile.Load Previous Game", True },
3005 // { "menuFile.Reload Same Game", True },
3006 { "menuEdit.Paste Game", True },
3007 { "menuFile.Load Position", True },
3008 // { "menuFile.Load Next Position", True },
3009 // { "menuFile.Load Previous Position", True },
3010 // { "menuFile.Reload Same Position", True },
3011 { "menuEdit.Paste Position", True },
3012 { "menuMode.Machine White", True },
3013 { "menuMode.Machine Black", True },
3014 { "menuMode.Two Machines", True },
3015 // { "menuMode.Machine Match", True },
3016 { "menuEngine.Retract Move", True },
3023 SetMenuEnables(icsEnables);
3026 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3027 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3028 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3036 SetMenuEnables(ncpEnables);
3042 SetMenuEnables(gnuEnables);
3048 SetMenuEnables(cmailEnables);
3052 SetTrainingModeOn ()
3054 SetMenuEnables(trainingOnEnables);
3055 if (appData.showButtonBar) {
3056 XtSetSensitive(buttonBarWidget, False);
3062 SetTrainingModeOff ()
3064 SetMenuEnables(trainingOffEnables);
3065 if (appData.showButtonBar) {
3066 XtSetSensitive(buttonBarWidget, True);
3071 SetUserThinkingEnables ()
3073 if (appData.noChessProgram) return;
3074 SetMenuEnables(userThinkingEnables);
3078 SetMachineThinkingEnables ()
3080 if (appData.noChessProgram) return;
3081 SetMenuEnables(machineThinkingEnables);
3083 case MachinePlaysBlack:
3084 case MachinePlaysWhite:
3085 case TwoMachinesPlay:
3086 XtSetSensitive(XtNameToWidget(menuBarWidget,
3087 ModeToWidgetName(gameMode)), True);
3094 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3095 #define HISTORY_SIZE 64
3096 static char *history[HISTORY_SIZE];
3097 int histIn = 0, histP = 0;
3100 SaveInHistory (char *cmd)
3102 if (history[histIn] != NULL) {
3103 free(history[histIn]);
3104 history[histIn] = NULL;
3106 if (*cmd == NULLCHAR) return;
3107 history[histIn] = StrSave(cmd);
3108 histIn = (histIn + 1) % HISTORY_SIZE;
3109 if (history[histIn] != NULL) {
3110 free(history[histIn]);
3111 history[histIn] = NULL;
3117 PrevInHistory (char *cmd)
3120 if (histP == histIn) {
3121 if (history[histIn] != NULL) free(history[histIn]);
3122 history[histIn] = StrSave(cmd);
3124 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3125 if (newhp == histIn || history[newhp] == NULL) return NULL;
3127 return history[histP];
3133 if (histP == histIn) return NULL;
3134 histP = (histP + 1) % HISTORY_SIZE;
3135 return history[histP];
3137 // end of borrowed code
3139 #define Abs(n) ((n)<0 ? -(n) : (n))
3143 InsertPxlSize (char *pattern, int targetPxlSize)
3145 char *base_fnt_lst, strInt[12], *p, *q;
3146 int alternatives, i, len, strIntLen;
3149 * Replace the "*" (if present) in the pixel-size slot of each
3150 * alternative with the targetPxlSize.
3154 while ((p = strchr(p, ',')) != NULL) {
3158 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3159 strIntLen = strlen(strInt);
3160 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3164 while (alternatives--) {
3165 char *comma = strchr(p, ',');
3166 for (i=0; i<14; i++) {
3167 char *hyphen = strchr(p, '-');
3169 if (comma && hyphen > comma) break;
3170 len = hyphen + 1 - p;
3171 if (i == 7 && *p == '*' && len == 2) {
3173 memcpy(q, strInt, strIntLen);
3183 len = comma + 1 - p;
3190 return base_fnt_lst;
3194 CreateFontSet (char *base_fnt_lst)
3197 char **missing_list;
3201 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3202 &missing_list, &missing_count, &def_string);
3203 if (appData.debugMode) {
3205 XFontStruct **font_struct_list;
3206 char **font_name_list;
3207 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3209 fprintf(debugFP, " got list %s, locale %s\n",
3210 XBaseFontNameListOfFontSet(fntSet),
3211 XLocaleOfFontSet(fntSet));
3212 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3213 for (i = 0; i < count; i++) {
3214 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3217 for (i = 0; i < missing_count; i++) {
3218 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3221 if (fntSet == NULL) {
3222 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3227 #else // not ENABLE_NLS
3229 * Find a font that matches "pattern" that is as close as
3230 * possible to the targetPxlSize. Prefer fonts that are k
3231 * pixels smaller to fonts that are k pixels larger. The
3232 * pattern must be in the X Consortium standard format,
3233 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3234 * The return value should be freed with XtFree when no
3238 FindFont (char *pattern, int targetPxlSize)
3240 char **fonts, *p, *best, *scalable, *scalableTail;
3241 int i, j, nfonts, minerr, err, pxlSize;
3243 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3245 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3246 programName, pattern);
3253 for (i=0; i<nfonts; i++) {
3256 if (*p != '-') continue;
3258 if (*p == NULLCHAR) break;
3259 if (*p++ == '-') j++;
3261 if (j < 7) continue;
3264 scalable = fonts[i];
3267 err = pxlSize - targetPxlSize;
3268 if (Abs(err) < Abs(minerr) ||
3269 (minerr > 0 && err < 0 && -err == minerr)) {
3275 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3276 /* If the error is too big and there is a scalable font,
3277 use the scalable font. */
3278 int headlen = scalableTail - scalable;
3279 p = (char *) XtMalloc(strlen(scalable) + 10);
3280 while (isdigit(*scalableTail)) scalableTail++;
3281 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3283 p = (char *) XtMalloc(strlen(best) + 2);
3284 safeStrCpy(p, best, strlen(best)+1 );
3286 if (appData.debugMode) {
3287 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3288 pattern, targetPxlSize, p);
3290 XFreeFontNames(fonts);
3297 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3298 // must be called before all non-first callse to CreateGCs()
3299 XtReleaseGC(shellWidget, highlineGC);
3300 XtReleaseGC(shellWidget, lightSquareGC);
3301 XtReleaseGC(shellWidget, darkSquareGC);
3302 XtReleaseGC(shellWidget, lineGC);
3303 if (appData.monoMode) {
3304 if (DefaultDepth(xDisplay, xScreen) == 1) {
3305 XtReleaseGC(shellWidget, wbPieceGC);
3307 XtReleaseGC(shellWidget, bwPieceGC);
3310 XtReleaseGC(shellWidget, prelineGC);
3311 XtReleaseGC(shellWidget, jailSquareGC);
3312 XtReleaseGC(shellWidget, wdPieceGC);
3313 XtReleaseGC(shellWidget, wlPieceGC);
3314 XtReleaseGC(shellWidget, wjPieceGC);
3315 XtReleaseGC(shellWidget, bdPieceGC);
3316 XtReleaseGC(shellWidget, blPieceGC);
3317 XtReleaseGC(shellWidget, bjPieceGC);
3322 CreateGCs (int redo)
3324 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3325 | GCBackground | GCFunction | GCPlaneMask;
3326 XGCValues gc_values;
3329 gc_values.plane_mask = AllPlanes;
3330 gc_values.line_width = lineGap;
3331 gc_values.line_style = LineSolid;
3332 gc_values.function = GXcopy;
3335 DeleteGCs(); // called a second time; clean up old GCs first
3336 } else { // [HGM] grid and font GCs created on first call only
3337 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3338 gc_values.background = XWhitePixel(xDisplay, xScreen);
3339 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3340 XSetFont(xDisplay, coordGC, coordFontID);
3342 // [HGM] make font for holdings counts (white on black)
3343 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3344 gc_values.background = XBlackPixel(xDisplay, xScreen);
3345 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3346 XSetFont(xDisplay, countGC, countFontID);
3348 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3349 gc_values.background = XBlackPixel(xDisplay, xScreen);
3350 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3352 if (appData.monoMode) {
3353 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3354 gc_values.background = XWhitePixel(xDisplay, xScreen);
3355 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3357 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3358 gc_values.background = XBlackPixel(xDisplay, xScreen);
3359 lightSquareGC = wbPieceGC
3360 = XtGetGC(shellWidget, value_mask, &gc_values);
3362 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3363 gc_values.background = XWhitePixel(xDisplay, xScreen);
3364 darkSquareGC = bwPieceGC
3365 = XtGetGC(shellWidget, value_mask, &gc_values);
3367 if (DefaultDepth(xDisplay, xScreen) == 1) {
3368 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3369 gc_values.function = GXcopyInverted;
3370 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3371 gc_values.function = GXcopy;
3372 if (XBlackPixel(xDisplay, xScreen) == 1) {
3373 bwPieceGC = darkSquareGC;
3374 wbPieceGC = copyInvertedGC;
3376 bwPieceGC = copyInvertedGC;
3377 wbPieceGC = lightSquareGC;
3381 gc_values.foreground = highlightSquareColor;
3382 gc_values.background = highlightSquareColor;
3383 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3385 gc_values.foreground = premoveHighlightColor;
3386 gc_values.background = premoveHighlightColor;
3387 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3389 gc_values.foreground = lightSquareColor;
3390 gc_values.background = darkSquareColor;
3391 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3393 gc_values.foreground = darkSquareColor;
3394 gc_values.background = lightSquareColor;
3395 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3397 gc_values.foreground = jailSquareColor;
3398 gc_values.background = jailSquareColor;
3399 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3401 gc_values.foreground = whitePieceColor;
3402 gc_values.background = darkSquareColor;
3403 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3405 gc_values.foreground = whitePieceColor;
3406 gc_values.background = lightSquareColor;
3407 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3409 gc_values.foreground = whitePieceColor;
3410 gc_values.background = jailSquareColor;
3411 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3413 gc_values.foreground = blackPieceColor;
3414 gc_values.background = darkSquareColor;
3415 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3417 gc_values.foreground = blackPieceColor;
3418 gc_values.background = lightSquareColor;
3419 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3421 gc_values.foreground = blackPieceColor;
3422 gc_values.background = jailSquareColor;
3423 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3428 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3436 fp = fopen(filename, "rb");
3438 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3445 for (y=0; y<h; ++y) {
3446 for (x=0; x<h; ++x) {
3451 XPutPixel(xim, x, y, blackPieceColor);
3453 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3456 XPutPixel(xim, x, y, darkSquareColor);
3458 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3461 XPutPixel(xim, x, y, whitePieceColor);
3463 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3466 XPutPixel(xim, x, y, lightSquareColor);
3468 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3476 /* create Pixmap of piece */
3477 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3479 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3482 /* create Pixmap of clipmask
3483 Note: We assume the white/black pieces have the same
3484 outline, so we make only 6 masks. This is okay
3485 since the XPM clipmask routines do the same. */
3487 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3489 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3492 /* now create the 1-bit version */
3493 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3496 values.foreground = 1;
3497 values.background = 0;
3499 /* Don't use XtGetGC, not read only */
3500 maskGC = XCreateGC(xDisplay, *mask,
3501 GCForeground | GCBackground, &values);
3502 XCopyPlane(xDisplay, temp, *mask, maskGC,
3503 0, 0, squareSize, squareSize, 0, 0, 1);
3504 XFreePixmap(xDisplay, temp);
3509 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3517 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3522 /* The XSynchronize calls were copied from CreatePieces.
3523 Not sure if needed, but can't hurt */
3524 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3527 /* temp needed by loadXIM() */
3528 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3529 0, 0, ss, ss, AllPlanes, XYPixmap);
3531 if (strlen(appData.pixmapDirectory) == 0) {
3535 if (appData.monoMode) {
3536 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3540 fprintf(stderr, _("\nLoading XIMs...\n"));
3542 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3543 fprintf(stderr, "%d", piece+1);
3544 for (kind=0; kind<4; kind++) {
3545 fprintf(stderr, ".");
3546 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3547 ExpandPathName(appData.pixmapDirectory),
3548 piece <= (int) WhiteKing ? "" : "w",
3549 pieceBitmapNames[piece],
3551 ximPieceBitmap[kind][piece] =
3552 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3553 0, 0, ss, ss, AllPlanes, XYPixmap);
3554 if (appData.debugMode)
3555 fprintf(stderr, _("(File:%s:) "), buf);
3556 loadXIM(ximPieceBitmap[kind][piece],
3558 &(xpmPieceBitmap2[kind][piece]),
3559 &(ximMaskPm2[piece]));
3560 if(piece <= (int)WhiteKing)
3561 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3563 fprintf(stderr," ");
3565 /* Load light and dark squares */
3566 /* If the LSQ and DSQ pieces don't exist, we will
3567 draw them with solid squares. */
3568 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3569 if (access(buf, 0) != 0) {
3573 fprintf(stderr, _("light square "));
3575 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3576 0, 0, ss, ss, AllPlanes, XYPixmap);
3577 if (appData.debugMode)
3578 fprintf(stderr, _("(File:%s:) "), buf);
3580 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3581 fprintf(stderr, _("dark square "));
3582 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3583 ExpandPathName(appData.pixmapDirectory), ss);
3584 if (appData.debugMode)
3585 fprintf(stderr, _("(File:%s:) "), buf);
3587 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3588 0, 0, ss, ss, AllPlanes, XYPixmap);
3589 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3590 xpmJailSquare = xpmLightSquare;
3592 fprintf(stderr, _("Done.\n"));
3594 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3597 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3601 CreateXPMBoard (char *s, int kind)
3605 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3606 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3607 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3613 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3614 // thisroutine has to be called t free the old piece pixmaps
3616 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3617 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3619 XFreePixmap(xDisplay, xpmLightSquare);
3620 XFreePixmap(xDisplay, xpmDarkSquare);
3629 u_int ss = squareSize;
3631 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3632 XpmColorSymbol symbols[4];
3633 static int redo = False;
3635 if(redo) FreeXPMPieces(); else redo = 1;
3637 /* The XSynchronize calls were copied from CreatePieces.
3638 Not sure if needed, but can't hurt */
3639 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3641 /* Setup translations so piece colors match square colors */
3642 symbols[0].name = "light_piece";
3643 symbols[0].value = appData.whitePieceColor;
3644 symbols[1].name = "dark_piece";
3645 symbols[1].value = appData.blackPieceColor;
3646 symbols[2].name = "light_square";
3647 symbols[2].value = appData.lightSquareColor;
3648 symbols[3].name = "dark_square";
3649 symbols[3].value = appData.darkSquareColor;
3651 attr.valuemask = XpmColorSymbols;
3652 attr.colorsymbols = symbols;
3653 attr.numsymbols = 4;
3655 if (appData.monoMode) {
3656 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3660 if (strlen(appData.pixmapDirectory) == 0) {
3661 XpmPieces* pieces = builtInXpms;
3664 while (pieces->size != squareSize && pieces->size) pieces++;
3665 if (!pieces->size) {
3666 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3669 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3670 for (kind=0; kind<4; kind++) {
3672 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3673 pieces->xpm[piece][kind],
3674 &(xpmPieceBitmap2[kind][piece]),
3675 NULL, &attr)) != 0) {
3676 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3680 if(piece <= (int) WhiteKing)
3681 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3685 xpmJailSquare = xpmLightSquare;
3689 fprintf(stderr, _("\nLoading XPMs...\n"));
3692 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3693 fprintf(stderr, "%d ", piece+1);
3694 for (kind=0; kind<4; kind++) {
3695 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3696 ExpandPathName(appData.pixmapDirectory),
3697 piece > (int) WhiteKing ? "w" : "",
3698 pieceBitmapNames[piece],
3700 if (appData.debugMode) {
3701 fprintf(stderr, _("(File:%s:) "), buf);
3703 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3704 &(xpmPieceBitmap2[kind][piece]),
3705 NULL, &attr)) != 0) {
3706 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3707 // [HGM] missing: read of unorthodox piece failed; substitute King.
3708 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3709 ExpandPathName(appData.pixmapDirectory),
3711 if (appData.debugMode) {
3712 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3714 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3715 &(xpmPieceBitmap2[kind][piece]),
3719 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3724 if(piece <= (int) WhiteKing)
3725 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3728 /* Load light and dark squares */
3729 /* If the LSQ and DSQ pieces don't exist, we will
3730 draw them with solid squares. */
3731 fprintf(stderr, _("light square "));
3732 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3733 if (access(buf, 0) != 0) {
3737 if (appData.debugMode)
3738 fprintf(stderr, _("(File:%s:) "), buf);
3740 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3741 &xpmLightSquare, NULL, &attr)) != 0) {
3742 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3745 fprintf(stderr, _("dark square "));
3746 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3747 ExpandPathName(appData.pixmapDirectory), ss);
3748 if (appData.debugMode) {
3749 fprintf(stderr, _("(File:%s:) "), buf);
3751 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3752 &xpmDarkSquare, NULL, &attr)) != 0) {
3753 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3757 xpmJailSquare = xpmLightSquare;
3758 fprintf(stderr, _("Done.\n"));
3760 oldVariant = -1; // kludge to force re-makig of animation masks
3761 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3764 #endif /* HAVE_LIBXPM */
3767 /* No built-in bitmaps */
3772 u_int ss = squareSize;
3774 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3777 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3778 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3779 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3780 pieceBitmapNames[piece],
3781 ss, kind == SOLID ? 's' : 'o');
3782 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3783 if(piece <= (int)WhiteKing)
3784 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3788 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3792 /* With built-in bitmaps */
3796 BuiltInBits* bib = builtInBits;
3799 u_int ss = squareSize;
3801 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3804 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3806 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3807 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3808 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3809 pieceBitmapNames[piece],
3810 ss, kind == SOLID ? 's' : 'o');
3811 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3812 bib->bits[kind][piece], ss, ss);
3813 if(piece <= (int)WhiteKing)
3814 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3818 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3824 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3829 char msg[MSG_SIZ], fullname[MSG_SIZ];
3831 if (*appData.bitmapDirectory != NULLCHAR) {
3832 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3833 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3834 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3835 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3836 &w, &h, pm, &x_hot, &y_hot);
3837 fprintf(stderr, "load %s\n", name);
3838 if (errcode != BitmapSuccess) {
3840 case BitmapOpenFailed:
3841 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3843 case BitmapFileInvalid:
3844 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3846 case BitmapNoMemory:
3847 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3851 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3855 fprintf(stderr, _("%s: %s...using built-in\n"),
3857 } else if (w != wreq || h != hreq) {
3859 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3860 programName, fullname, w, h, wreq, hreq);
3866 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3876 if (lineGap == 0) return;
3878 /* [HR] Split this into 2 loops for non-square boards. */
3880 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3881 gridSegments[i].x1 = 0;
3882 gridSegments[i].x2 =
3883 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3884 gridSegments[i].y1 = gridSegments[i].y2
3885 = lineGap / 2 + (i * (squareSize + lineGap));
3888 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3889 gridSegments[j + i].y1 = 0;
3890 gridSegments[j + i].y2 =
3891 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3892 gridSegments[j + i].x1 = gridSegments[j + i].x2
3893 = lineGap / 2 + (j * (squareSize + lineGap));
3898 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3900 XtActionProc proc = (XtActionProc) addr;
3902 (proc)(NULL, NULL, NULL, NULL);
3906 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3908 RecentEngineEvent((int) addr);
3912 AppendEnginesToMenu (Widget menu, char *list)
3920 if(appData.recentEngines <= 0) return;
3921 recentEngines = strdup(list);
3923 XtSetArg(args[j], XtNleftMargin, 20); j++;
3924 XtSetArg(args[j], XtNrightMargin, 20); j++;
3926 p = strchr(list, '\n'); if(p == NULL) break;
3927 if(i == 0) XtCreateManagedWidget(_("----"), smeLineObjectClass, menu, args, j); // at least one valid item to add
3929 XtSetArg(args[j], XtNlabel, XtNewString(list));
3930 entry = XtCreateManagedWidget("engine", smeBSBObjectClass, menu, args, j+1);
3931 XtAddCallback(entry, XtNcallback,
3932 (XtCallbackProc) MenuEngineSelect,
3934 i++; *p = '\n'; list = p + 1;
3939 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3946 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3949 XtSetArg(args[j], XtNleftMargin, 20); j++;
3950 XtSetArg(args[j], XtNrightMargin, 20); j++;
3952 while (mi->string != NULL) {
3953 if (strcmp(mi->string, "----") == 0) {
3954 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3957 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3958 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3960 XtAddCallback(entry, XtNcallback,
3961 (XtCallbackProc) MenuBarSelect,
3962 (caddr_t) mi->proc);
3966 if(!strcmp(mb->name, "Engine")) AppendEnginesToMenu(menu, appData.recentEngineList);
3970 CreateMenuBar (Menu *mb, int boardWidth)
3972 int i, j, nr = 0, wtot = 0, widths[10];
3975 char menuName[MSG_SIZ];
3980 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3981 XtSetArg(args[j], XtNvSpace, 0); j++;
3982 XtSetArg(args[j], XtNborderWidth, 0); j++;
3983 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3984 formWidget, args, j);
3986 while (mb->name != NULL) {
3987 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3988 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3990 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3991 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3992 XtSetArg(args[j], XtNborderWidth, 0); j++;
3993 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3995 CreateMenuBarPopup(menuBar, menuName, mb);
3997 XtSetArg(args[j], XtNwidth, &w); j++;
3998 XtGetValues(mb->subMenu, args, j);
3999 wtot += mb->textWidth = widths[nr++] = w;
4002 while(wtot > boardWidth - 40) {
4004 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
4008 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
4010 XtSetArg(args[j], XtNwidth, widths[i]); j++;
4011 XtSetValues(ma[i].subMenu, args, j);
4017 CreateButtonBar (MenuItem *mi)
4020 Widget button, buttonBar;
4024 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4026 XtSetArg(args[j], XtNhSpace, 0); j++;
4028 XtSetArg(args[j], XtNborderWidth, 0); j++;
4029 XtSetArg(args[j], XtNvSpace, 0); j++;
4030 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4031 formWidget, args, j);
4033 while (mi->string != NULL) {
4036 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4037 XtSetArg(args[j], XtNborderWidth, 0); j++;
4039 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4040 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4041 buttonBar, args, j);
4042 XtAddCallback(button, XtNcallback,
4043 (XtCallbackProc) MenuBarSelect,
4044 (caddr_t) mi->proc);
4051 CreatePieceMenu (char *name, int color)
4056 ChessSquare selection;
4058 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4059 boardWidget, args, 0);
4061 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4062 String item = pieceMenuStrings[color][i];
4064 if (strcmp(item, "----") == 0) {
4065 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4068 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4069 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4071 selection = pieceMenuTranslation[color][i];
4072 XtAddCallback(entry, XtNcallback,
4073 (XtCallbackProc) PieceMenuSelect,
4074 (caddr_t) selection);
4075 if (selection == WhitePawn || selection == BlackPawn) {
4076 XtSetArg(args[0], XtNpopupOnEntry, entry);
4077 XtSetValues(menu, args, 1);
4090 ChessSquare selection;
4092 whitePieceMenu = CreatePieceMenu("menuW", 0);
4093 blackPieceMenu = CreatePieceMenu("menuB", 1);
4095 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
4096 XtRegisterGrabAction(PieceMenuPopup, True,
4097 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4098 GrabModeAsync, GrabModeAsync);
4100 XtSetArg(args[0], XtNlabel, _("Drop"));
4101 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4102 boardWidget, args, 1);
4103 for (i = 0; i < DROP_MENU_SIZE; i++) {
4104 String item = dropMenuStrings[i];
4106 if (strcmp(item, "----") == 0) {
4107 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4110 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4111 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4113 selection = dropMenuTranslation[i];
4114 XtAddCallback(entry, XtNcallback,
4115 (XtCallbackProc) DropMenuSelect,
4116 (caddr_t) selection);
4130 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4131 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4132 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4133 dmEnables[i].piece);
4134 XtSetSensitive(entry, p != NULL || !appData.testLegality
4135 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4136 && !appData.icsActive));
4138 while (p && *p++ == dmEnables[i].piece) count++;
4139 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4141 XtSetArg(args[j], XtNlabel, label); j++;
4142 XtSetValues(entry, args, j);
4147 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4149 String whichMenu; int menuNr = -2;
4150 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4151 if (event->type == ButtonRelease)
4152 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4153 else if (event->type == ButtonPress)
4154 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4156 case 0: whichMenu = params[0]; break;
4157 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4159 case -1: if (errorUp) ErrorPopDown();
4162 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4166 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4168 if (pmFromX < 0 || pmFromY < 0) return;
4169 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4173 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4175 if (pmFromX < 0 || pmFromY < 0) return;
4176 DropMenuEvent(piece, pmFromX, pmFromY);
4180 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4182 shiftKey = prms[0][0] & 1;
4187 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4189 shiftKey = prms[0][0] & 1;
4195 * If the user selects on a border boundary, return -1; if off the board,
4196 * return -2. Otherwise map the event coordinate to the square.
4199 EventToSquare (int x, int limit)
4206 if ((x % (squareSize + lineGap)) >= squareSize)
4208 x /= (squareSize + lineGap);
4215 do_flash_delay (unsigned long msec)
4221 drawHighlight (int file, int rank, GC gc)
4225 if (lineGap == 0) return;
4228 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4229 (squareSize + lineGap);
4230 y = lineGap/2 + rank * (squareSize + lineGap);
4232 x = lineGap/2 + file * (squareSize + lineGap);
4233 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4234 (squareSize + lineGap);
4237 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4238 squareSize+lineGap, squareSize+lineGap);
4241 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4242 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4245 SetHighlights (int fromX, int fromY, int toX, int toY)
4247 if (hi1X != fromX || hi1Y != fromY) {
4248 if (hi1X >= 0 && hi1Y >= 0) {
4249 drawHighlight(hi1X, hi1Y, lineGC);
4251 } // [HGM] first erase both, then draw new!
4252 if (hi2X != toX || hi2Y != toY) {
4253 if (hi2X >= 0 && hi2Y >= 0) {
4254 drawHighlight(hi2X, hi2Y, lineGC);
4257 if (hi1X != fromX || hi1Y != fromY) {
4258 if (fromX >= 0 && fromY >= 0) {
4259 drawHighlight(fromX, fromY, highlineGC);
4262 if (hi2X != toX || hi2Y != toY) {
4263 if (toX >= 0 && toY >= 0) {
4264 drawHighlight(toX, toY, highlineGC);
4267 if(toX<0) // clearing the highlights must have damaged arrow
4268 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4278 SetHighlights(-1, -1, -1, -1);
4283 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4285 if (pm1X != fromX || pm1Y != fromY) {
4286 if (pm1X >= 0 && pm1Y >= 0) {
4287 drawHighlight(pm1X, pm1Y, lineGC);
4289 if (fromX >= 0 && fromY >= 0) {
4290 drawHighlight(fromX, fromY, prelineGC);
4293 if (pm2X != toX || pm2Y != toY) {
4294 if (pm2X >= 0 && pm2Y >= 0) {
4295 drawHighlight(pm2X, pm2Y, lineGC);
4297 if (toX >= 0 && toY >= 0) {
4298 drawHighlight(toX, toY, prelineGC);
4308 ClearPremoveHighlights ()
4310 SetPremoveHighlights(-1, -1, -1, -1);
4314 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4316 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4317 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4319 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4320 if(textureW[kind] < W*squareSize)
4321 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4323 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4324 if(textureH[kind] < H*squareSize)
4325 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4327 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4332 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4333 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4335 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4336 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4337 squareSize, squareSize, x*fac, y*fac);
4339 if (useImages && useImageSqs) {
4343 pm = xpmLightSquare;
4348 case 2: /* neutral */
4353 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4354 squareSize, squareSize, x*fac, y*fac);
4364 case 2: /* neutral */
4369 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4374 I split out the routines to draw a piece so that I could
4375 make a generic flash routine.
4378 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4380 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4381 switch (square_color) {
4383 case 2: /* neutral */
4385 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4386 ? *pieceToOutline(piece)
4387 : *pieceToSolid(piece),
4388 dest, bwPieceGC, 0, 0,
4389 squareSize, squareSize, x, y);
4392 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4393 ? *pieceToSolid(piece)
4394 : *pieceToOutline(piece),
4395 dest, wbPieceGC, 0, 0,
4396 squareSize, squareSize, x, y);
4402 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4404 switch (square_color) {
4406 case 2: /* neutral */
4408 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4409 ? *pieceToOutline(piece)
4410 : *pieceToSolid(piece),
4411 dest, bwPieceGC, 0, 0,
4412 squareSize, squareSize, x, y, 1);
4415 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4416 ? *pieceToSolid(piece)
4417 : *pieceToOutline(piece),
4418 dest, wbPieceGC, 0, 0,
4419 squareSize, squareSize, x, y, 1);
4425 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4427 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4428 switch (square_color) {
4430 XCopyPlane(xDisplay, *pieceToSolid(piece),
4431 dest, (int) piece < (int) BlackPawn
4432 ? wlPieceGC : blPieceGC, 0, 0,
4433 squareSize, squareSize, x, y, 1);
4436 XCopyPlane(xDisplay, *pieceToSolid(piece),
4437 dest, (int) piece < (int) BlackPawn
4438 ? wdPieceGC : bdPieceGC, 0, 0,
4439 squareSize, squareSize, x, y, 1);
4441 case 2: /* neutral */
4443 XCopyPlane(xDisplay, *pieceToSolid(piece),
4444 dest, (int) piece < (int) BlackPawn
4445 ? wjPieceGC : bjPieceGC, 0, 0,
4446 squareSize, squareSize, x, y, 1);
4452 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4454 int kind, p = piece;
4456 switch (square_color) {
4458 case 2: /* neutral */
4460 if ((int)piece < (int) BlackPawn) {
4468 if ((int)piece < (int) BlackPawn) {
4476 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4477 if(useTexture & square_color+1) {
4478 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4479 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4480 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4481 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4482 XSetClipMask(xDisplay, wlPieceGC, None);
4483 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4485 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4486 dest, wlPieceGC, 0, 0,
4487 squareSize, squareSize, x, y);
4490 typedef void (*DrawFunc)();
4495 if (appData.monoMode) {
4496 if (DefaultDepth(xDisplay, xScreen) == 1) {
4497 return monoDrawPiece_1bit;
4499 return monoDrawPiece;
4503 return colorDrawPieceImage;
4505 return colorDrawPiece;
4509 /* [HR] determine square color depending on chess variant. */
4511 SquareColor (int row, int column)
4515 if (gameInfo.variant == VariantXiangqi) {
4516 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4518 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4520 } else if (row <= 4) {
4526 square_color = ((column + row) % 2) == 1;
4529 /* [hgm] holdings: next line makes all holdings squares light */
4530 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4532 return square_color;
4536 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4538 int square_color, x, y, direction, font_ascent, font_descent;
4541 XCharStruct overall;
4545 /* Calculate delay in milliseconds (2-delays per complete flash) */
4546 flash_delay = 500 / appData.flashRate;
4549 x = lineGap + ((BOARD_WIDTH-1)-column) *
4550 (squareSize + lineGap);
4551 y = lineGap + row * (squareSize + lineGap);
4553 x = lineGap + column * (squareSize + lineGap);
4554 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4555 (squareSize + lineGap);
4558 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4560 square_color = SquareColor(row, column);
4562 if ( // [HGM] holdings: blank out area between board and holdings
4563 column == BOARD_LEFT-1 || column == BOARD_RGHT
4564 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4565 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4566 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4568 // [HGM] print piece counts next to holdings
4569 string[1] = NULLCHAR;
4570 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4571 string[0] = '0' + piece;
4572 XTextExtents(countFontStruct, string, 1, &direction,
4573 &font_ascent, &font_descent, &overall);
4574 if (appData.monoMode) {
4575 XDrawImageString(xDisplay, xBoardWindow, countGC,
4576 x + squareSize - overall.width - 2,
4577 y + font_ascent + 1, string, 1);
4579 XDrawString(xDisplay, xBoardWindow, countGC,
4580 x + squareSize - overall.width - 2,
4581 y + font_ascent + 1, string, 1);
4584 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4585 string[0] = '0' + piece;
4586 XTextExtents(countFontStruct, string, 1, &direction,
4587 &font_ascent, &font_descent, &overall);
4588 if (appData.monoMode) {
4589 XDrawImageString(xDisplay, xBoardWindow, countGC,
4590 x + 2, y + font_ascent + 1, string, 1);
4592 XDrawString(xDisplay, xBoardWindow, countGC,
4593 x + 2, y + font_ascent + 1, string, 1);
4597 if (piece == EmptySquare || appData.blindfold) {
4598 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4600 drawfunc = ChooseDrawFunc();
4602 if (do_flash && appData.flashCount > 0) {
4603 for (i=0; i<appData.flashCount; ++i) {
4604 drawfunc(piece, square_color, x, y, xBoardWindow);
4605 XSync(xDisplay, False);
4606 do_flash_delay(flash_delay);
4608 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4609 XSync(xDisplay, False);
4610 do_flash_delay(flash_delay);
4613 drawfunc(piece, square_color, x, y, xBoardWindow);
4617 string[1] = NULLCHAR;
4618 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4619 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4620 string[0] = 'a' + column - BOARD_LEFT;
4621 XTextExtents(coordFontStruct, string, 1, &direction,
4622 &font_ascent, &font_descent, &overall);
4623 if (appData.monoMode) {
4624 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4625 x + squareSize - overall.width - 2,
4626 y + squareSize - font_descent - 1, string, 1);
4628 XDrawString(xDisplay, xBoardWindow, coordGC,
4629 x + squareSize - overall.width - 2,
4630 y + squareSize - font_descent - 1, string, 1);
4633 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4634 string[0] = ONE + row;
4635 XTextExtents(coordFontStruct, string, 1, &direction,
4636 &font_ascent, &font_descent, &overall);
4637 if (appData.monoMode) {
4638 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4639 x + 2, y + font_ascent + 1, string, 1);
4641 XDrawString(xDisplay, xBoardWindow, coordGC,
4642 x + 2, y + font_ascent + 1, string, 1);
4645 if(!partnerUp && marker[row][column]) {
4646 if(appData.monoMode) {
4647 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4648 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4649 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4650 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4652 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4653 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4658 Fraction (int x, int start, int stop)
4660 double f = ((double) x - start)/(stop - start);
4661 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
4665 static WindowPlacement wpNew;
4668 CoDrag (Widget sh, WindowPlacement *wp)
4671 int j=0, touch=0, fudge = 2;
4672 GetActualPlacement(sh, wp);
4673 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
4674 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
4675 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
4676 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
4677 if(!touch ) return; // only windows that touch co-move
4678 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
4679 int heightInc = wpNew.height - wpMain.height;
4680 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
4681 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
4682 wp->y += fracTop * heightInc;
4683 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
4684 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
4685 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
4686 int widthInc = wpNew.width - wpMain.width;
4687 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
4688 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
4689 wp->y += fracLeft * widthInc;
4690 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
4691 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
4693 wp->x += wpNew.x - wpMain.x;
4694 wp->y += wpNew.y - wpMain.y;
4695 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
4696 if(touch == 3) wp->y += wpNew.height - wpMain.height;
4697 XtSetArg(args[j], XtNx, wp->x); j++;
4698 XtSetArg(args[j], XtNy, wp->y); j++;
4699 XtSetValues(sh, args, j);
4705 GetActualPlacement(shellWidget, &wpNew);
4706 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
4707 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
4708 return; // false alarm
4709 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
4710 if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
4711 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
4712 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
4714 XDrawPosition(boardWidget, True, NULL);
4721 static XtIntervalId delayedDragID = 0;
4722 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
4724 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
4727 /* Why is this needed on some versions of X? */
4729 EventProc (Widget widget, caddr_t unused, XEvent *event)
4731 if (!XtIsRealized(widget))
4733 switch (event->type) {
4734 case ConfigureNotify: // main window is being dragged: drag attached windows with it
4735 if(appData.useStickyWindows)
4736 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
4739 if (event->xexpose.count > 0) return; /* no clipping is done */
4740 XDrawPosition(widget, True, NULL);
4741 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4742 flipView = !flipView; partnerUp = !partnerUp;
4743 XDrawPosition(widget, True, NULL);
4744 flipView = !flipView; partnerUp = !partnerUp;
4748 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4756 DrawPosition (int fullRedraw, Board board)
4758 XDrawPosition(boardWidget, fullRedraw, board);
4761 /* Returns 1 if there are "too many" differences between b1 and b2
4762 (i.e. more than 1 move was made) */
4764 too_many_diffs (Board b1, Board b2)
4769 for (i=0; i<BOARD_HEIGHT; ++i) {
4770 for (j=0; j<BOARD_WIDTH; ++j) {
4771 if (b1[i][j] != b2[i][j]) {
4772 if (++c > 4) /* Castling causes 4 diffs */
4780 /* Matrix describing castling maneuvers */
4781 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4782 static int castling_matrix[4][5] = {
4783 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4784 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4785 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4786 { 7, 7, 4, 5, 6 } /* 0-0, black */
4789 /* Checks whether castling occurred. If it did, *rrow and *rcol
4790 are set to the destination (row,col) of the rook that moved.
4792 Returns 1 if castling occurred, 0 if not.
4794 Note: Only handles a max of 1 castling move, so be sure
4795 to call too_many_diffs() first.
4798 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4803 /* For each type of castling... */
4804 for (i=0; i<4; ++i) {
4805 r = castling_matrix[i];
4807 /* Check the 4 squares involved in the castling move */
4809 for (j=1; j<=4; ++j) {
4810 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4817 /* All 4 changed, so it must be a castling move */
4826 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4828 DrawSeekAxis (int x, int y, int xTo, int yTo)
4830 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4834 DrawSeekBackground (int left, int top, int right, int bottom)
4836 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4840 DrawSeekText (char *buf, int x, int y)
4842 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4846 DrawSeekDot (int x, int y, int colorNr)
4848 int square = colorNr & 0x80;
4851 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4853 XFillRectangle(xDisplay, xBoardWindow, color,
4854 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4856 XFillArc(xDisplay, xBoardWindow, color,
4857 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4860 static int damage[2][BOARD_RANKS][BOARD_FILES];
4863 * event handler for redrawing the board
4866 XDrawPosition (Widget w, int repaint, Board board)
4869 static int lastFlipView = 0;
4870 static int lastBoardValid[2] = {0, 0};
4871 static Board lastBoard[2];
4874 int nr = twoBoards*partnerUp;
4876 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4878 if (board == NULL) {
4879 if (!lastBoardValid[nr]) return;
4880 board = lastBoard[nr];
4882 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4883 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4884 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4889 * It would be simpler to clear the window with XClearWindow()
4890 * but this causes a very distracting flicker.
4893 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4895 if ( lineGap && IsDrawArrowEnabled())
4896 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4897 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4899 /* If too much changes (begin observing new game, etc.), don't
4901 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4903 /* Special check for castling so we don't flash both the king
4904 and the rook (just flash the king). */
4906 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4907 /* Draw rook with NO flashing. King will be drawn flashing later */
4908 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4909 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4913 /* First pass -- Draw (newly) empty squares and repair damage.
4914 This prevents you from having a piece show up twice while it
4915 is flashing on its new square */
4916 for (i = 0; i < BOARD_HEIGHT; i++)
4917 for (j = 0; j < BOARD_WIDTH; j++)
4918 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4919 || damage[nr][i][j]) {
4920 DrawSquare(i, j, board[i][j], 0);
4921 damage[nr][i][j] = False;
4924 /* Second pass -- Draw piece(s) in new position and flash them */
4925 for (i = 0; i < BOARD_HEIGHT; i++)
4926 for (j = 0; j < BOARD_WIDTH; j++)
4927 if (board[i][j] != lastBoard[nr][i][j]) {
4928 DrawSquare(i, j, board[i][j], do_flash);
4932 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4933 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4934 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4936 for (i = 0; i < BOARD_HEIGHT; i++)
4937 for (j = 0; j < BOARD_WIDTH; j++) {
4938 DrawSquare(i, j, board[i][j], 0);
4939 damage[nr][i][j] = False;
4943 CopyBoard(lastBoard[nr], board);
4944 lastBoardValid[nr] = 1;
4945 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4946 lastFlipView = flipView;
4948 /* Draw highlights */
4949 if (pm1X >= 0 && pm1Y >= 0) {
4950 drawHighlight(pm1X, pm1Y, prelineGC);
4952 if (pm2X >= 0 && pm2Y >= 0) {
4953 drawHighlight(pm2X, pm2Y, prelineGC);
4955 if (hi1X >= 0 && hi1Y >= 0) {
4956 drawHighlight(hi1X, hi1Y, highlineGC);
4958 if (hi2X >= 0 && hi2Y >= 0) {
4959 drawHighlight(hi2X, hi2Y, highlineGC);
4961 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4963 /* If piece being dragged around board, must redraw that too */
4966 XSync(xDisplay, False);
4971 * event handler for redrawing the board
4974 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4976 XDrawPosition(w, True, NULL);
4981 * event handler for parsing user moves
4983 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4984 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4985 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4986 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4987 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4988 // and at the end FinishMove() to perform the move after optional promotion popups.
4989 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4991 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4993 if (w != boardWidget || errorExitStatus != -1) return;
4994 if(nprms) shiftKey = !strcmp(prms[0], "1");
4997 if (event->type == ButtonPress) {
4998 XtPopdown(promotionShell);
4999 XtDestroyWidget(promotionShell);
5000 promotionUp = False;
5008 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
5009 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
5010 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
5014 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
5016 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
5017 DragPieceMove(event->xmotion.x, event->xmotion.y);
5021 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
5022 { // [HGM] pv: walk PV
5023 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
5026 static int savedIndex; /* gross that this is global */
5029 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
5032 XawTextPosition index, dummy;
5035 XawTextGetSelectionPos(w, &index, &dummy);
5036 XtSetArg(arg, XtNstring, &val);
5037 XtGetValues(w, &arg, 1);
5038 ReplaceComment(savedIndex, val);
5039 if(savedIndex != currentMove) ToNrEvent(savedIndex);
5040 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
5044 EditCommentPopUp (int index, char *title, char *text)
5047 if (text == NULL) text = "";
5048 NewCommentPopup(title, text, index);
5057 extern Option boxOptions[];
5067 edit = boxOptions[0].handle;
5069 XtSetArg(args[j], XtNstring, &val); j++;
5070 XtGetValues(edit, args, j);
5072 SendMultiLineToICS(val);
5073 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5074 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5078 ICSInputBoxPopDown ()
5084 CommentPopUp (char *title, char *text)
5086 savedIndex = currentMove; // [HGM] vari
5087 NewCommentPopup(title, text, currentMove);
5096 static char *openName;
5102 (void) (*fileProc)(openFP, 0, openName);
5106 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
5108 fileProc = proc; /* I can't see a way not */
5109 fileOpenMode = openMode; /* to use globals here */
5110 { // [HGM] use file-selector dialog stolen from Ghostview
5111 int index; // this is not supported yet
5112 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5113 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5114 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5115 ScheduleDelayedEvent(&DelayedLoad, 50);
5122 if (!filenameUp) return;
5123 XtPopdown(fileNameShell);
5124 XtDestroyWidget(fileNameShell);
5130 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5135 XtSetArg(args[0], XtNlabel, &name);
5136 XtGetValues(w, args, 1);
5138 if (strcmp(name, _("cancel")) == 0) {
5143 FileNameAction(w, NULL, NULL, NULL);
5147 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5155 name = XawDialogGetValueString(w = XtParent(w));
5157 if ((name != NULL) && (*name != NULLCHAR)) {
5158 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5159 XtPopdown(w = XtParent(XtParent(w)));
5163 p = strrchr(buf, ' ');
5170 fullname = ExpandPathName(buf);
5172 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5175 f = fopen(fullname, fileOpenMode);
5177 DisplayError(_("Failed to open file"), errno);
5179 (void) (*fileProc)(f, index, buf);
5186 XtPopdown(w = XtParent(XtParent(w)));
5196 Widget dialog, layout;
5198 Dimension bw_width, pw_width;
5200 char *PromoChars = "wglcqrbnkac+=\0";
5203 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5204 XtGetValues(boardWidget, args, j);
5207 XtSetArg(args[j], XtNresizable, True); j++;
5208 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5210 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5211 shellWidget, args, j);
5213 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5214 layoutArgs, XtNumber(layoutArgs));
5217 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5218 XtSetArg(args[j], XtNborderWidth, 0); j++;
5219 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5222 if(gameInfo.variant != VariantShogi) {
5223 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5224 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5225 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5226 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5227 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5229 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5230 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5231 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5232 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5234 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5235 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5236 gameInfo.variant == VariantGiveaway) {
5237 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5239 if(gameInfo.variant == VariantCapablanca ||
5240 gameInfo.variant == VariantGothic ||
5241 gameInfo.variant == VariantCapaRandom) {
5242 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5243 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5245 } else // [HGM] shogi
5247 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5248 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5250 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5252 XtRealizeWidget(promotionShell);
5253 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5256 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5257 XtGetValues(promotionShell, args, j);
5259 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5260 lineGap + squareSize/3 +
5261 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5262 0 : 6*(squareSize + lineGap)), &x, &y);
5265 XtSetArg(args[j], XtNx, x); j++;
5266 XtSetArg(args[j], XtNy, y); j++;
5267 XtSetValues(promotionShell, args, j);
5269 XtPopup(promotionShell, XtGrabNone);
5277 if (!promotionUp) return;
5278 XtPopdown(promotionShell);
5279 XtDestroyWidget(promotionShell);
5280 promotionUp = False;
5284 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5286 int promoChar = * (const char *) client_data;
5290 if (fromX == -1) return;
5297 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5299 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5300 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5306 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5308 dialogError = errorUp = False;
5309 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5311 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5318 if (!errorUp) return;
5319 dialogError = errorUp = False;
5320 XtPopdown(errorShell);
5321 XtDestroyWidget(errorShell);
5322 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5326 ErrorPopUp (char *title, char *label, int modal)
5329 Widget dialog, layout;
5333 Dimension bw_width, pw_width;
5334 Dimension pw_height;
5338 XtSetArg(args[i], XtNresizable, True); i++;
5339 XtSetArg(args[i], XtNtitle, title); i++;
5341 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5342 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
5344 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5345 layoutArgs, XtNumber(layoutArgs));
5348 XtSetArg(args[i], XtNlabel, label); i++;
5349 XtSetArg(args[i], XtNborderWidth, 0); i++;
5350 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5353 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5355 XtRealizeWidget(errorShell);
5356 CatchDeleteWindow(errorShell, "ErrorPopDown");
5359 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5360 XtGetValues(boardWidget, args, i);
5362 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5363 XtSetArg(args[i], XtNheight, &pw_height); i++;
5364 XtGetValues(errorShell, args, i);
5367 /* This code seems to tickle an X bug if it is executed too soon
5368 after xboard starts up. The coordinates get transformed as if
5369 the main window was positioned at (0, 0).
5371 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5372 0 - pw_height + squareSize / 3, &x, &y);
5374 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5375 RootWindowOfScreen(XtScreen(boardWidget)),
5376 (bw_width - pw_width) / 2,
5377 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5381 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5384 XtSetArg(args[i], XtNx, x); i++;
5385 XtSetArg(args[i], XtNy, y); i++;
5386 XtSetValues(errorShell, args, i);
5389 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5392 /* Disable all user input other than deleting the window */
5393 static int frozen = 0;
5399 /* Grab by a widget that doesn't accept input */
5400 XtAddGrab(messageWidget, TRUE, FALSE);
5404 /* Undo a FreezeUI */
5408 if (!frozen) return;
5409 XtRemoveGrab(messageWidget);
5414 ModeToWidgetName (GameMode mode)
5417 case BeginningOfGame:
5418 if (appData.icsActive)
5419 return "menuMode.ICS Client";
5420 else if (appData.noChessProgram ||
5421 *appData.cmailGameName != NULLCHAR)
5422 return "menuMode.Edit Game";
5424 return "menuMode.Machine Black";
5425 case MachinePlaysBlack:
5426 return "menuMode.Machine Black";
5427 case MachinePlaysWhite:
5428 return "menuMode.Machine White";
5430 return "menuMode.Analysis Mode";
5432 return "menuMode.Analyze File";
5433 case TwoMachinesPlay:
5434 return "menuMode.Two Machines";
5436 return "menuMode.Edit Game";
5437 case PlayFromGameFile:
5438 return "menuFile.Load Game";
5440 return "menuMode.Edit Position";
5442 return "menuMode.Training";
5443 case IcsPlayingWhite:
5444 case IcsPlayingBlack:
5448 return "menuMode.ICS Client";
5459 static int oldPausing = FALSE;
5460 static GameMode oldmode = (GameMode) -1;
5463 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5465 if (pausing != oldPausing) {
5466 oldPausing = pausing;
5468 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5470 XtSetArg(args[0], XtNleftBitmap, None);
5472 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5475 if (appData.showButtonBar) {
5476 /* Always toggle, don't set. Previous code messes up when
5477 invoked while the button is pressed, as releasing it
5478 toggles the state again. */
5481 XtSetArg(args[0], XtNbackground, &oldbg);
5482 XtSetArg(args[1], XtNforeground, &oldfg);
5483 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5485 XtSetArg(args[0], XtNbackground, oldfg);
5486 XtSetArg(args[1], XtNforeground, oldbg);
5488 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5492 wname = ModeToWidgetName(oldmode);
5493 if (wname != NULL) {
5494 XtSetArg(args[0], XtNleftBitmap, None);
5495 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5497 wname = ModeToWidgetName(gameMode);
5498 if (wname != NULL) {
5499 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5500 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5503 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5504 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5506 /* Maybe all the enables should be handled here, not just this one */
5507 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5508 gameMode == Training || gameMode == PlayFromGameFile);
5513 * Button/menu procedures
5516 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5522 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5524 cmailMsgLoaded = FALSE;
5525 if (gameNumber == 0) {
5526 int error = GameListBuild(f);
5528 DisplayError(_("Cannot build game list"), error);
5529 } else if (!ListEmpty(&gameList) &&
5530 ((ListGame *) gameList.tailPred)->number > 1) {
5531 GameListPopUp(f, title);
5537 return LoadGame(f, gameNumber, title, FALSE);
5541 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5543 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5546 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5550 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5556 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5562 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5568 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5574 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5580 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5586 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5588 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5591 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5595 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5597 FileNamePopUp(_("Save game file name?"),
5598 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5599 appData.oldSaveStyle ? ".game" : ".pgn",
5604 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5606 FileNamePopUp(_("Save position file name?"),
5607 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5608 appData.oldSaveStyle ? ".pos" : ".fen",
5613 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5615 ReloadCmailMsgEvent(FALSE);
5619 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5624 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5625 char *selected_fen_position=NULL;
5628 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5629 Atom *type_return, XtPointer *value_return,
5630 unsigned long *length_return, int *format_return)
5632 char *selection_tmp;
5634 if (!selected_fen_position) return False; /* should never happen */
5635 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5636 /* note: since no XtSelectionDoneProc was registered, Xt will
5637 * automatically call XtFree on the value returned. So have to
5638 * make a copy of it allocated with XtMalloc */
5639 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5640 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5642 *value_return=selection_tmp;
5643 *length_return=strlen(selection_tmp);
5644 *type_return=*target;
5645 *format_return = 8; /* bits per byte */
5647 } else if (*target == XA_TARGETS(xDisplay)) {
5648 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5649 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5650 targets_tmp[1] = XA_STRING;
5651 *value_return = targets_tmp;
5652 *type_return = XA_ATOM;
5655 // This code leads to a read of value_return out of bounds on 64-bit systems.
5656 // Other code which I have seen always sets *format_return to 32 independent of
5657 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5658 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5659 *format_return = 8 * sizeof(Atom);
5660 if (*format_return > 32) {
5661 *length_return *= *format_return / 32;
5662 *format_return = 32;
5665 *format_return = 32;
5673 /* note: when called from menu all parameters are NULL, so no clue what the
5674 * Widget which was clicked on was, or what the click event was
5677 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5680 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5681 * have a notion of a position that is selected but not copied.
5682 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5684 if(gameMode == EditPosition) EditPositionDone(TRUE);
5685 if (selected_fen_position) free(selected_fen_position);
5686 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5687 if (!selected_fen_position) return;
5688 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5690 SendPositionSelection,
5691 NULL/* lose_ownership_proc */ ,
5692 NULL/* transfer_done_proc */);
5693 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5695 SendPositionSelection,
5696 NULL/* lose_ownership_proc */ ,
5697 NULL/* transfer_done_proc */);
5701 CopyFENToClipboard ()
5702 { // wrapper to make call from back-end possible
5703 CopyPositionProc(NULL, NULL, NULL, NULL);
5706 /* function called when the data to Paste is ready */
5708 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5709 Atom *type, XtPointer value, unsigned long *len, int *format)
5712 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5713 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5714 EditPositionPasteFEN(fenstr);
5718 /* called when Paste Position button is pressed,
5719 * all parameters will be NULL */
5720 void PastePositionProc(w, event, prms, nprms)
5726 XtGetSelectionValue(menuBarWidget,
5727 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5728 /* (XtSelectionCallbackProc) */ PastePositionCB,
5729 NULL, /* client_data passed to PastePositionCB */
5731 /* better to use the time field from the event that triggered the
5732 * call to this function, but that isn't trivial to get
5740 SendGameSelection (Widget w, Atom *selection, Atom *target,
5741 Atom *type_return, XtPointer *value_return,
5742 unsigned long *length_return, int *format_return)
5744 char *selection_tmp;
5746 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5747 FILE* f = fopen(gameCopyFilename, "r");
5750 if (f == NULL) return False;
5754 selection_tmp = XtMalloc(len + 1);
5755 count = fread(selection_tmp, 1, len, f);
5758 XtFree(selection_tmp);
5761 selection_tmp[len] = NULLCHAR;
5762 *value_return = selection_tmp;
5763 *length_return = len;
5764 *type_return = *target;
5765 *format_return = 8; /* bits per byte */
5767 } else if (*target == XA_TARGETS(xDisplay)) {
5768 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5769 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5770 targets_tmp[1] = XA_STRING;
5771 *value_return = targets_tmp;
5772 *type_return = XA_ATOM;
5775 // This code leads to a read of value_return out of bounds on 64-bit systems.
5776 // Other code which I have seen always sets *format_return to 32 independent of
5777 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5778 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5779 *format_return = 8 * sizeof(Atom);
5780 if (*format_return > 32) {
5781 *length_return *= *format_return / 32;
5782 *format_return = 32;
5785 *format_return = 32;
5797 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5798 * have a notion of a game that is selected but not copied.
5799 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5801 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5804 NULL/* lose_ownership_proc */ ,
5805 NULL/* transfer_done_proc */);
5806 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5809 NULL/* lose_ownership_proc */ ,
5810 NULL/* transfer_done_proc */);
5813 /* note: when called from menu all parameters are NULL, so no clue what the
5814 * Widget which was clicked on was, or what the click event was
5817 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5821 ret = SaveGameToFile(gameCopyFilename, FALSE);
5828 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5830 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5834 /* function called when the data to Paste is ready */
5836 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5837 Atom *type, XtPointer value, unsigned long *len, int *format)
5840 if (value == NULL || *len == 0) {
5841 return; /* nothing had been selected to copy */
5843 f = fopen(gamePasteFilename, "w");
5845 DisplayError(_("Can't open temp file"), errno);
5848 fwrite(value, 1, *len, f);
5851 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5854 /* called when Paste Game button is pressed,
5855 * all parameters will be NULL */
5857 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5859 XtGetSelectionValue(menuBarWidget,
5860 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5861 /* (XtSelectionCallbackProc) */ PasteGameCB,
5862 NULL, /* client_data passed to PasteGameCB */
5864 /* better to use the time field from the event that triggered the
5865 * call to this function, but that isn't trivial to get
5876 SaveGameProc(NULL, NULL, NULL, NULL);
5881 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5887 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5893 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5895 MachineBlackEvent();
5899 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5901 MachineWhiteEvent();
5905 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5909 if (!first.analysisSupport) {
5910 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5911 DisplayError(buf, 0);
5914 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5915 if (appData.icsActive) {
5916 if (gameMode != IcsObserving) {
5917 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5918 DisplayError(buf, 0);
5920 if (appData.icsEngineAnalyze) {
5921 if (appData.debugMode)
5922 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5928 /* if enable, use want disable icsEngineAnalyze */
5929 if (appData.icsEngineAnalyze) {
5934 appData.icsEngineAnalyze = TRUE;
5935 if (appData.debugMode)
5936 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5938 #ifndef OPTIONSDIALOG
5939 if (!appData.showThinking)
5940 ShowThinkingProc(w,event,prms,nprms);
5947 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5949 if (!first.analysisSupport) {
5951 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5952 DisplayError(buf, 0);
5955 // Reset(FALSE, TRUE);
5956 #ifndef OPTIONSDIALOG
5957 if (!appData.showThinking)
5958 ShowThinkingProc(w,event,prms,nprms);
5961 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5962 AnalysisPeriodicEvent(1);
5966 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5972 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5978 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5984 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5990 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5992 EditPositionEvent();
5996 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6002 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6006 if (PopDown(1)) { // popdown succesful
6008 XtSetArg(args[j], XtNleftBitmap, None); j++;
6009 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6010 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6011 } else // was not up
6016 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6018 if (!PopDown(4)) ICSInputBoxPopUp();
6022 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6028 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6034 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6040 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6046 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6052 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6058 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6064 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6070 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6072 UserAdjudicationEvent(+1);
6076 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6078 UserAdjudicationEvent(-1);
6082 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6084 UserAdjudicationEvent(0);
6088 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6090 if (shellUp[4] == True)
6095 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6096 { // [HGM] input: let up-arrow recall previous line from history
6103 if (!shellUp[4]) return;
6104 edit = boxOptions[0].handle;
6106 XtSetArg(args[j], XtNstring, &val); j++;
6107 XtGetValues(edit, args, j);
6108 val = PrevInHistory(val);
6109 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6110 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6112 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6113 XawTextReplace(edit, 0, 0, &t);
6114 XawTextSetInsertionPoint(edit, 9999);
6119 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6120 { // [HGM] input: let down-arrow recall next line from history
6125 if (!shellUp[4]) return;
6126 edit = boxOptions[0].handle;
6127 val = NextInHistory();
6128 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6129 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6131 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6132 XawTextReplace(edit, 0, 0, &t);
6133 XawTextSetInsertionPoint(edit, 9999);
6138 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6140 StopObservingEvent();
6144 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6146 StopExaminingEvent();
6150 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6157 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6164 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6170 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6172 if (!TempBackwardActive) {
6173 TempBackwardActive = True;
6179 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6181 /* Check to see if triggered by a key release event for a repeating key.
6182 * If so the next queued event will be a key press of the same key at the same time */
6183 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6185 XPeekEvent(xDisplay, &next);
6186 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6187 next.xkey.keycode == event->xkey.keycode)
6191 TempBackwardActive = False;
6195 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6201 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6207 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6213 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6219 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6221 TruncateGameEvent();
6225 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6231 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6237 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6239 flipView = !flipView;
6240 DrawPosition(True, NULL);
6244 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6248 PonderNextMoveEvent(!appData.ponderNextMove);
6249 #ifndef OPTIONSDIALOG
6250 if (appData.ponderNextMove) {
6251 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6253 XtSetArg(args[0], XtNleftBitmap, None);
6255 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6260 #ifndef OPTIONSDIALOG
6262 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6266 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6268 if (appData.alwaysPromoteToQueen) {
6269 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6271 XtSetArg(args[0], XtNleftBitmap, None);
6273 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6278 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6282 appData.animateDragging = !appData.animateDragging;
6284 if (appData.animateDragging) {
6285 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6288 XtSetArg(args[0], XtNleftBitmap, None);
6290 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6295 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6299 appData.animate = !appData.animate;
6301 if (appData.animate) {
6302 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6305 XtSetArg(args[0], XtNleftBitmap, None);
6307 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6312 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6316 appData.autoCallFlag = !appData.autoCallFlag;
6318 if (appData.autoCallFlag) {
6319 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6321 XtSetArg(args[0], XtNleftBitmap, None);
6323 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6328 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6332 appData.autoFlipView = !appData.autoFlipView;
6334 if (appData.autoFlipView) {
6335 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6337 XtSetArg(args[0], XtNleftBitmap, None);
6339 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6344 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6348 appData.blindfold = !appData.blindfold;
6350 if (appData.blindfold) {
6351 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6353 XtSetArg(args[0], XtNleftBitmap, None);
6355 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6358 DrawPosition(True, NULL);
6362 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6366 appData.testLegality = !appData.testLegality;
6368 if (appData.testLegality) {
6369 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6371 XtSetArg(args[0], XtNleftBitmap, None);
6373 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6379 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6383 if (appData.flashCount == 0) {
6384 appData.flashCount = 3;
6386 appData.flashCount = -appData.flashCount;
6389 if (appData.flashCount > 0) {
6390 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6392 XtSetArg(args[0], XtNleftBitmap, None);
6394 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6400 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6404 appData.highlightDragging = !appData.highlightDragging;
6406 if (appData.highlightDragging) {
6407 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6409 XtSetArg(args[0], XtNleftBitmap, None);
6411 XtSetValues(XtNameToWidget(menuBarWidget,
6412 "menuOptions.Highlight Dragging"), args, 1);
6417 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6421 appData.highlightLastMove = !appData.highlightLastMove;
6423 if (appData.highlightLastMove) {
6424 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6426 XtSetArg(args[0], XtNleftBitmap, None);
6428 XtSetValues(XtNameToWidget(menuBarWidget,
6429 "menuOptions.Highlight Last Move"), args, 1);
6433 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6437 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6439 if (appData.highlightMoveWithArrow) {
6440 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6442 XtSetArg(args[0], XtNleftBitmap, None);
6444 XtSetValues(XtNameToWidget(menuBarWidget,
6445 "menuOptions.Arrow"), args, 1);
6450 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6454 appData.icsAlarm = !appData.icsAlarm;
6456 if (appData.icsAlarm) {
6457 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6459 XtSetArg(args[0], XtNleftBitmap, None);
6461 XtSetValues(XtNameToWidget(menuBarWidget,
6462 "menuOptions.ICS Alarm"), args, 1);
6467 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6471 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6473 if (appData.ringBellAfterMoves) {
6474 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6476 XtSetArg(args[0], XtNleftBitmap, None);
6478 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6483 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6487 appData.oneClick = !appData.oneClick;
6489 if (appData.oneClick) {
6490 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6492 XtSetArg(args[0], XtNleftBitmap, None);
6494 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6499 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6503 PeriodicUpdatesEvent(!appData.periodicUpdates);
6505 if (appData.periodicUpdates) {
6506 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6508 XtSetArg(args[0], XtNleftBitmap, None);
6510 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6515 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6519 appData.popupExitMessage = !appData.popupExitMessage;
6521 if (appData.popupExitMessage) {
6522 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6524 XtSetArg(args[0], XtNleftBitmap, None);
6526 XtSetValues(XtNameToWidget(menuBarWidget,
6527 "menuOptions.Popup Exit Message"), args, 1);
6531 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6535 appData.popupMoveErrors = !appData.popupMoveErrors;
6537 if (appData.popupMoveErrors) {
6538 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6540 XtSetArg(args[0], XtNleftBitmap, None);
6542 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6548 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6552 appData.premove = !appData.premove;
6554 if (appData.premove) {
6555 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6557 XtSetArg(args[0], XtNleftBitmap, None);
6559 XtSetValues(XtNameToWidget(menuBarWidget,
6560 "menuOptions.Premove"), args, 1);
6565 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6569 appData.showCoords = !appData.showCoords;
6571 if (appData.showCoords) {
6572 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6574 XtSetArg(args[0], XtNleftBitmap, None);
6576 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6579 DrawPosition(True, NULL);
6583 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6585 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6586 ShowThinkingEvent();
6590 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6594 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6595 ShowThinkingEvent();
6597 if (appData.hideThinkingFromHuman) {
6598 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6600 XtSetArg(args[0], XtNleftBitmap, None);
6602 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6608 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6612 saveSettingsOnExit = !saveSettingsOnExit;
6614 if (saveSettingsOnExit) {
6615 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6617 XtSetArg(args[0], XtNleftBitmap, None);
6619 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6624 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6626 SaveSettings(settingsFileName);
6630 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6633 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6639 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6643 if (nprms && *nprms > 0)
6647 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6652 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6658 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6664 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6668 char *zippy = _(" (with Zippy code)");
6672 snprintf(buf, sizeof(buf),
6674 "Copyright 1991 Digital Equipment Corporation\n"
6675 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6676 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6677 "%s is free software and carries NO WARRANTY;"
6678 "see the file COPYING for more information."),
6679 programVersion, zippy, PACKAGE);
6680 ErrorPopUp(_("About XBoard"), buf, FALSE);
6684 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6686 appData.debugMode = !appData.debugMode;
6690 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6696 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6702 DisplayMessage (char *message, char *extMessage)
6704 /* display a message in the message widget */
6713 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6718 message = extMessage;
6722 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6724 /* need to test if messageWidget already exists, since this function
6725 can also be called during the startup, if for example a Xresource
6726 is not set up correctly */
6729 XtSetArg(arg, XtNlabel, message);
6730 XtSetValues(messageWidget, &arg, 1);
6737 DisplayTitle (char *text)
6741 char title[MSG_SIZ];
6744 if (text == NULL) text = "";
6746 if (appData.titleInWindow) {
6748 XtSetArg(args[i], XtNlabel, text); i++;
6749 XtSetValues(titleWidget, args, i);
6752 if (*text != NULLCHAR) {
6753 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6754 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6755 } else if (appData.icsActive) {
6756 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6757 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6758 } else if (appData.cmailGameName[0] != NULLCHAR) {
6759 snprintf(icon, sizeof(icon), "%s", "CMail");
6760 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6762 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6763 } else if (gameInfo.variant == VariantGothic) {
6764 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6765 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6768 } else if (gameInfo.variant == VariantFalcon) {
6769 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6770 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6772 } else if (appData.noChessProgram) {
6773 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6774 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6776 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6777 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6780 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6781 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6782 XtSetValues(shellWidget, args, i);
6783 XSync(xDisplay, False);
6788 DisplayError (String message, int error)
6793 if (appData.debugMode || appData.matchMode) {
6794 fprintf(stderr, "%s: %s\n", programName, message);
6797 if (appData.debugMode || appData.matchMode) {
6798 fprintf(stderr, "%s: %s: %s\n",
6799 programName, message, strerror(error));
6801 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6804 ErrorPopUp(_("Error"), message, FALSE);
6809 DisplayMoveError (String message)
6813 DrawPosition(FALSE, NULL);
6814 if (appData.debugMode || appData.matchMode) {
6815 fprintf(stderr, "%s: %s\n", programName, message);
6817 if (appData.popupMoveErrors) {
6818 ErrorPopUp(_("Error"), message, FALSE);
6820 DisplayMessage(message, "");
6826 DisplayFatalError (String message, int error, int status)
6830 errorExitStatus = status;
6832 fprintf(stderr, "%s: %s\n", programName, message);
6834 fprintf(stderr, "%s: %s: %s\n",
6835 programName, message, strerror(error));
6836 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6839 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6840 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6847 DisplayInformation (String message)
6850 ErrorPopUp(_("Information"), message, TRUE);
6854 DisplayNote (String message)
6857 ErrorPopUp(_("Note"), message, FALSE);
6861 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6867 DisplayIcsInteractionTitle (String message)
6869 if (oldICSInteractionTitle == NULL) {
6870 /* Magic to find the old window title, adapted from vim */
6871 char *wina = getenv("WINDOWID");
6873 Window win = (Window) atoi(wina);
6874 Window root, parent, *children;
6875 unsigned int nchildren;
6876 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6878 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6879 if (!XQueryTree(xDisplay, win, &root, &parent,
6880 &children, &nchildren)) break;
6881 if (children) XFree((void *)children);
6882 if (parent == root || parent == 0) break;
6885 XSetErrorHandler(oldHandler);
6887 if (oldICSInteractionTitle == NULL) {
6888 oldICSInteractionTitle = "xterm";
6891 printf("\033]0;%s\007", message);
6895 char pendingReplyPrefix[MSG_SIZ];
6896 ProcRef pendingReplyPR;
6899 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6902 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6906 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6910 AskQuestionPopDown ()
6912 if (!askQuestionUp) return;
6913 XtPopdown(askQuestionShell);
6914 XtDestroyWidget(askQuestionShell);
6915 askQuestionUp = False;
6919 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6925 reply = XawDialogGetValueString(w = XtParent(w));
6926 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6927 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6928 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6929 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6930 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6931 AskQuestionPopDown();
6933 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6937 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6942 XtSetArg(args[0], XtNlabel, &name);
6943 XtGetValues(w, args, 1);
6945 if (strcmp(name, _("cancel")) == 0) {
6946 AskQuestionPopDown();
6948 AskQuestionReplyAction(w, NULL, NULL, NULL);
6953 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6956 Widget popup, layout, dialog, edit;
6962 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6963 pendingReplyPR = pr;
6966 XtSetArg(args[i], XtNresizable, True); i++;
6967 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6968 askQuestionShell = popup =
6969 XtCreatePopupShell(title, transientShellWidgetClass,
6970 shellWidget, args, i);
6973 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6974 layoutArgs, XtNumber(layoutArgs));
6977 XtSetArg(args[i], XtNlabel, question); i++;
6978 XtSetArg(args[i], XtNvalue, ""); i++;
6979 XtSetArg(args[i], XtNborderWidth, 0); i++;
6980 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6983 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6984 (XtPointer) dialog);
6985 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6986 (XtPointer) dialog);
6988 XtRealizeWidget(popup);
6989 CatchDeleteWindow(popup, "AskQuestionPopDown");
6991 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6992 &x, &y, &win_x, &win_y, &mask);
6994 XtSetArg(args[0], XtNx, x - 10);
6995 XtSetArg(args[1], XtNy, y - 30);
6996 XtSetValues(popup, args, 2);
6998 XtPopup(popup, XtGrabExclusive);
6999 askQuestionUp = True;
7001 edit = XtNameToWidget(dialog, "*value");
7002 XtSetKeyboardFocus(popup, edit);
7007 PlaySound (char *name)
7009 if (*name == NULLCHAR) {
7011 } else if (strcmp(name, "$") == 0) {
7012 putc(BELLCHAR, stderr);
7015 char *prefix = "", *sep = "";
7016 if(appData.soundProgram[0] == NULLCHAR) return;
7017 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7018 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7026 PlaySound(appData.soundMove);
7032 PlaySound(appData.soundIcsWin);
7038 PlaySound(appData.soundIcsLoss);
7044 PlaySound(appData.soundIcsDraw);
7048 PlayIcsUnfinishedSound ()
7050 PlaySound(appData.soundIcsUnfinished);
7056 PlaySound(appData.soundIcsAlarm);
7062 PlaySound(appData.soundTell);
7068 system("stty echo");
7075 system("stty -echo");
7080 RunCommand (char *buf)
7086 Colorize (ColorClass cc, int continuation)
7089 int count, outCount, error;
7091 if (textColors[(int)cc].bg > 0) {
7092 if (textColors[(int)cc].fg > 0) {
7093 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7094 textColors[(int)cc].fg, textColors[(int)cc].bg);
7096 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7097 textColors[(int)cc].bg);
7100 if (textColors[(int)cc].fg > 0) {
7101 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7102 textColors[(int)cc].fg);
7104 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7107 count = strlen(buf);
7108 outCount = OutputToProcess(NoProc, buf, count, &error);
7109 if (outCount < count) {
7110 DisplayFatalError(_("Error writing to display"), error, 1);
7113 if (continuation) return;
7116 PlaySound(appData.soundShout);
7119 PlaySound(appData.soundSShout);
7122 PlaySound(appData.soundChannel1);
7125 PlaySound(appData.soundChannel);
7128 PlaySound(appData.soundKibitz);
7131 PlaySound(appData.soundTell);
7133 case ColorChallenge:
7134 PlaySound(appData.soundChallenge);
7137 PlaySound(appData.soundRequest);
7140 PlaySound(appData.soundSeek);
7152 return getpwuid(getuid())->pw_name;
7156 ExpandPathName (char *path)
7158 static char static_buf[4*MSG_SIZ];
7159 char *d, *s, buf[4*MSG_SIZ];
7165 while (*s && isspace(*s))
7174 if (*(s+1) == '/') {
7175 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7179 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7180 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7181 pwd = getpwnam(buf);
7184 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7188 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7189 strcat(d, strchr(s+1, '/'));
7193 safeStrCpy(d, s, 4*MSG_SIZ );
7201 static char host_name[MSG_SIZ];
7203 #if HAVE_GETHOSTNAME
7204 gethostname(host_name, MSG_SIZ);
7206 #else /* not HAVE_GETHOSTNAME */
7207 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7208 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7210 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7212 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7213 #endif /* not HAVE_GETHOSTNAME */
7216 XtIntervalId delayedEventTimerXID = 0;
7217 DelayedEventCallback delayedEventCallback = 0;
7222 delayedEventTimerXID = 0;
7223 delayedEventCallback();
7227 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7229 if(delayedEventTimerXID && delayedEventCallback == cb)
7230 // [HGM] alive: replace, rather than add or flush identical event
7231 XtRemoveTimeOut(delayedEventTimerXID);
7232 delayedEventCallback = cb;
7233 delayedEventTimerXID =
7234 XtAppAddTimeOut(appContext, millisec,
7235 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7238 DelayedEventCallback
7241 if (delayedEventTimerXID) {
7242 return delayedEventCallback;
7249 CancelDelayedEvent ()
7251 if (delayedEventTimerXID) {
7252 XtRemoveTimeOut(delayedEventTimerXID);
7253 delayedEventTimerXID = 0;
7257 XtIntervalId loadGameTimerXID = 0;
7260 LoadGameTimerRunning ()
7262 return loadGameTimerXID != 0;
7266 StopLoadGameTimer ()
7268 if (loadGameTimerXID != 0) {
7269 XtRemoveTimeOut(loadGameTimerXID);
7270 loadGameTimerXID = 0;
7278 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7280 loadGameTimerXID = 0;
7285 StartLoadGameTimer (long millisec)
7288 XtAppAddTimeOut(appContext, millisec,
7289 (XtTimerCallbackProc) LoadGameTimerCallback,
7293 XtIntervalId analysisClockXID = 0;
7296 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7298 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7299 || appData.icsEngineAnalyze) { // [DM]
7300 AnalysisPeriodicEvent(0);
7301 StartAnalysisClock();
7306 StartAnalysisClock ()
7309 XtAppAddTimeOut(appContext, 2000,
7310 (XtTimerCallbackProc) AnalysisClockCallback,
7314 XtIntervalId clockTimerXID = 0;
7317 ClockTimerRunning ()
7319 return clockTimerXID != 0;
7325 if (clockTimerXID != 0) {
7326 XtRemoveTimeOut(clockTimerXID);
7335 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7342 StartClockTimer (long millisec)
7345 XtAppAddTimeOut(appContext, millisec,
7346 (XtTimerCallbackProc) ClockTimerCallback,
7351 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7356 /* check for low time warning */
7357 Pixel foregroundOrWarningColor = timerForegroundPixel;
7360 appData.lowTimeWarning &&
7361 (timer / 1000) < appData.icsAlarmTime)
7362 foregroundOrWarningColor = lowTimeWarningColor;
7364 if (appData.clockMode) {
7365 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7366 XtSetArg(args[0], XtNlabel, buf);
7368 snprintf(buf, MSG_SIZ, "%s ", color);
7369 XtSetArg(args[0], XtNlabel, buf);
7374 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7375 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7377 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7378 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7381 XtSetValues(w, args, 3);
7385 DisplayWhiteClock (long timeRemaining, int highlight)
7389 if(appData.noGUI) return;
7390 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7391 if (highlight && iconPixmap == bIconPixmap) {
7392 iconPixmap = wIconPixmap;
7393 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7394 XtSetValues(shellWidget, args, 1);
7399 DisplayBlackClock (long timeRemaining, int highlight)
7403 if(appData.noGUI) return;
7404 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7405 if (highlight && iconPixmap == wIconPixmap) {
7406 iconPixmap = bIconPixmap;
7407 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7408 XtSetValues(shellWidget, args, 1);
7427 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7431 int to_prog[2], from_prog[2];
7435 if (appData.debugMode) {
7436 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7439 /* We do NOT feed the cmdLine to the shell; we just
7440 parse it into blank-separated arguments in the
7441 most simple-minded way possible.
7444 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7447 while(*p == ' ') p++;
7449 if(*p == '"' || *p == '\'')
7450 p = strchr(++argv[i-1], *p);
7451 else p = strchr(p, ' ');
7452 if (p == NULL) break;
7457 SetUpChildIO(to_prog, from_prog);
7459 if ((pid = fork()) == 0) {
7461 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7462 close(to_prog[1]); // first close the unused pipe ends
7463 close(from_prog[0]);
7464 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7465 dup2(from_prog[1], 1);
7466 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7467 close(from_prog[1]); // and closing again loses one of the pipes!
7468 if(fileno(stderr) >= 2) // better safe than sorry...
7469 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7471 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7476 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7478 execvp(argv[0], argv);
7480 /* If we get here, exec failed */
7485 /* Parent process */
7487 close(from_prog[1]);
7489 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7492 cp->fdFrom = from_prog[0];
7493 cp->fdTo = to_prog[1];
7498 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7500 AlarmCallBack (int n)
7506 DestroyChildProcess (ProcRef pr, int signalType)
7508 ChildProc *cp = (ChildProc *) pr;
7510 if (cp->kind != CPReal) return;
7512 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7513 signal(SIGALRM, AlarmCallBack);
7515 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7516 kill(cp->pid, SIGKILL); // kill it forcefully
7517 wait((int *) 0); // and wait again
7521 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7523 /* Process is exiting either because of the kill or because of
7524 a quit command sent by the backend; either way, wait for it to die.
7533 InterruptChildProcess (ProcRef pr)
7535 ChildProc *cp = (ChildProc *) pr;
7537 if (cp->kind != CPReal) return;
7538 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7542 OpenTelnet (char *host, char *port, ProcRef *pr)
7544 char cmdLine[MSG_SIZ];
7546 if (port[0] == NULLCHAR) {
7547 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7549 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7551 return StartChildProcess(cmdLine, "", pr);
7555 OpenTCP (char *host, char *port, ProcRef *pr)
7558 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7559 #else /* !OMIT_SOCKETS */
7560 struct addrinfo hints;
7561 struct addrinfo *ais, *ai;
7566 memset(&hints, 0, sizeof(hints));
7567 hints.ai_family = AF_UNSPEC;
7568 hints.ai_socktype = SOCK_STREAM;
7570 error = getaddrinfo(host, port, &hints, &ais);
7572 /* a getaddrinfo error is not an errno, so can't return it */
7573 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7574 host, port, gai_strerror(error));
7578 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7579 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7583 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7596 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7602 #endif /* !OMIT_SOCKETS */
7608 OpenCommPort (char *name, ProcRef *pr)
7613 fd = open(name, 2, 0);
7614 if (fd < 0) return errno;
7616 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7627 OpenLoopback (ProcRef *pr)
7632 SetUpChildIO(to, from);
7634 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7637 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7645 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7647 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7651 #define INPUT_SOURCE_BUF_SIZE 8192
7660 char buf[INPUT_SOURCE_BUF_SIZE];
7665 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7667 InputSource *is = (InputSource *) closure;
7672 if (is->lineByLine) {
7673 count = read(is->fd, is->unused,
7674 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7676 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7679 is->unused += count;
7681 while (p < is->unused) {
7682 q = memchr(p, '\n', is->unused - p);
7683 if (q == NULL) break;
7685 (is->func)(is, is->closure, p, q - p, 0);
7689 while (p < is->unused) {
7694 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7699 (is->func)(is, is->closure, is->buf, count, error);
7704 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7707 ChildProc *cp = (ChildProc *) pr;
7709 is = (InputSource *) calloc(1, sizeof(InputSource));
7710 is->lineByLine = lineByLine;
7714 is->fd = fileno(stdin);
7716 is->kind = cp->kind;
7717 is->fd = cp->fdFrom;
7720 is->unused = is->buf;
7723 is->xid = XtAppAddInput(appContext, is->fd,
7724 (XtPointer) (XtInputReadMask),
7725 (XtInputCallbackProc) DoInputCallback,
7727 is->closure = closure;
7728 return (InputSourceRef) is;
7732 RemoveInputSource (InputSourceRef isr)
7734 InputSource *is = (InputSource *) isr;
7736 if (is->xid == 0) return;
7737 XtRemoveInput(is->xid);
7742 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7744 static int line = 0;
7745 ChildProc *cp = (ChildProc *) pr;
7750 if (appData.noJoin || !appData.useInternalWrap)
7751 outCount = fwrite(message, 1, count, stdout);
7754 int width = get_term_width();
7755 int len = wrap(NULL, message, count, width, &line);
7756 char *msg = malloc(len);
7760 outCount = fwrite(message, 1, count, stdout);
7763 dbgchk = wrap(msg, message, count, width, &line);
7764 if (dbgchk != len && appData.debugMode)
7765 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7766 outCount = fwrite(msg, 1, dbgchk, stdout);
7772 outCount = write(cp->fdTo, message, count);
7782 /* Output message to process, with "ms" milliseconds of delay
7783 between each character. This is needed when sending the logon
7784 script to ICC, which for some reason doesn't like the
7785 instantaneous send. */
7787 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7789 ChildProc *cp = (ChildProc *) pr;
7794 r = write(cp->fdTo, message++, 1);
7807 /**** Animation code by Hugh Fisher, DCS, ANU.
7809 Known problem: if a window overlapping the board is
7810 moved away while a piece is being animated underneath,
7811 the newly exposed area won't be updated properly.
7812 I can live with this.
7814 Known problem: if you look carefully at the animation
7815 of pieces in mono mode, they are being drawn as solid
7816 shapes without interior detail while moving. Fixing
7817 this would be a major complication for minimal return.
7820 /* Masks for XPM pieces. Black and white pieces can have
7821 different shapes, but in the interest of retaining my
7822 sanity pieces must have the same outline on both light
7823 and dark squares, and all pieces must use the same
7824 background square colors/images. */
7826 static int xpmDone = 0;
7829 CreateAnimMasks (int pieceDepth)
7835 unsigned long plane;
7838 /* Need a bitmap just to get a GC with right depth */
7839 buf = XCreatePixmap(xDisplay, xBoardWindow,
7841 values.foreground = 1;
7842 values.background = 0;
7843 /* Don't use XtGetGC, not read only */
7844 maskGC = XCreateGC(xDisplay, buf,
7845 GCForeground | GCBackground, &values);
7846 XFreePixmap(xDisplay, buf);
7848 buf = XCreatePixmap(xDisplay, xBoardWindow,
7849 squareSize, squareSize, pieceDepth);
7850 values.foreground = XBlackPixel(xDisplay, xScreen);
7851 values.background = XWhitePixel(xDisplay, xScreen);
7852 bufGC = XCreateGC(xDisplay, buf,
7853 GCForeground | GCBackground, &values);
7855 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7856 /* Begin with empty mask */
7857 if(!xpmDone) // [HGM] pieces: keep using existing
7858 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7859 squareSize, squareSize, 1);
7860 XSetFunction(xDisplay, maskGC, GXclear);
7861 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7862 0, 0, squareSize, squareSize);
7864 /* Take a copy of the piece */
7869 XSetFunction(xDisplay, bufGC, GXcopy);
7870 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7872 0, 0, squareSize, squareSize, 0, 0);
7874 /* XOR the background (light) over the piece */
7875 XSetFunction(xDisplay, bufGC, GXxor);
7877 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7878 0, 0, squareSize, squareSize, 0, 0);
7880 XSetForeground(xDisplay, bufGC, lightSquareColor);
7881 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7884 /* We now have an inverted piece image with the background
7885 erased. Construct mask by just selecting all the non-zero
7886 pixels - no need to reconstruct the original image. */
7887 XSetFunction(xDisplay, maskGC, GXor);
7889 /* Might be quicker to download an XImage and create bitmap
7890 data from it rather than this N copies per piece, but it
7891 only takes a fraction of a second and there is a much
7892 longer delay for loading the pieces. */
7893 for (n = 0; n < pieceDepth; n ++) {
7894 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7895 0, 0, squareSize, squareSize,
7901 XFreePixmap(xDisplay, buf);
7902 XFreeGC(xDisplay, bufGC);
7903 XFreeGC(xDisplay, maskGC);
7907 InitAnimState (AnimState *anim, XWindowAttributes *info)
7912 /* Each buffer is square size, same depth as window */
7913 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7914 squareSize, squareSize, info->depth);
7915 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7916 squareSize, squareSize, info->depth);
7918 /* Create a plain GC for blitting */
7919 mask = GCForeground | GCBackground | GCFunction |
7920 GCPlaneMask | GCGraphicsExposures;
7921 values.foreground = XBlackPixel(xDisplay, xScreen);
7922 values.background = XWhitePixel(xDisplay, xScreen);
7923 values.function = GXcopy;
7924 values.plane_mask = AllPlanes;
7925 values.graphics_exposures = False;
7926 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7928 /* Piece will be copied from an existing context at
7929 the start of each new animation/drag. */
7930 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7932 /* Outline will be a read-only copy of an existing */
7933 anim->outlineGC = None;
7939 XWindowAttributes info;
7941 if (xpmDone && gameInfo.variant == oldVariant) return;
7942 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7943 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7945 InitAnimState(&game, &info);
7946 InitAnimState(&player, &info);
7948 /* For XPM pieces, we need bitmaps to use as masks. */
7950 CreateAnimMasks(info.depth), xpmDone = 1;
7955 static Boolean frameWaiting;
7958 FrameAlarm (int sig)
7960 frameWaiting = False;
7961 /* In case System-V style signals. Needed?? */
7962 signal(SIGALRM, FrameAlarm);
7966 FrameDelay (int time)
7968 struct itimerval delay;
7970 XSync(xDisplay, False);
7973 frameWaiting = True;
7974 signal(SIGALRM, FrameAlarm);
7975 delay.it_interval.tv_sec =
7976 delay.it_value.tv_sec = time / 1000;
7977 delay.it_interval.tv_usec =
7978 delay.it_value.tv_usec = (time % 1000) * 1000;
7979 setitimer(ITIMER_REAL, &delay, NULL);
7980 while (frameWaiting) pause();
7981 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7982 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7983 setitimer(ITIMER_REAL, &delay, NULL);
7990 FrameDelay (int time)
7992 XSync(xDisplay, False);
7994 usleep(time * 1000);
8005 /* Convert board position to corner of screen rect and color */
8008 ScreenSquare (int column, int row, XPoint *pt, int *color)
8011 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8012 pt->y = lineGap + row * (squareSize + lineGap);
8014 pt->x = lineGap + column * (squareSize + lineGap);
8015 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8017 *color = SquareColor(row, column);
8020 /* Convert window coords to square */
8023 BoardSquare (int x, int y, int *column, int *row)
8025 *column = EventToSquare(x, BOARD_WIDTH);
8026 if (flipView && *column >= 0)
8027 *column = BOARD_WIDTH - 1 - *column;
8028 *row = EventToSquare(y, BOARD_HEIGHT);
8029 if (!flipView && *row >= 0)
8030 *row = BOARD_HEIGHT - 1 - *row;
8035 #undef Max /* just in case */
8037 #define Max(a, b) ((a) > (b) ? (a) : (b))
8038 #define Min(a, b) ((a) < (b) ? (a) : (b))
8041 SetRect (XRectangle *rect, int x, int y, int width, int height)
8045 rect->width = width;
8046 rect->height = height;
8049 /* Test if two frames overlap. If they do, return
8050 intersection rect within old and location of
8051 that rect within new. */
8054 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
8056 if (old->x > new->x + size || new->x > old->x + size ||
8057 old->y > new->y + size || new->y > old->y + size) {
8060 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8061 size - abs(old->x - new->x), size - abs(old->y - new->y));
8062 pt->x = Max(old->x - new->x, 0);
8063 pt->y = Max(old->y - new->y, 0);
8068 /* For two overlapping frames, return the rect(s)
8069 in the old that do not intersect with the new. */
8072 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
8076 /* If old = new (shouldn't happen) then nothing to draw */
8077 if (old->x == new->x && old->y == new->y) {
8081 /* Work out what bits overlap. Since we know the rects
8082 are the same size we don't need a full intersect calc. */
8084 /* Top or bottom edge? */
8085 if (new->y > old->y) {
8086 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8088 } else if (old->y > new->y) {
8089 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8090 size, old->y - new->y);
8093 /* Left or right edge - don't overlap any update calculated above. */
8094 if (new->x > old->x) {
8095 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8096 new->x - old->x, size - abs(new->y - old->y));
8098 } else if (old->x > new->x) {
8099 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8100 old->x - new->x, size - abs(new->y - old->y));
8107 /* Generate a series of frame coords from start->mid->finish.
8108 The movement rate doubles until the half way point is
8109 reached, then halves back down to the final destination,
8110 which gives a nice slow in/out effect. The algorithmn
8111 may seem to generate too many intermediates for short
8112 moves, but remember that the purpose is to attract the
8113 viewers attention to the piece about to be moved and
8114 then to where it ends up. Too few frames would be less
8118 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8120 int fraction, n, count;
8124 /* Slow in, stepping 1/16th, then 1/8th, ... */
8126 for (n = 0; n < factor; n++)
8128 for (n = 0; n < factor; n++) {
8129 frames[count].x = start->x + (mid->x - start->x) / fraction;
8130 frames[count].y = start->y + (mid->y - start->y) / fraction;
8132 fraction = fraction / 2;
8136 frames[count] = *mid;
8139 /* Slow out, stepping 1/2, then 1/4, ... */
8141 for (n = 0; n < factor; n++) {
8142 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8143 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8145 fraction = fraction * 2;
8150 /* Draw a piece on the screen without disturbing what's there */
8153 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8157 /* Bitmap for piece being moved. */
8158 if (appData.monoMode) {
8159 *mask = *pieceToSolid(piece);
8160 } else if (useImages) {
8162 *mask = xpmMask[piece];
8164 *mask = ximMaskPm[piece];
8167 *mask = *pieceToSolid(piece);
8170 /* GC for piece being moved. Square color doesn't matter, but
8171 since it gets modified we make a copy of the original. */
8173 if (appData.monoMode)
8178 if (appData.monoMode)
8183 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8185 /* Outline only used in mono mode and is not modified */
8187 *outline = bwPieceGC;
8189 *outline = wbPieceGC;
8193 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8198 /* Draw solid rectangle which will be clipped to shape of piece */
8199 XFillRectangle(xDisplay, dest, clip,
8200 0, 0, squareSize, squareSize);
8201 if (appData.monoMode)
8202 /* Also draw outline in contrasting color for black
8203 on black / white on white cases */
8204 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8205 0, 0, squareSize, squareSize, 0, 0, 1);
8207 /* Copy the piece */
8212 if(appData.upsideDown && flipView) kind ^= 2;
8213 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8215 0, 0, squareSize, squareSize,
8220 /* Animate the movement of a single piece */
8223 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8227 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8228 /* The old buffer is initialised with the start square (empty) */
8229 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8230 anim->prevFrame = *start;
8232 /* The piece will be drawn using its own bitmap as a matte */
8233 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8234 XSetClipMask(xDisplay, anim->pieceGC, mask);
8238 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8240 XRectangle updates[4];
8245 /* Save what we are about to draw into the new buffer */
8246 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8247 frame->x, frame->y, squareSize, squareSize,
8250 /* Erase bits of the previous frame */
8251 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8252 /* Where the new frame overlapped the previous,
8253 the contents in newBuf are wrong. */
8254 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8255 overlap.x, overlap.y,
8256 overlap.width, overlap.height,
8258 /* Repaint the areas in the old that don't overlap new */
8259 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8260 for (i = 0; i < count; i++)
8261 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8262 updates[i].x - anim->prevFrame.x,
8263 updates[i].y - anim->prevFrame.y,
8264 updates[i].width, updates[i].height,
8265 updates[i].x, updates[i].y);
8267 /* Easy when no overlap */
8268 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8269 0, 0, squareSize, squareSize,
8270 anim->prevFrame.x, anim->prevFrame.y);
8273 /* Save this frame for next time round */
8274 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8275 0, 0, squareSize, squareSize,
8277 anim->prevFrame = *frame;
8279 /* Draw piece over original screen contents, not current,
8280 and copy entire rect. Wipes out overlapping piece images. */
8281 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8282 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8283 0, 0, squareSize, squareSize,
8284 frame->x, frame->y);
8288 EndAnimation (AnimState *anim, XPoint *finish)
8290 XRectangle updates[4];
8295 /* The main code will redraw the final square, so we
8296 only need to erase the bits that don't overlap. */
8297 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8298 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8299 for (i = 0; i < count; i++)
8300 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8301 updates[i].x - anim->prevFrame.x,
8302 updates[i].y - anim->prevFrame.y,
8303 updates[i].width, updates[i].height,
8304 updates[i].x, updates[i].y);
8306 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8307 0, 0, squareSize, squareSize,
8308 anim->prevFrame.x, anim->prevFrame.y);
8313 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8317 BeginAnimation(anim, piece, startColor, start);
8318 for (n = 0; n < nFrames; n++) {
8319 AnimationFrame(anim, &(frames[n]), piece);
8320 FrameDelay(appData.animSpeed);
8322 EndAnimation(anim, finish);
8326 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8329 ChessSquare piece = board[fromY][toY];
8330 board[fromY][toY] = EmptySquare;
8331 DrawPosition(FALSE, board);
8333 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8334 y = lineGap + toY * (squareSize + lineGap);
8336 x = lineGap + toX * (squareSize + lineGap);
8337 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8339 for(i=1; i<4*kFactor; i++) {
8340 int r = squareSize * 9 * i/(20*kFactor - 5);
8341 XFillArc(xDisplay, xBoardWindow, highlineGC,
8342 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8343 FrameDelay(appData.animSpeed);
8345 board[fromY][toY] = piece;
8348 /* Main control logic for deciding what to animate and how */
8351 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8355 XPoint start, finish, mid;
8356 XPoint frames[kFactor * 2 + 1];
8357 int nFrames, startColor, endColor;
8359 /* Are we animating? */
8360 if (!appData.animate || appData.blindfold)
8363 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8364 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8365 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8367 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8368 piece = board[fromY][fromX];
8369 if (piece >= EmptySquare) return;
8374 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8377 ScreenSquare(fromX, fromY, &start, &startColor);
8378 ScreenSquare(toX, toY, &finish, &endColor);
8381 /* Knight: make straight movement then diagonal */
8382 if (abs(toY - fromY) < abs(toX - fromX)) {
8383 mid.x = start.x + (finish.x - start.x) / 2;
8387 mid.y = start.y + (finish.y - start.y) / 2;
8390 mid.x = start.x + (finish.x - start.x) / 2;
8391 mid.y = start.y + (finish.y - start.y) / 2;
8394 /* Don't use as many frames for very short moves */
8395 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8396 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8398 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8399 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8400 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8402 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8403 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8406 /* Be sure end square is redrawn */
8407 damage[0][toY][toX] = True;
8411 DragPieceBegin (int x, int y, Boolean instantly)
8413 int boardX, boardY, color;
8416 /* Are we animating? */
8417 if (!appData.animateDragging || appData.blindfold)
8420 /* Figure out which square we start in and the
8421 mouse position relative to top left corner. */
8422 BoardSquare(x, y, &boardX, &boardY);
8423 player.startBoardX = boardX;
8424 player.startBoardY = boardY;
8425 ScreenSquare(boardX, boardY, &corner, &color);
8426 player.startSquare = corner;
8427 player.startColor = color;
8428 /* As soon as we start dragging, the piece will jump slightly to
8429 be centered over the mouse pointer. */
8430 player.mouseDelta.x = squareSize/2;
8431 player.mouseDelta.y = squareSize/2;
8432 /* Initialise animation */
8433 player.dragPiece = PieceForSquare(boardX, boardY);
8435 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8436 player.dragActive = True;
8437 BeginAnimation(&player, player.dragPiece, color, &corner);
8438 /* Mark this square as needing to be redrawn. Note that
8439 we don't remove the piece though, since logically (ie
8440 as seen by opponent) the move hasn't been made yet. */
8441 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8442 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8443 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8444 corner.x, corner.y, squareSize, squareSize,
8445 0, 0); // [HGM] zh: unstack in stead of grab
8446 if(gatingPiece != EmptySquare) {
8447 /* Kludge alert: When gating we want the introduced
8448 piece to appear on the from square. To generate an
8449 image of it, we draw it on the board, copy the image,
8450 and draw the original piece again. */
8451 ChessSquare piece = boards[currentMove][boardY][boardX];
8452 DrawSquare(boardY, boardX, gatingPiece, 0);
8453 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8454 corner.x, corner.y, squareSize, squareSize, 0, 0);
8455 DrawSquare(boardY, boardX, piece, 0);
8457 damage[0][boardY][boardX] = True;
8459 player.dragActive = False;
8464 ChangeDragPiece (ChessSquare piece)
8467 player.dragPiece = piece;
8468 /* The piece will be drawn using its own bitmap as a matte */
8469 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8470 XSetClipMask(xDisplay, player.pieceGC, mask);
8474 DragPieceMove (int x, int y)
8478 /* Are we animating? */
8479 if (!appData.animateDragging || appData.blindfold)
8483 if (! player.dragActive)
8485 /* Move piece, maintaining same relative position
8486 of mouse within square */
8487 corner.x = x - player.mouseDelta.x;
8488 corner.y = y - player.mouseDelta.y;
8489 AnimationFrame(&player, &corner, player.dragPiece);
8491 if (appData.highlightDragging) {
8493 BoardSquare(x, y, &boardX, &boardY);
8494 SetHighlights(fromX, fromY, boardX, boardY);
8500 DragPieceEnd (int x, int y)
8502 int boardX, boardY, color;
8505 /* Are we animating? */
8506 if (!appData.animateDragging || appData.blindfold)
8510 if (! player.dragActive)
8512 /* Last frame in sequence is square piece is
8513 placed on, which may not match mouse exactly. */
8514 BoardSquare(x, y, &boardX, &boardY);
8515 ScreenSquare(boardX, boardY, &corner, &color);
8516 EndAnimation(&player, &corner);
8518 /* Be sure end square is redrawn */
8519 damage[0][boardY][boardX] = True;
8521 /* This prevents weird things happening with fast successive
8522 clicks which on my Sun at least can cause motion events
8523 without corresponding press/release. */
8524 player.dragActive = False;
8527 /* Handle expose event while piece being dragged */
8532 if (!player.dragActive || appData.blindfold)
8535 /* What we're doing: logically, the move hasn't been made yet,
8536 so the piece is still in it's original square. But visually
8537 it's being dragged around the board. So we erase the square
8538 that the piece is on and draw it at the last known drag point. */
8539 BlankSquare(player.startSquare.x, player.startSquare.y,
8540 player.startColor, EmptySquare, xBoardWindow, 1);
8541 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8542 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8545 #include <sys/ioctl.h>
8549 int fd, default_width;
8552 default_width = 79; // this is FICS default anyway...
8554 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8556 if (!ioctl(fd, TIOCGSIZE, &win))
8557 default_width = win.ts_cols;
8558 #elif defined(TIOCGWINSZ)
8560 if (!ioctl(fd, TIOCGWINSZ, &win))
8561 default_width = win.ws_col;
8563 return default_width;
8569 static int old_width = 0;
8570 int new_width = get_term_width();
8572 if (old_width != new_width)
8573 ics_printf("set width %d\n", new_width);
8574 old_width = new_width;
8578 NotifyFrontendLogin ()
8583 /* [AS] Arrow highlighting support */
8585 static double A_WIDTH = 5; /* Width of arrow body */
8587 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8588 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8599 return (int) (x + 0.5);
8603 SquareToPos (int rank, int file, int *x, int *y)
8606 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8607 *y = lineGap + rank * (squareSize + lineGap);
8609 *x = lineGap + file * (squareSize + lineGap);
8610 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8614 /* Draw an arrow between two points using current settings */
8616 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8619 double dx, dy, j, k, x, y;
8622 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8624 arrow[0].x = s_x + A_WIDTH + 0.5;
8627 arrow[1].x = s_x + A_WIDTH + 0.5;
8628 arrow[1].y = d_y - h;
8630 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8631 arrow[2].y = d_y - h;
8636 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8637 arrow[5].y = d_y - h;
8639 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8640 arrow[4].y = d_y - h;
8642 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8645 else if( d_y == s_y ) {
8646 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8649 arrow[0].y = s_y + A_WIDTH + 0.5;
8651 arrow[1].x = d_x - w;
8652 arrow[1].y = s_y + A_WIDTH + 0.5;
8654 arrow[2].x = d_x - w;
8655 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8660 arrow[5].x = d_x - w;
8661 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8663 arrow[4].x = d_x - w;
8664 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8667 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8670 /* [AS] Needed a lot of paper for this! :-) */
8671 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8672 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8674 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8676 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8681 arrow[0].x = Round(x - j);
8682 arrow[0].y = Round(y + j*dx);
8684 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8685 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8688 x = (double) d_x - k;
8689 y = (double) d_y - k*dy;
8692 x = (double) d_x + k;
8693 y = (double) d_y + k*dy;
8696 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8698 arrow[6].x = Round(x - j);
8699 arrow[6].y = Round(y + j*dx);
8701 arrow[2].x = Round(arrow[6].x + 2*j);
8702 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8704 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8705 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8710 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8711 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8714 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8715 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8716 // Polygon( hdc, arrow, 7 );
8720 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8723 hor = 64*s_col + 32; vert = 64*s_row + 32;
8724 for(i=0; i<= 64; i++) {
8725 damage[0][vert+6>>6][hor+6>>6] = True;
8726 damage[0][vert-6>>6][hor+6>>6] = True;
8727 damage[0][vert+6>>6][hor-6>>6] = True;
8728 damage[0][vert-6>>6][hor-6>>6] = True;
8729 hor += d_col - s_col; vert += d_row - s_row;
8733 /* [AS] Draw an arrow between two squares */
8735 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8737 int s_x, s_y, d_x, d_y;
8739 if( s_col == d_col && s_row == d_row ) {
8743 /* Get source and destination points */
8744 SquareToPos( s_row, s_col, &s_x, &s_y);
8745 SquareToPos( d_row, d_col, &d_x, &d_y);
8748 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8750 else if( d_y < s_y ) {
8751 d_y += squareSize / 2 + squareSize / 4;
8754 d_y += squareSize / 2;
8758 d_x += squareSize / 2 - squareSize / 4;
8760 else if( d_x < s_x ) {
8761 d_x += squareSize / 2 + squareSize / 4;
8764 d_x += squareSize / 2;
8767 s_x += squareSize / 2;
8768 s_y += squareSize / 2;
8771 A_WIDTH = squareSize / 14.; //[HGM] make float
8773 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8774 ArrowDamage(s_col, s_row, d_col, d_row);
8778 IsDrawArrowEnabled ()
8780 return appData.highlightMoveWithArrow && squareSize >= 32;
8784 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8786 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8787 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8791 UpdateLogos (int displ)
8793 return; // no logos in XBoard yet