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
1829 if(appData.monoMode != oldMono)
1832 oldMono = appData.monoMode;
1837 ParseIcsTextColors ()
1838 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1839 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1840 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1841 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1842 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1843 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1844 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1845 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1846 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1847 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1848 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1850 if (appData.colorize) {
1852 _("%s: can't parse color names; disabling colorization\n"),
1855 appData.colorize = FALSE;
1861 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1862 XrmValue vFrom, vTo;
1863 int forceMono = False;
1865 if (!appData.monoMode) {
1866 vFrom.addr = (caddr_t) appData.lightSquareColor;
1867 vFrom.size = strlen(appData.lightSquareColor);
1868 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1869 if (vTo.addr == NULL) {
1870 appData.monoMode = True;
1873 lightSquareColor = *(Pixel *) vTo.addr;
1876 if (!appData.monoMode) {
1877 vFrom.addr = (caddr_t) appData.darkSquareColor;
1878 vFrom.size = strlen(appData.darkSquareColor);
1879 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1880 if (vTo.addr == NULL) {
1881 appData.monoMode = True;
1884 darkSquareColor = *(Pixel *) vTo.addr;
1887 if (!appData.monoMode) {
1888 vFrom.addr = (caddr_t) appData.whitePieceColor;
1889 vFrom.size = strlen(appData.whitePieceColor);
1890 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1891 if (vTo.addr == NULL) {
1892 appData.monoMode = True;
1895 whitePieceColor = *(Pixel *) vTo.addr;
1898 if (!appData.monoMode) {
1899 vFrom.addr = (caddr_t) appData.blackPieceColor;
1900 vFrom.size = strlen(appData.blackPieceColor);
1901 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1902 if (vTo.addr == NULL) {
1903 appData.monoMode = True;
1906 blackPieceColor = *(Pixel *) vTo.addr;
1910 if (!appData.monoMode) {
1911 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1912 vFrom.size = strlen(appData.highlightSquareColor);
1913 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1914 if (vTo.addr == NULL) {
1915 appData.monoMode = True;
1918 highlightSquareColor = *(Pixel *) vTo.addr;
1922 if (!appData.monoMode) {
1923 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1924 vFrom.size = strlen(appData.premoveHighlightColor);
1925 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1926 if (vTo.addr == NULL) {
1927 appData.monoMode = True;
1930 premoveHighlightColor = *(Pixel *) vTo.addr;
1938 { // [HGM] taken out of main
1940 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1941 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1942 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1944 if (appData.bitmapDirectory[0] != NULLCHAR) {
1948 CreateXPMBoard(appData.liteBackTextureFile, 1);
1949 CreateXPMBoard(appData.darkBackTextureFile, 0);
1953 /* Create regular pieces */
1954 if (!useImages) CreatePieces();
1959 main (int argc, char **argv)
1961 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1962 XSetWindowAttributes window_attributes;
1964 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1965 XrmValue vFrom, vTo;
1966 XtGeometryResult gres;
1969 int forceMono = False;
1971 srandom(time(0)); // [HGM] book: make random truly random
1973 setbuf(stdout, NULL);
1974 setbuf(stderr, NULL);
1977 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1978 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1982 programName = strrchr(argv[0], '/');
1983 if (programName == NULL)
1984 programName = argv[0];
1989 XtSetLanguageProc(NULL, NULL, NULL);
1990 bindtextdomain(PACKAGE, LOCALEDIR);
1991 textdomain(PACKAGE);
1995 XtAppInitialize(&appContext, "XBoard", shellOptions,
1996 XtNumber(shellOptions),
1997 &argc, argv, xboardResources, NULL, 0);
1998 appData.boardSize = "";
1999 InitAppData(ConvertToLine(argc, argv));
2001 if (p == NULL) p = "/tmp";
2002 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2003 gameCopyFilename = (char*) malloc(i);
2004 gamePasteFilename = (char*) malloc(i);
2005 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2006 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2008 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2009 clientResources, XtNumber(clientResources),
2012 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2013 static char buf[MSG_SIZ];
2014 EscapeExpand(buf, appData.firstInitString);
2015 appData.firstInitString = strdup(buf);
2016 EscapeExpand(buf, appData.secondInitString);
2017 appData.secondInitString = strdup(buf);
2018 EscapeExpand(buf, appData.firstComputerString);
2019 appData.firstComputerString = strdup(buf);
2020 EscapeExpand(buf, appData.secondComputerString);
2021 appData.secondComputerString = strdup(buf);
2024 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2027 if (chdir(chessDir) != 0) {
2028 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2034 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2035 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2036 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2037 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2040 setbuf(debugFP, NULL);
2044 if (appData.debugMode) {
2045 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2049 /* [HGM,HR] make sure board size is acceptable */
2050 if(appData.NrFiles > BOARD_FILES ||
2051 appData.NrRanks > BOARD_RANKS )
2052 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2055 /* This feature does not work; animation needs a rewrite */
2056 appData.highlightDragging = FALSE;
2060 xDisplay = XtDisplay(shellWidget);
2061 xScreen = DefaultScreen(xDisplay);
2062 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2064 gameInfo.variant = StringToVariant(appData.variant);
2065 InitPosition(FALSE);
2068 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2070 if (isdigit(appData.boardSize[0])) {
2071 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2072 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2073 &fontPxlSize, &smallLayout, &tinyLayout);
2075 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2076 programName, appData.boardSize);
2080 /* Find some defaults; use the nearest known size */
2081 SizeDefaults *szd, *nearest;
2082 int distance = 99999;
2083 nearest = szd = sizeDefaults;
2084 while (szd->name != NULL) {
2085 if (abs(szd->squareSize - squareSize) < distance) {
2087 distance = abs(szd->squareSize - squareSize);
2088 if (distance == 0) break;
2092 if (i < 2) lineGap = nearest->lineGap;
2093 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2094 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2095 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2096 if (i < 6) smallLayout = nearest->smallLayout;
2097 if (i < 7) tinyLayout = nearest->tinyLayout;
2100 SizeDefaults *szd = sizeDefaults;
2101 if (*appData.boardSize == NULLCHAR) {
2102 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2103 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2106 if (szd->name == NULL) szd--;
2107 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2109 while (szd->name != NULL &&
2110 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2111 if (szd->name == NULL) {
2112 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2113 programName, appData.boardSize);
2117 squareSize = szd->squareSize;
2118 lineGap = szd->lineGap;
2119 clockFontPxlSize = szd->clockFontPxlSize;
2120 coordFontPxlSize = szd->coordFontPxlSize;
2121 fontPxlSize = szd->fontPxlSize;
2122 smallLayout = szd->smallLayout;
2123 tinyLayout = szd->tinyLayout;
2124 // [HGM] font: use defaults from settings file if available and not overruled
2126 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2127 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2128 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2129 appData.font = fontTable[MESSAGE_FONT][squareSize];
2130 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2131 appData.coordFont = fontTable[COORD_FONT][squareSize];
2133 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2134 if (strlen(appData.pixmapDirectory) > 0) {
2135 p = ExpandPathName(appData.pixmapDirectory);
2137 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2138 appData.pixmapDirectory);
2141 if (appData.debugMode) {
2142 fprintf(stderr, _("\
2143 XBoard square size (hint): %d\n\
2144 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2146 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2147 if (appData.debugMode) {
2148 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2151 defaultLineGap = lineGap;
2152 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2154 /* [HR] height treated separately (hacked) */
2155 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2156 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2157 if (appData.showJail == 1) {
2158 /* Jail on top and bottom */
2159 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2160 XtSetArg(boardArgs[2], XtNheight,
2161 boardHeight + 2*(lineGap + squareSize));
2162 } else if (appData.showJail == 2) {
2164 XtSetArg(boardArgs[1], XtNwidth,
2165 boardWidth + 2*(lineGap + squareSize));
2166 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2169 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2170 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2174 * Determine what fonts to use.
2177 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2178 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2179 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2180 fontSet = CreateFontSet(appData.font);
2181 clockFontSet = CreateFontSet(appData.clockFont);
2183 /* For the coordFont, use the 0th font of the fontset. */
2184 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2185 XFontStruct **font_struct_list;
2186 XFontSetExtents *fontSize;
2187 char **font_name_list;
2188 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2189 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2190 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2191 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
2192 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
2195 appData.font = FindFont(appData.font, fontPxlSize);
2196 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2197 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2198 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2199 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2200 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2201 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2203 countFontID = coordFontID; // [HGM] holdings
2204 countFontStruct = coordFontStruct;
2206 xdb = XtDatabase(xDisplay);
2208 XrmPutLineResource(&xdb, "*international: True");
2209 vTo.size = sizeof(XFontSet);
2210 vTo.addr = (XtPointer) &fontSet;
2211 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2213 XrmPutStringResource(&xdb, "*font", appData.font);
2217 * Detect if there are not enough colors available and adapt.
2219 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2220 appData.monoMode = True;
2223 forceMono = MakeColors();
2226 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2228 appData.monoMode = True;
2231 if (appData.lowTimeWarning && !appData.monoMode) {
2232 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2233 vFrom.size = strlen(appData.lowTimeWarningColor);
2234 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2235 if (vTo.addr == NULL)
2236 appData.monoMode = True;
2238 lowTimeWarningColor = *(Pixel *) vTo.addr;
2241 if (appData.monoMode && appData.debugMode) {
2242 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2243 (unsigned long) XWhitePixel(xDisplay, xScreen),
2244 (unsigned long) XBlackPixel(xDisplay, xScreen));
2247 ParseIcsTextColors();
2248 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2249 textColors[ColorNone].attr = 0;
2251 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2257 layoutName = "tinyLayout";
2258 } else if (smallLayout) {
2259 layoutName = "smallLayout";
2261 layoutName = "normalLayout";
2263 /* Outer layoutWidget is there only to provide a name for use in
2264 resources that depend on the layout style */
2266 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2267 layoutArgs, XtNumber(layoutArgs));
2269 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2270 formArgs, XtNumber(formArgs));
2271 XtSetArg(args[0], XtNdefaultDistance, &sep);
2272 XtGetValues(formWidget, args, 1);
2275 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2276 XtSetArg(args[0], XtNtop, XtChainTop);
2277 XtSetArg(args[1], XtNbottom, XtChainTop);
2278 XtSetArg(args[2], XtNright, XtChainLeft);
2279 XtSetValues(menuBarWidget, args, 3);
2281 widgetList[j++] = whiteTimerWidget =
2282 XtCreateWidget("whiteTime", labelWidgetClass,
2283 formWidget, timerArgs, XtNumber(timerArgs));
2285 XtSetArg(args[0], XtNfontSet, clockFontSet);
2287 XtSetArg(args[0], XtNfont, clockFontStruct);
2289 XtSetArg(args[1], XtNtop, XtChainTop);
2290 XtSetArg(args[2], XtNbottom, XtChainTop);
2291 XtSetValues(whiteTimerWidget, args, 3);
2293 widgetList[j++] = blackTimerWidget =
2294 XtCreateWidget("blackTime", labelWidgetClass,
2295 formWidget, timerArgs, XtNumber(timerArgs));
2297 XtSetArg(args[0], XtNfontSet, clockFontSet);
2299 XtSetArg(args[0], XtNfont, clockFontStruct);
2301 XtSetArg(args[1], XtNtop, XtChainTop);
2302 XtSetArg(args[2], XtNbottom, XtChainTop);
2303 XtSetValues(blackTimerWidget, args, 3);
2305 if (appData.titleInWindow) {
2306 widgetList[j++] = titleWidget =
2307 XtCreateWidget("title", labelWidgetClass, formWidget,
2308 titleArgs, XtNumber(titleArgs));
2309 XtSetArg(args[0], XtNtop, XtChainTop);
2310 XtSetArg(args[1], XtNbottom, XtChainTop);
2311 XtSetValues(titleWidget, args, 2);
2314 if (appData.showButtonBar) {
2315 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2316 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2317 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2318 XtSetArg(args[2], XtNtop, XtChainTop);
2319 XtSetArg(args[3], XtNbottom, XtChainTop);
2320 XtSetValues(buttonBarWidget, args, 4);
2323 widgetList[j++] = messageWidget =
2324 XtCreateWidget("message", labelWidgetClass, formWidget,
2325 messageArgs, XtNumber(messageArgs));
2326 XtSetArg(args[0], XtNtop, XtChainTop);
2327 XtSetArg(args[1], XtNbottom, XtChainTop);
2328 XtSetValues(messageWidget, args, 2);
2330 widgetList[j++] = boardWidget =
2331 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2332 XtNumber(boardArgs));
2334 XtManageChildren(widgetList, j);
2336 timerWidth = (boardWidth - sep) / 2;
2337 XtSetArg(args[0], XtNwidth, timerWidth);
2338 XtSetValues(whiteTimerWidget, args, 1);
2339 XtSetValues(blackTimerWidget, args, 1);
2341 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2342 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2343 XtGetValues(whiteTimerWidget, args, 2);
2345 if (appData.showButtonBar) {
2346 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2347 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2348 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2352 * formWidget uses these constraints but they are stored
2356 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2357 XtSetValues(menuBarWidget, args, i);
2358 if (appData.titleInWindow) {
2361 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2362 XtSetValues(whiteTimerWidget, args, i);
2364 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2365 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2366 XtSetValues(blackTimerWidget, args, i);
2368 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2369 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2370 XtSetValues(titleWidget, args, i);
2372 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2373 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2374 XtSetValues(messageWidget, args, i);
2375 if (appData.showButtonBar) {
2377 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2378 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2379 XtSetValues(buttonBarWidget, args, i);
2383 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2384 XtSetValues(whiteTimerWidget, args, i);
2386 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2387 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2388 XtSetValues(blackTimerWidget, args, i);
2390 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2391 XtSetValues(titleWidget, args, i);
2393 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2394 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2395 XtSetValues(messageWidget, args, i);
2396 if (appData.showButtonBar) {
2398 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2399 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2400 XtSetValues(buttonBarWidget, args, i);
2405 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2406 XtSetValues(whiteTimerWidget, args, i);
2408 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2409 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2410 XtSetValues(blackTimerWidget, args, i);
2412 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2413 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2414 XtSetValues(messageWidget, args, i);
2415 if (appData.showButtonBar) {
2417 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2418 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2419 XtSetValues(buttonBarWidget, args, i);
2423 XtSetArg(args[0], XtNfromVert, messageWidget);
2424 XtSetArg(args[1], XtNtop, XtChainTop);
2425 XtSetArg(args[2], XtNbottom, XtChainBottom);
2426 XtSetArg(args[3], XtNleft, XtChainLeft);
2427 XtSetArg(args[4], XtNright, XtChainRight);
2428 XtSetValues(boardWidget, args, 5);
2430 XtRealizeWidget(shellWidget);
2433 XtSetArg(args[0], XtNx, wpMain.x);
2434 XtSetArg(args[1], XtNy, wpMain.y);
2435 XtSetValues(shellWidget, args, 2);
2439 * Correct the width of the message and title widgets.
2440 * It is not known why some systems need the extra fudge term.
2441 * The value "2" is probably larger than needed.
2443 XawFormDoLayout(formWidget, False);
2445 #define WIDTH_FUDGE 2
2447 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2448 XtSetArg(args[i], XtNheight, &h); i++;
2449 XtGetValues(messageWidget, args, i);
2450 if (appData.showButtonBar) {
2452 XtSetArg(args[i], XtNwidth, &w); i++;
2453 XtGetValues(buttonBarWidget, args, i);
2454 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2456 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2459 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2460 if (gres != XtGeometryYes && appData.debugMode) {
2461 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2462 programName, gres, w, h, wr, hr);
2465 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2466 /* The size used for the child widget in layout lags one resize behind
2467 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2469 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2470 if (gres != XtGeometryYes && appData.debugMode) {
2471 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2472 programName, gres, w, h, wr, hr);
2475 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2476 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2477 XtSetArg(args[1], XtNright, XtChainRight);
2478 XtSetValues(messageWidget, args, 2);
2480 if (appData.titleInWindow) {
2482 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2483 XtSetArg(args[i], XtNheight, &h); i++;
2484 XtGetValues(titleWidget, args, i);
2486 w = boardWidth - 2*bor;
2488 XtSetArg(args[0], XtNwidth, &w);
2489 XtGetValues(menuBarWidget, args, 1);
2490 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2493 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2494 if (gres != XtGeometryYes && appData.debugMode) {
2496 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2497 programName, gres, w, h, wr, hr);
2500 XawFormDoLayout(formWidget, True);
2502 xBoardWindow = XtWindow(boardWidget);
2504 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2505 // not need to go into InitDrawingSizes().
2509 * Create X checkmark bitmap and initialize option menu checks.
2511 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2512 checkmark_bits, checkmark_width, checkmark_height);
2513 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2514 #ifndef OPTIONSDIALOG
2515 if (appData.alwaysPromoteToQueen) {
2516 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2519 if (appData.animateDragging) {
2520 XtSetValues(XtNameToWidget(menuBarWidget,
2521 "menuOptions.Animate Dragging"),
2524 if (appData.animate) {
2525 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2528 if (appData.autoCallFlag) {
2529 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2532 if (appData.autoFlipView) {
2533 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2536 if (appData.blindfold) {
2537 XtSetValues(XtNameToWidget(menuBarWidget,
2538 "menuOptions.Blindfold"), args, 1);
2540 if (appData.flashCount > 0) {
2541 XtSetValues(XtNameToWidget(menuBarWidget,
2542 "menuOptions.Flash Moves"),
2546 if (appData.highlightDragging) {
2547 XtSetValues(XtNameToWidget(menuBarWidget,
2548 "menuOptions.Highlight Dragging"),
2552 if (appData.highlightLastMove) {
2553 XtSetValues(XtNameToWidget(menuBarWidget,
2554 "menuOptions.Highlight Last Move"),
2557 if (appData.highlightMoveWithArrow) {
2558 XtSetValues(XtNameToWidget(menuBarWidget,
2559 "menuOptions.Arrow"),
2562 // if (appData.icsAlarm) {
2563 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2566 if (appData.ringBellAfterMoves) {
2567 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2570 if (appData.oneClick) {
2571 XtSetValues(XtNameToWidget(menuBarWidget,
2572 "menuOptions.OneClick"), args, 1);
2574 if (appData.periodicUpdates) {
2575 XtSetValues(XtNameToWidget(menuBarWidget,
2576 "menuOptions.Periodic Updates"), args, 1);
2578 if (appData.ponderNextMove) {
2579 XtSetValues(XtNameToWidget(menuBarWidget,
2580 "menuOptions.Ponder Next Move"), args, 1);
2582 if (appData.popupExitMessage) {
2583 XtSetValues(XtNameToWidget(menuBarWidget,
2584 "menuOptions.Popup Exit Message"), args, 1);
2586 if (appData.popupMoveErrors) {
2587 XtSetValues(XtNameToWidget(menuBarWidget,
2588 "menuOptions.Popup Move Errors"), args, 1);
2590 // if (appData.premove) {
2591 // XtSetValues(XtNameToWidget(menuBarWidget,
2592 // "menuOptions.Premove"), args, 1);
2594 if (appData.showCoords) {
2595 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2598 if (appData.hideThinkingFromHuman) {
2599 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2602 if (appData.testLegality) {
2603 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2607 if (saveSettingsOnExit) {
2608 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2615 ReadBitmap(&wIconPixmap, "icon_white.bm",
2616 icon_white_bits, icon_white_width, icon_white_height);
2617 ReadBitmap(&bIconPixmap, "icon_black.bm",
2618 icon_black_bits, icon_black_width, icon_black_height);
2619 iconPixmap = wIconPixmap;
2621 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2622 XtSetValues(shellWidget, args, i);
2625 * Create a cursor for the board widget.
2627 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2628 XChangeWindowAttributes(xDisplay, xBoardWindow,
2629 CWCursor, &window_attributes);
2632 * Inhibit shell resizing.
2634 shellArgs[0].value = (XtArgVal) &w;
2635 shellArgs[1].value = (XtArgVal) &h;
2636 XtGetValues(shellWidget, shellArgs, 2);
2637 shellArgs[4].value = shellArgs[2].value = w;
2638 shellArgs[5].value = shellArgs[3].value = h;
2639 XtSetValues(shellWidget, &shellArgs[2], 4);
2640 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2641 marginH = h - boardHeight;
2643 CatchDeleteWindow(shellWidget, "QuitProc");
2651 if (appData.animate || appData.animateDragging)
2654 XtAugmentTranslations(formWidget,
2655 XtParseTranslationTable(globalTranslations));
2656 XtAugmentTranslations(boardWidget,
2657 XtParseTranslationTable(boardTranslations));
2658 XtAugmentTranslations(whiteTimerWidget,
2659 XtParseTranslationTable(whiteTranslations));
2660 XtAugmentTranslations(blackTimerWidget,
2661 XtParseTranslationTable(blackTranslations));
2663 /* Why is the following needed on some versions of X instead
2664 * of a translation? */
2665 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2666 (XtEventHandler) EventProc, NULL);
2668 XtAddEventHandler(formWidget, KeyPressMask, False,
2669 (XtEventHandler) MoveTypeInProc, NULL);
2670 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2671 (XtEventHandler) EventProc, NULL);
2673 /* [AS] Restore layout */
2674 if( wpMoveHistory.visible ) {
2678 if( wpEvalGraph.visible )
2683 if( wpEngineOutput.visible ) {
2684 EngineOutputPopUp();
2689 if (errorExitStatus == -1) {
2690 if (appData.icsActive) {
2691 /* We now wait until we see "login:" from the ICS before
2692 sending the logon script (problems with timestamp otherwise) */
2693 /*ICSInitScript();*/
2694 if (appData.icsInputBox) ICSInputBoxPopUp();
2698 signal(SIGWINCH, TermSizeSigHandler);
2700 signal(SIGINT, IntSigHandler);
2701 signal(SIGTERM, IntSigHandler);
2702 if (*appData.cmailGameName != NULLCHAR) {
2703 signal(SIGUSR1, CmailSigHandler);
2706 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2708 // XtSetKeyboardFocus(shellWidget, formWidget);
2709 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2711 XtAppMainLoop(appContext);
2712 if (appData.debugMode) fclose(debugFP); // [DM] debug
2716 static Boolean noEcho;
2721 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2722 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2724 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2725 unlink(gameCopyFilename);
2726 unlink(gamePasteFilename);
2727 if(noEcho) EchoOn();
2731 TermSizeSigHandler (int sig)
2737 IntSigHandler (int sig)
2743 CmailSigHandler (int sig)
2748 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2750 /* Activate call-back function CmailSigHandlerCallBack() */
2751 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2753 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2757 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2760 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2762 /**** end signal code ****/
2768 /* try to open the icsLogon script, either in the location given
2769 * or in the users HOME directory
2776 f = fopen(appData.icsLogon, "r");
2779 homedir = getenv("HOME");
2780 if (homedir != NULL)
2782 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2783 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2784 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2785 f = fopen(buf, "r");
2790 ProcessICSInitScript(f);
2792 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2811 GreyRevert (Boolean grey)
2814 if (!menuBarWidget) return;
2815 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2817 DisplayError("menuEdit.Revert", 0);
2819 XtSetSensitive(w, !grey);
2821 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2823 DisplayError("menuEdit.Annotate", 0);
2825 XtSetSensitive(w, !grey);
2830 SetMenuEnables (Enables *enab)
2833 if (!menuBarWidget) return;
2834 while (enab->name != NULL) {
2835 w = XtNameToWidget(menuBarWidget, enab->name);
2837 DisplayError(enab->name, 0);
2839 XtSetSensitive(w, enab->value);
2845 Enables icsEnables[] = {
2846 { "menuFile.Mail Move", False },
2847 { "menuFile.Reload CMail Message", False },
2848 { "menuMode.Machine Black", False },
2849 { "menuMode.Machine White", False },
2850 { "menuMode.Analysis Mode", False },
2851 { "menuMode.Analyze File", False },
2852 { "menuMode.Two Machines", False },
2853 { "menuMode.Machine Match", False },
2855 { "menuEngine.Hint", False },
2856 { "menuEngine.Book", False },
2857 { "menuEngine.Move Now", False },
2858 #ifndef OPTIONSDIALOG
2859 { "menuOptions.Periodic Updates", False },
2860 { "menuOptions.Hide Thinking", False },
2861 { "menuOptions.Ponder Next Move", False },
2864 { "menuEngine.Engine #1 Settings", False },
2865 { "menuEngine.Engine #2 Settings", False },
2866 { "menuEngine.Load Engine", False },
2867 { "menuEdit.Annotate", False },
2868 { "menuOptions.Match", False },
2872 Enables ncpEnables[] = {
2873 { "menuFile.Mail Move", False },
2874 { "menuFile.Reload CMail Message", False },
2875 { "menuMode.Machine White", False },
2876 { "menuMode.Machine Black", False },
2877 { "menuMode.Analysis Mode", False },
2878 { "menuMode.Analyze File", False },
2879 { "menuMode.Two Machines", False },
2880 { "menuMode.Machine Match", False },
2881 { "menuMode.ICS Client", False },
2882 { "menuView.ICStex", False },
2883 { "menuView.ICS Input Box", False },
2884 { "Action", False },
2885 { "menuEdit.Revert", False },
2886 { "menuEdit.Annotate", False },
2887 { "menuEngine.Engine #1 Settings", False },
2888 { "menuEngine.Engine #2 Settings", False },
2889 { "menuEngine.Move Now", False },
2890 { "menuEngine.Retract Move", False },
2891 { "menuOptions.ICS", False },
2892 #ifndef OPTIONSDIALOG
2893 { "menuOptions.Auto Flag", False },
2894 { "menuOptions.Auto Flip View", False },
2895 // { "menuOptions.ICS Alarm", False },
2896 { "menuOptions.Move Sound", False },
2897 { "menuOptions.Hide Thinking", False },
2898 { "menuOptions.Periodic Updates", False },
2899 { "menuOptions.Ponder Next Move", False },
2901 { "menuEngine.Hint", False },
2902 { "menuEngine.Book", False },
2906 Enables gnuEnables[] = {
2907 { "menuMode.ICS Client", False },
2908 { "menuView.ICStex", False },
2909 { "menuView.ICS Input Box", False },
2910 { "menuAction.Accept", False },
2911 { "menuAction.Decline", False },
2912 { "menuAction.Rematch", False },
2913 { "menuAction.Adjourn", False },
2914 { "menuAction.Stop Examining", False },
2915 { "menuAction.Stop Observing", False },
2916 { "menuAction.Upload to Examine", False },
2917 { "menuEdit.Revert", False },
2918 { "menuEdit.Annotate", False },
2919 { "menuOptions.ICS", False },
2921 /* The next two options rely on SetCmailMode being called *after* */
2922 /* SetGNUMode so that when GNU is being used to give hints these */
2923 /* menu options are still available */
2925 { "menuFile.Mail Move", False },
2926 { "menuFile.Reload CMail Message", False },
2927 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2928 { "menuMode.Machine White", True },
2929 { "menuMode.Machine Black", True },
2930 { "menuMode.Analysis Mode", True },
2931 { "menuMode.Analyze File", True },
2932 { "menuMode.Two Machines", True },
2933 { "menuMode.Machine Match", True },
2934 { "menuEngine.Engine #1 Settings", True },
2935 { "menuEngine.Engine #2 Settings", True },
2936 { "menuEngine.Hint", True },
2937 { "menuEngine.Book", True },
2938 { "menuEngine.Move Now", True },
2939 { "menuEngine.Retract Move", True },
2944 Enables cmailEnables[] = {
2946 { "menuAction.Call Flag", False },
2947 { "menuAction.Draw", True },
2948 { "menuAction.Adjourn", False },
2949 { "menuAction.Abort", False },
2950 { "menuAction.Stop Observing", False },
2951 { "menuAction.Stop Examining", False },
2952 { "menuFile.Mail Move", True },
2953 { "menuFile.Reload CMail Message", True },
2957 Enables trainingOnEnables[] = {
2958 { "menuMode.Edit Comment", False },
2959 { "menuMode.Pause", False },
2960 { "menuEdit.Forward", False },
2961 { "menuEdit.Backward", False },
2962 { "menuEdit.Forward to End", False },
2963 { "menuEdit.Back to Start", False },
2964 { "menuEngine.Move Now", False },
2965 { "menuEdit.Truncate Game", False },
2969 Enables trainingOffEnables[] = {
2970 { "menuMode.Edit Comment", True },
2971 { "menuMode.Pause", True },
2972 { "menuEdit.Forward", True },
2973 { "menuEdit.Backward", True },
2974 { "menuEdit.Forward to End", True },
2975 { "menuEdit.Back to Start", True },
2976 { "menuEngine.Move Now", True },
2977 { "menuEdit.Truncate Game", True },
2981 Enables machineThinkingEnables[] = {
2982 { "menuFile.Load Game", False },
2983 // { "menuFile.Load Next Game", False },
2984 // { "menuFile.Load Previous Game", False },
2985 // { "menuFile.Reload Same Game", False },
2986 { "menuEdit.Paste Game", False },
2987 { "menuFile.Load Position", False },
2988 // { "menuFile.Load Next Position", False },
2989 // { "menuFile.Load Previous Position", False },
2990 // { "menuFile.Reload Same Position", False },
2991 { "menuEdit.Paste Position", False },
2992 { "menuMode.Machine White", False },
2993 { "menuMode.Machine Black", False },
2994 { "menuMode.Two Machines", False },
2995 // { "menuMode.Machine Match", False },
2996 { "menuEngine.Retract Move", False },
3000 Enables userThinkingEnables[] = {
3001 { "menuFile.Load Game", True },
3002 // { "menuFile.Load Next Game", True },
3003 // { "menuFile.Load Previous Game", True },
3004 // { "menuFile.Reload Same Game", True },
3005 { "menuEdit.Paste Game", True },
3006 { "menuFile.Load Position", True },
3007 // { "menuFile.Load Next Position", True },
3008 // { "menuFile.Load Previous Position", True },
3009 // { "menuFile.Reload Same Position", True },
3010 { "menuEdit.Paste Position", True },
3011 { "menuMode.Machine White", True },
3012 { "menuMode.Machine Black", True },
3013 { "menuMode.Two Machines", True },
3014 // { "menuMode.Machine Match", True },
3015 { "menuEngine.Retract Move", True },
3022 SetMenuEnables(icsEnables);
3025 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3026 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3027 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3035 SetMenuEnables(ncpEnables);
3041 SetMenuEnables(gnuEnables);
3047 SetMenuEnables(cmailEnables);
3051 SetTrainingModeOn ()
3053 SetMenuEnables(trainingOnEnables);
3054 if (appData.showButtonBar) {
3055 XtSetSensitive(buttonBarWidget, False);
3061 SetTrainingModeOff ()
3063 SetMenuEnables(trainingOffEnables);
3064 if (appData.showButtonBar) {
3065 XtSetSensitive(buttonBarWidget, True);
3070 SetUserThinkingEnables ()
3072 if (appData.noChessProgram) return;
3073 SetMenuEnables(userThinkingEnables);
3077 SetMachineThinkingEnables ()
3079 if (appData.noChessProgram) return;
3080 SetMenuEnables(machineThinkingEnables);
3082 case MachinePlaysBlack:
3083 case MachinePlaysWhite:
3084 case TwoMachinesPlay:
3085 XtSetSensitive(XtNameToWidget(menuBarWidget,
3086 ModeToWidgetName(gameMode)), True);
3093 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3094 #define HISTORY_SIZE 64
3095 static char *history[HISTORY_SIZE];
3096 int histIn = 0, histP = 0;
3099 SaveInHistory (char *cmd)
3101 if (history[histIn] != NULL) {
3102 free(history[histIn]);
3103 history[histIn] = NULL;
3105 if (*cmd == NULLCHAR) return;
3106 history[histIn] = StrSave(cmd);
3107 histIn = (histIn + 1) % HISTORY_SIZE;
3108 if (history[histIn] != NULL) {
3109 free(history[histIn]);
3110 history[histIn] = NULL;
3116 PrevInHistory (char *cmd)
3119 if (histP == histIn) {
3120 if (history[histIn] != NULL) free(history[histIn]);
3121 history[histIn] = StrSave(cmd);
3123 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3124 if (newhp == histIn || history[newhp] == NULL) return NULL;
3126 return history[histP];
3132 if (histP == histIn) return NULL;
3133 histP = (histP + 1) % HISTORY_SIZE;
3134 return history[histP];
3136 // end of borrowed code
3138 #define Abs(n) ((n)<0 ? -(n) : (n))
3142 InsertPxlSize (char *pattern, int targetPxlSize)
3144 char *base_fnt_lst, strInt[12], *p, *q;
3145 int alternatives, i, len, strIntLen;
3148 * Replace the "*" (if present) in the pixel-size slot of each
3149 * alternative with the targetPxlSize.
3153 while ((p = strchr(p, ',')) != NULL) {
3157 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3158 strIntLen = strlen(strInt);
3159 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3163 while (alternatives--) {
3164 char *comma = strchr(p, ',');
3165 for (i=0; i<14; i++) {
3166 char *hyphen = strchr(p, '-');
3168 if (comma && hyphen > comma) break;
3169 len = hyphen + 1 - p;
3170 if (i == 7 && *p == '*' && len == 2) {
3172 memcpy(q, strInt, strIntLen);
3182 len = comma + 1 - p;
3189 return base_fnt_lst;
3193 CreateFontSet (char *base_fnt_lst)
3196 char **missing_list;
3200 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3201 &missing_list, &missing_count, &def_string);
3202 if (appData.debugMode) {
3204 XFontStruct **font_struct_list;
3205 char **font_name_list;
3206 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3208 fprintf(debugFP, " got list %s, locale %s\n",
3209 XBaseFontNameListOfFontSet(fntSet),
3210 XLocaleOfFontSet(fntSet));
3211 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3212 for (i = 0; i < count; i++) {
3213 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3216 for (i = 0; i < missing_count; i++) {
3217 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3220 if (fntSet == NULL) {
3221 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3226 #else // not ENABLE_NLS
3228 * Find a font that matches "pattern" that is as close as
3229 * possible to the targetPxlSize. Prefer fonts that are k
3230 * pixels smaller to fonts that are k pixels larger. The
3231 * pattern must be in the X Consortium standard format,
3232 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3233 * The return value should be freed with XtFree when no
3237 FindFont (char *pattern, int targetPxlSize)
3239 char **fonts, *p, *best, *scalable, *scalableTail;
3240 int i, j, nfonts, minerr, err, pxlSize;
3242 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3244 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3245 programName, pattern);
3252 for (i=0; i<nfonts; i++) {
3255 if (*p != '-') continue;
3257 if (*p == NULLCHAR) break;
3258 if (*p++ == '-') j++;
3260 if (j < 7) continue;
3263 scalable = fonts[i];
3266 err = pxlSize - targetPxlSize;
3267 if (Abs(err) < Abs(minerr) ||
3268 (minerr > 0 && err < 0 && -err == minerr)) {
3274 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3275 /* If the error is too big and there is a scalable font,
3276 use the scalable font. */
3277 int headlen = scalableTail - scalable;
3278 p = (char *) XtMalloc(strlen(scalable) + 10);
3279 while (isdigit(*scalableTail)) scalableTail++;
3280 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3282 p = (char *) XtMalloc(strlen(best) + 2);
3283 safeStrCpy(p, best, strlen(best)+1 );
3285 if (appData.debugMode) {
3286 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3287 pattern, targetPxlSize, p);
3289 XFreeFontNames(fonts);
3296 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3297 // must be called before all non-first callse to CreateGCs()
3298 XtReleaseGC(shellWidget, highlineGC);
3299 XtReleaseGC(shellWidget, lightSquareGC);
3300 XtReleaseGC(shellWidget, darkSquareGC);
3301 XtReleaseGC(shellWidget, lineGC);
3302 if (appData.monoMode) {
3303 if (DefaultDepth(xDisplay, xScreen) == 1) {
3304 XtReleaseGC(shellWidget, wbPieceGC);
3306 XtReleaseGC(shellWidget, bwPieceGC);
3309 XtReleaseGC(shellWidget, prelineGC);
3310 XtReleaseGC(shellWidget, jailSquareGC);
3311 XtReleaseGC(shellWidget, wdPieceGC);
3312 XtReleaseGC(shellWidget, wlPieceGC);
3313 XtReleaseGC(shellWidget, wjPieceGC);
3314 XtReleaseGC(shellWidget, bdPieceGC);
3315 XtReleaseGC(shellWidget, blPieceGC);
3316 XtReleaseGC(shellWidget, bjPieceGC);
3321 CreateGCs (int redo)
3323 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3324 | GCBackground | GCFunction | GCPlaneMask;
3325 XGCValues gc_values;
3328 gc_values.plane_mask = AllPlanes;
3329 gc_values.line_width = lineGap;
3330 gc_values.line_style = LineSolid;
3331 gc_values.function = GXcopy;
3334 DeleteGCs(); // called a second time; clean up old GCs first
3335 } else { // [HGM] grid and font GCs created on first call only
3336 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3337 gc_values.background = XWhitePixel(xDisplay, xScreen);
3338 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3339 XSetFont(xDisplay, coordGC, coordFontID);
3341 // [HGM] make font for holdings counts (white on black)
3342 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3343 gc_values.background = XBlackPixel(xDisplay, xScreen);
3344 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3345 XSetFont(xDisplay, countGC, countFontID);
3347 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3348 gc_values.background = XBlackPixel(xDisplay, xScreen);
3349 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3351 if (appData.monoMode) {
3352 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3353 gc_values.background = XWhitePixel(xDisplay, xScreen);
3354 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3356 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3357 gc_values.background = XBlackPixel(xDisplay, xScreen);
3358 lightSquareGC = wbPieceGC
3359 = XtGetGC(shellWidget, value_mask, &gc_values);
3361 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3362 gc_values.background = XWhitePixel(xDisplay, xScreen);
3363 darkSquareGC = bwPieceGC
3364 = XtGetGC(shellWidget, value_mask, &gc_values);
3366 if (DefaultDepth(xDisplay, xScreen) == 1) {
3367 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3368 gc_values.function = GXcopyInverted;
3369 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3370 gc_values.function = GXcopy;
3371 if (XBlackPixel(xDisplay, xScreen) == 1) {
3372 bwPieceGC = darkSquareGC;
3373 wbPieceGC = copyInvertedGC;
3375 bwPieceGC = copyInvertedGC;
3376 wbPieceGC = lightSquareGC;
3380 gc_values.foreground = highlightSquareColor;
3381 gc_values.background = highlightSquareColor;
3382 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3384 gc_values.foreground = premoveHighlightColor;
3385 gc_values.background = premoveHighlightColor;
3386 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3388 gc_values.foreground = lightSquareColor;
3389 gc_values.background = darkSquareColor;
3390 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3392 gc_values.foreground = darkSquareColor;
3393 gc_values.background = lightSquareColor;
3394 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3396 gc_values.foreground = jailSquareColor;
3397 gc_values.background = jailSquareColor;
3398 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3400 gc_values.foreground = whitePieceColor;
3401 gc_values.background = darkSquareColor;
3402 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3404 gc_values.foreground = whitePieceColor;
3405 gc_values.background = lightSquareColor;
3406 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3408 gc_values.foreground = whitePieceColor;
3409 gc_values.background = jailSquareColor;
3410 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3412 gc_values.foreground = blackPieceColor;
3413 gc_values.background = darkSquareColor;
3414 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3416 gc_values.foreground = blackPieceColor;
3417 gc_values.background = lightSquareColor;
3418 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3420 gc_values.foreground = blackPieceColor;
3421 gc_values.background = jailSquareColor;
3422 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3427 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3435 fp = fopen(filename, "rb");
3437 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3444 for (y=0; y<h; ++y) {
3445 for (x=0; x<h; ++x) {
3450 XPutPixel(xim, x, y, blackPieceColor);
3452 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3455 XPutPixel(xim, x, y, darkSquareColor);
3457 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3460 XPutPixel(xim, x, y, whitePieceColor);
3462 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3465 XPutPixel(xim, x, y, lightSquareColor);
3467 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3475 /* create Pixmap of piece */
3476 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3478 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3481 /* create Pixmap of clipmask
3482 Note: We assume the white/black pieces have the same
3483 outline, so we make only 6 masks. This is okay
3484 since the XPM clipmask routines do the same. */
3486 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3488 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3491 /* now create the 1-bit version */
3492 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3495 values.foreground = 1;
3496 values.background = 0;
3498 /* Don't use XtGetGC, not read only */
3499 maskGC = XCreateGC(xDisplay, *mask,
3500 GCForeground | GCBackground, &values);
3501 XCopyPlane(xDisplay, temp, *mask, maskGC,
3502 0, 0, squareSize, squareSize, 0, 0, 1);
3503 XFreePixmap(xDisplay, temp);
3508 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3516 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3521 /* The XSynchronize calls were copied from CreatePieces.
3522 Not sure if needed, but can't hurt */
3523 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3526 /* temp needed by loadXIM() */
3527 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3528 0, 0, ss, ss, AllPlanes, XYPixmap);
3530 if (strlen(appData.pixmapDirectory) == 0) {
3534 if (appData.monoMode) {
3535 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3539 fprintf(stderr, _("\nLoading XIMs...\n"));
3541 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3542 fprintf(stderr, "%d", piece+1);
3543 for (kind=0; kind<4; kind++) {
3544 fprintf(stderr, ".");
3545 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3546 ExpandPathName(appData.pixmapDirectory),
3547 piece <= (int) WhiteKing ? "" : "w",
3548 pieceBitmapNames[piece],
3550 ximPieceBitmap[kind][piece] =
3551 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3552 0, 0, ss, ss, AllPlanes, XYPixmap);
3553 if (appData.debugMode)
3554 fprintf(stderr, _("(File:%s:) "), buf);
3555 loadXIM(ximPieceBitmap[kind][piece],
3557 &(xpmPieceBitmap2[kind][piece]),
3558 &(ximMaskPm2[piece]));
3559 if(piece <= (int)WhiteKing)
3560 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3562 fprintf(stderr," ");
3564 /* Load light and dark squares */
3565 /* If the LSQ and DSQ pieces don't exist, we will
3566 draw them with solid squares. */
3567 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3568 if (access(buf, 0) != 0) {
3572 fprintf(stderr, _("light square "));
3574 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3575 0, 0, ss, ss, AllPlanes, XYPixmap);
3576 if (appData.debugMode)
3577 fprintf(stderr, _("(File:%s:) "), buf);
3579 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3580 fprintf(stderr, _("dark square "));
3581 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3582 ExpandPathName(appData.pixmapDirectory), ss);
3583 if (appData.debugMode)
3584 fprintf(stderr, _("(File:%s:) "), buf);
3586 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3587 0, 0, ss, ss, AllPlanes, XYPixmap);
3588 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3589 xpmJailSquare = xpmLightSquare;
3591 fprintf(stderr, _("Done.\n"));
3593 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3596 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3600 CreateXPMBoard (char *s, int kind)
3604 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3605 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3606 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3612 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3613 // thisroutine has to be called t free the old piece pixmaps
3615 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3616 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3618 XFreePixmap(xDisplay, xpmLightSquare);
3619 XFreePixmap(xDisplay, xpmDarkSquare);
3628 u_int ss = squareSize;
3630 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3631 XpmColorSymbol symbols[4];
3632 static int redo = False;
3634 if(redo) FreeXPMPieces(); else redo = 1;
3636 /* The XSynchronize calls were copied from CreatePieces.
3637 Not sure if needed, but can't hurt */
3638 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3640 /* Setup translations so piece colors match square colors */
3641 symbols[0].name = "light_piece";
3642 symbols[0].value = appData.whitePieceColor;
3643 symbols[1].name = "dark_piece";
3644 symbols[1].value = appData.blackPieceColor;
3645 symbols[2].name = "light_square";
3646 symbols[2].value = appData.lightSquareColor;
3647 symbols[3].name = "dark_square";
3648 symbols[3].value = appData.darkSquareColor;
3650 attr.valuemask = XpmColorSymbols;
3651 attr.colorsymbols = symbols;
3652 attr.numsymbols = 4;
3654 if (appData.monoMode) {
3655 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3659 if (strlen(appData.pixmapDirectory) == 0) {
3660 XpmPieces* pieces = builtInXpms;
3663 while (pieces->size != squareSize && pieces->size) pieces++;
3664 if (!pieces->size) {
3665 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3668 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3669 for (kind=0; kind<4; kind++) {
3671 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3672 pieces->xpm[piece][kind],
3673 &(xpmPieceBitmap2[kind][piece]),
3674 NULL, &attr)) != 0) {
3675 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3679 if(piece <= (int) WhiteKing)
3680 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3684 xpmJailSquare = xpmLightSquare;
3688 fprintf(stderr, _("\nLoading XPMs...\n"));
3691 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3692 fprintf(stderr, "%d ", piece+1);
3693 for (kind=0; kind<4; kind++) {
3694 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3695 ExpandPathName(appData.pixmapDirectory),
3696 piece > (int) WhiteKing ? "w" : "",
3697 pieceBitmapNames[piece],
3699 if (appData.debugMode) {
3700 fprintf(stderr, _("(File:%s:) "), buf);
3702 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3703 &(xpmPieceBitmap2[kind][piece]),
3704 NULL, &attr)) != 0) {
3705 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3706 // [HGM] missing: read of unorthodox piece failed; substitute King.
3707 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3708 ExpandPathName(appData.pixmapDirectory),
3710 if (appData.debugMode) {
3711 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3713 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3714 &(xpmPieceBitmap2[kind][piece]),
3718 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3723 if(piece <= (int) WhiteKing)
3724 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3727 /* Load light and dark squares */
3728 /* If the LSQ and DSQ pieces don't exist, we will
3729 draw them with solid squares. */
3730 fprintf(stderr, _("light square "));
3731 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3732 if (access(buf, 0) != 0) {
3736 if (appData.debugMode)
3737 fprintf(stderr, _("(File:%s:) "), buf);
3739 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3740 &xpmLightSquare, NULL, &attr)) != 0) {
3741 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3744 fprintf(stderr, _("dark square "));
3745 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3746 ExpandPathName(appData.pixmapDirectory), ss);
3747 if (appData.debugMode) {
3748 fprintf(stderr, _("(File:%s:) "), buf);
3750 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3751 &xpmDarkSquare, NULL, &attr)) != 0) {
3752 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3756 xpmJailSquare = xpmLightSquare;
3757 fprintf(stderr, _("Done.\n"));
3759 oldVariant = -1; // kludge to force re-makig of animation masks
3760 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3763 #endif /* HAVE_LIBXPM */
3766 /* No built-in bitmaps */
3771 u_int ss = squareSize;
3773 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3776 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3777 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3778 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3779 pieceBitmapNames[piece],
3780 ss, kind == SOLID ? 's' : 'o');
3781 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3782 if(piece <= (int)WhiteKing)
3783 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3787 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3791 /* With built-in bitmaps */
3795 BuiltInBits* bib = builtInBits;
3798 u_int ss = squareSize;
3800 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3803 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3805 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3806 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3807 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3808 pieceBitmapNames[piece],
3809 ss, kind == SOLID ? 's' : 'o');
3810 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3811 bib->bits[kind][piece], ss, ss);
3812 if(piece <= (int)WhiteKing)
3813 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3817 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3823 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3828 char msg[MSG_SIZ], fullname[MSG_SIZ];
3830 if (*appData.bitmapDirectory != NULLCHAR) {
3831 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3832 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3833 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3834 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3835 &w, &h, pm, &x_hot, &y_hot);
3836 fprintf(stderr, "load %s\n", name);
3837 if (errcode != BitmapSuccess) {
3839 case BitmapOpenFailed:
3840 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3842 case BitmapFileInvalid:
3843 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3845 case BitmapNoMemory:
3846 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3850 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3854 fprintf(stderr, _("%s: %s...using built-in\n"),
3856 } else if (w != wreq || h != hreq) {
3858 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3859 programName, fullname, w, h, wreq, hreq);
3865 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3875 if (lineGap == 0) return;
3877 /* [HR] Split this into 2 loops for non-square boards. */
3879 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3880 gridSegments[i].x1 = 0;
3881 gridSegments[i].x2 =
3882 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3883 gridSegments[i].y1 = gridSegments[i].y2
3884 = lineGap / 2 + (i * (squareSize + lineGap));
3887 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3888 gridSegments[j + i].y1 = 0;
3889 gridSegments[j + i].y2 =
3890 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3891 gridSegments[j + i].x1 = gridSegments[j + i].x2
3892 = lineGap / 2 + (j * (squareSize + lineGap));
3897 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3899 XtActionProc proc = (XtActionProc) addr;
3901 (proc)(NULL, NULL, NULL, NULL);
3905 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3907 RecentEngineEvent((int) addr);
3911 AppendEnginesToMenu (Widget menu, char *list)
3919 if(appData.recentEngines <= 0) return;
3920 recentEngines = strdup(list);
3922 XtSetArg(args[j], XtNleftMargin, 20); j++;
3923 XtSetArg(args[j], XtNrightMargin, 20); j++;
3925 p = strchr(list, '\n'); if(p == NULL) break;
3926 if(i == 0) XtCreateManagedWidget(_("----"), smeLineObjectClass, menu, args, j); // at least one valid item to add
3928 XtSetArg(args[j], XtNlabel, XtNewString(list));
3929 entry = XtCreateManagedWidget("engine", smeBSBObjectClass, menu, args, j+1);
3930 XtAddCallback(entry, XtNcallback,
3931 (XtCallbackProc) MenuEngineSelect,
3933 i++; *p = '\n'; list = p + 1;
3938 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3945 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3948 XtSetArg(args[j], XtNleftMargin, 20); j++;
3949 XtSetArg(args[j], XtNrightMargin, 20); j++;
3951 while (mi->string != NULL) {
3952 if (strcmp(mi->string, "----") == 0) {
3953 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3956 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3957 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3959 XtAddCallback(entry, XtNcallback,
3960 (XtCallbackProc) MenuBarSelect,
3961 (caddr_t) mi->proc);
3965 if(!strcmp(mb->name, "Engine")) AppendEnginesToMenu(menu, appData.recentEngineList);
3969 CreateMenuBar (Menu *mb, int boardWidth)
3971 int i, j, nr = 0, wtot = 0, widths[10];
3974 char menuName[MSG_SIZ];
3979 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3980 XtSetArg(args[j], XtNvSpace, 0); j++;
3981 XtSetArg(args[j], XtNborderWidth, 0); j++;
3982 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3983 formWidget, args, j);
3985 while (mb->name != NULL) {
3986 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3987 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3989 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3990 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3991 XtSetArg(args[j], XtNborderWidth, 0); j++;
3992 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3994 CreateMenuBarPopup(menuBar, menuName, mb);
3996 XtSetArg(args[j], XtNwidth, &w); j++;
3997 XtGetValues(mb->subMenu, args, j);
3998 wtot += mb->textWidth = widths[nr++] = w;
4001 while(wtot > boardWidth - 40) {
4003 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
4007 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
4009 XtSetArg(args[j], XtNwidth, widths[i]); j++;
4010 XtSetValues(ma[i].subMenu, args, j);
4016 CreateButtonBar (MenuItem *mi)
4019 Widget button, buttonBar;
4023 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4025 XtSetArg(args[j], XtNhSpace, 0); j++;
4027 XtSetArg(args[j], XtNborderWidth, 0); j++;
4028 XtSetArg(args[j], XtNvSpace, 0); j++;
4029 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4030 formWidget, args, j);
4032 while (mi->string != NULL) {
4035 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4036 XtSetArg(args[j], XtNborderWidth, 0); j++;
4038 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4039 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4040 buttonBar, args, j);
4041 XtAddCallback(button, XtNcallback,
4042 (XtCallbackProc) MenuBarSelect,
4043 (caddr_t) mi->proc);
4050 CreatePieceMenu (char *name, int color)
4055 ChessSquare selection;
4057 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4058 boardWidget, args, 0);
4060 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4061 String item = pieceMenuStrings[color][i];
4063 if (strcmp(item, "----") == 0) {
4064 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4067 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4068 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4070 selection = pieceMenuTranslation[color][i];
4071 XtAddCallback(entry, XtNcallback,
4072 (XtCallbackProc) PieceMenuSelect,
4073 (caddr_t) selection);
4074 if (selection == WhitePawn || selection == BlackPawn) {
4075 XtSetArg(args[0], XtNpopupOnEntry, entry);
4076 XtSetValues(menu, args, 1);
4089 ChessSquare selection;
4091 whitePieceMenu = CreatePieceMenu("menuW", 0);
4092 blackPieceMenu = CreatePieceMenu("menuB", 1);
4094 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
4095 XtRegisterGrabAction(PieceMenuPopup, True,
4096 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4097 GrabModeAsync, GrabModeAsync);
4099 XtSetArg(args[0], XtNlabel, _("Drop"));
4100 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4101 boardWidget, args, 1);
4102 for (i = 0; i < DROP_MENU_SIZE; i++) {
4103 String item = dropMenuStrings[i];
4105 if (strcmp(item, "----") == 0) {
4106 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4109 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4110 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4112 selection = dropMenuTranslation[i];
4113 XtAddCallback(entry, XtNcallback,
4114 (XtCallbackProc) DropMenuSelect,
4115 (caddr_t) selection);
4129 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4130 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4131 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4132 dmEnables[i].piece);
4133 XtSetSensitive(entry, p != NULL || !appData.testLegality
4134 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4135 && !appData.icsActive));
4137 while (p && *p++ == dmEnables[i].piece) count++;
4138 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4140 XtSetArg(args[j], XtNlabel, label); j++;
4141 XtSetValues(entry, args, j);
4146 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4148 String whichMenu; int menuNr = -2;
4149 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4150 if (event->type == ButtonRelease)
4151 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4152 else if (event->type == ButtonPress)
4153 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4155 case 0: whichMenu = params[0]; break;
4156 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4158 case -1: if (errorUp) ErrorPopDown();
4161 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4165 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4167 if (pmFromX < 0 || pmFromY < 0) return;
4168 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4172 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4174 if (pmFromX < 0 || pmFromY < 0) return;
4175 DropMenuEvent(piece, pmFromX, pmFromY);
4179 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4181 shiftKey = prms[0][0] & 1;
4186 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4188 shiftKey = prms[0][0] & 1;
4194 * If the user selects on a border boundary, return -1; if off the board,
4195 * return -2. Otherwise map the event coordinate to the square.
4198 EventToSquare (int x, int limit)
4205 if ((x % (squareSize + lineGap)) >= squareSize)
4207 x /= (squareSize + lineGap);
4214 do_flash_delay (unsigned long msec)
4220 drawHighlight (int file, int rank, GC gc)
4224 if (lineGap == 0) return;
4227 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4228 (squareSize + lineGap);
4229 y = lineGap/2 + rank * (squareSize + lineGap);
4231 x = lineGap/2 + file * (squareSize + lineGap);
4232 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4233 (squareSize + lineGap);
4236 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4237 squareSize+lineGap, squareSize+lineGap);
4240 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4241 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4244 SetHighlights (int fromX, int fromY, int toX, int toY)
4246 if (hi1X != fromX || hi1Y != fromY) {
4247 if (hi1X >= 0 && hi1Y >= 0) {
4248 drawHighlight(hi1X, hi1Y, lineGC);
4250 } // [HGM] first erase both, then draw new!
4251 if (hi2X != toX || hi2Y != toY) {
4252 if (hi2X >= 0 && hi2Y >= 0) {
4253 drawHighlight(hi2X, hi2Y, lineGC);
4256 if (hi1X != fromX || hi1Y != fromY) {
4257 if (fromX >= 0 && fromY >= 0) {
4258 drawHighlight(fromX, fromY, highlineGC);
4261 if (hi2X != toX || hi2Y != toY) {
4262 if (toX >= 0 && toY >= 0) {
4263 drawHighlight(toX, toY, highlineGC);
4266 if(toX<0) // clearing the highlights must have damaged arrow
4267 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4277 SetHighlights(-1, -1, -1, -1);
4282 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4284 if (pm1X != fromX || pm1Y != fromY) {
4285 if (pm1X >= 0 && pm1Y >= 0) {
4286 drawHighlight(pm1X, pm1Y, lineGC);
4288 if (fromX >= 0 && fromY >= 0) {
4289 drawHighlight(fromX, fromY, prelineGC);
4292 if (pm2X != toX || pm2Y != toY) {
4293 if (pm2X >= 0 && pm2Y >= 0) {
4294 drawHighlight(pm2X, pm2Y, lineGC);
4296 if (toX >= 0 && toY >= 0) {
4297 drawHighlight(toX, toY, prelineGC);
4307 ClearPremoveHighlights ()
4309 SetPremoveHighlights(-1, -1, -1, -1);
4313 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4315 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4316 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4318 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4319 if(textureW[kind] < W*squareSize)
4320 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4322 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4323 if(textureH[kind] < H*squareSize)
4324 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4326 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4331 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4332 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4334 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4335 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4336 squareSize, squareSize, x*fac, y*fac);
4338 if (useImages && useImageSqs) {
4342 pm = xpmLightSquare;
4347 case 2: /* neutral */
4352 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4353 squareSize, squareSize, x*fac, y*fac);
4363 case 2: /* neutral */
4368 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4373 I split out the routines to draw a piece so that I could
4374 make a generic flash routine.
4377 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4379 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4380 switch (square_color) {
4382 case 2: /* neutral */
4384 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4385 ? *pieceToOutline(piece)
4386 : *pieceToSolid(piece),
4387 dest, bwPieceGC, 0, 0,
4388 squareSize, squareSize, x, y);
4391 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4392 ? *pieceToSolid(piece)
4393 : *pieceToOutline(piece),
4394 dest, wbPieceGC, 0, 0,
4395 squareSize, squareSize, x, y);
4401 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4403 switch (square_color) {
4405 case 2: /* neutral */
4407 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4408 ? *pieceToOutline(piece)
4409 : *pieceToSolid(piece),
4410 dest, bwPieceGC, 0, 0,
4411 squareSize, squareSize, x, y, 1);
4414 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4415 ? *pieceToSolid(piece)
4416 : *pieceToOutline(piece),
4417 dest, wbPieceGC, 0, 0,
4418 squareSize, squareSize, x, y, 1);
4424 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4426 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4427 switch (square_color) {
4429 XCopyPlane(xDisplay, *pieceToSolid(piece),
4430 dest, (int) piece < (int) BlackPawn
4431 ? wlPieceGC : blPieceGC, 0, 0,
4432 squareSize, squareSize, x, y, 1);
4435 XCopyPlane(xDisplay, *pieceToSolid(piece),
4436 dest, (int) piece < (int) BlackPawn
4437 ? wdPieceGC : bdPieceGC, 0, 0,
4438 squareSize, squareSize, x, y, 1);
4440 case 2: /* neutral */
4442 XCopyPlane(xDisplay, *pieceToSolid(piece),
4443 dest, (int) piece < (int) BlackPawn
4444 ? wjPieceGC : bjPieceGC, 0, 0,
4445 squareSize, squareSize, x, y, 1);
4451 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4453 int kind, p = piece;
4455 switch (square_color) {
4457 case 2: /* neutral */
4459 if ((int)piece < (int) BlackPawn) {
4467 if ((int)piece < (int) BlackPawn) {
4475 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4476 if(useTexture & square_color+1) {
4477 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4478 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4479 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4480 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4481 XSetClipMask(xDisplay, wlPieceGC, None);
4482 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4484 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4485 dest, wlPieceGC, 0, 0,
4486 squareSize, squareSize, x, y);
4489 typedef void (*DrawFunc)();
4494 if (appData.monoMode) {
4495 if (DefaultDepth(xDisplay, xScreen) == 1) {
4496 return monoDrawPiece_1bit;
4498 return monoDrawPiece;
4502 return colorDrawPieceImage;
4504 return colorDrawPiece;
4508 /* [HR] determine square color depending on chess variant. */
4510 SquareColor (int row, int column)
4514 if (gameInfo.variant == VariantXiangqi) {
4515 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4517 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4519 } else if (row <= 4) {
4525 square_color = ((column + row) % 2) == 1;
4528 /* [hgm] holdings: next line makes all holdings squares light */
4529 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4531 return square_color;
4535 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4537 int square_color, x, y, direction, font_ascent, font_descent;
4540 XCharStruct overall;
4544 /* Calculate delay in milliseconds (2-delays per complete flash) */
4545 flash_delay = 500 / appData.flashRate;
4548 x = lineGap + ((BOARD_WIDTH-1)-column) *
4549 (squareSize + lineGap);
4550 y = lineGap + row * (squareSize + lineGap);
4552 x = lineGap + column * (squareSize + lineGap);
4553 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4554 (squareSize + lineGap);
4557 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4559 square_color = SquareColor(row, column);
4561 if ( // [HGM] holdings: blank out area between board and holdings
4562 column == BOARD_LEFT-1 || column == BOARD_RGHT
4563 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4564 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4565 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4567 // [HGM] print piece counts next to holdings
4568 string[1] = NULLCHAR;
4569 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4570 string[0] = '0' + piece;
4571 XTextExtents(countFontStruct, string, 1, &direction,
4572 &font_ascent, &font_descent, &overall);
4573 if (appData.monoMode) {
4574 XDrawImageString(xDisplay, xBoardWindow, countGC,
4575 x + squareSize - overall.width - 2,
4576 y + font_ascent + 1, string, 1);
4578 XDrawString(xDisplay, xBoardWindow, countGC,
4579 x + squareSize - overall.width - 2,
4580 y + font_ascent + 1, string, 1);
4583 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4584 string[0] = '0' + piece;
4585 XTextExtents(countFontStruct, string, 1, &direction,
4586 &font_ascent, &font_descent, &overall);
4587 if (appData.monoMode) {
4588 XDrawImageString(xDisplay, xBoardWindow, countGC,
4589 x + 2, y + font_ascent + 1, string, 1);
4591 XDrawString(xDisplay, xBoardWindow, countGC,
4592 x + 2, y + font_ascent + 1, string, 1);
4596 if (piece == EmptySquare || appData.blindfold) {
4597 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4599 drawfunc = ChooseDrawFunc();
4601 if (do_flash && appData.flashCount > 0) {
4602 for (i=0; i<appData.flashCount; ++i) {
4603 drawfunc(piece, square_color, x, y, xBoardWindow);
4604 XSync(xDisplay, False);
4605 do_flash_delay(flash_delay);
4607 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4608 XSync(xDisplay, False);
4609 do_flash_delay(flash_delay);
4612 drawfunc(piece, square_color, x, y, xBoardWindow);
4616 string[1] = NULLCHAR;
4617 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4618 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4619 string[0] = 'a' + column - BOARD_LEFT;
4620 XTextExtents(coordFontStruct, string, 1, &direction,
4621 &font_ascent, &font_descent, &overall);
4622 if (appData.monoMode) {
4623 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4624 x + squareSize - overall.width - 2,
4625 y + squareSize - font_descent - 1, string, 1);
4627 XDrawString(xDisplay, xBoardWindow, coordGC,
4628 x + squareSize - overall.width - 2,
4629 y + squareSize - font_descent - 1, string, 1);
4632 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4633 string[0] = ONE + row;
4634 XTextExtents(coordFontStruct, string, 1, &direction,
4635 &font_ascent, &font_descent, &overall);
4636 if (appData.monoMode) {
4637 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4638 x + 2, y + font_ascent + 1, string, 1);
4640 XDrawString(xDisplay, xBoardWindow, coordGC,
4641 x + 2, y + font_ascent + 1, string, 1);
4644 if(!partnerUp && marker[row][column]) {
4645 if(appData.monoMode) {
4646 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4647 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4648 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4649 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4651 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4652 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4657 Fraction (int x, int start, int stop)
4659 double f = ((double) x - start)/(stop - start);
4660 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
4664 static WindowPlacement wpNew;
4667 CoDrag (Widget sh, WindowPlacement *wp)
4670 int j=0, touch=0, fudge = 2;
4671 GetActualPlacement(sh, wp);
4672 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
4673 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
4674 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
4675 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
4676 if(!touch ) return; // only windows that touch co-move
4677 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
4678 int heightInc = wpNew.height - wpMain.height;
4679 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
4680 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
4681 wp->y += fracTop * heightInc;
4682 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
4683 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
4684 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
4685 int widthInc = wpNew.width - wpMain.width;
4686 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
4687 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
4688 wp->y += fracLeft * widthInc;
4689 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
4690 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
4692 wp->x += wpNew.x - wpMain.x;
4693 wp->y += wpNew.y - wpMain.y;
4694 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
4695 if(touch == 3) wp->y += wpNew.height - wpMain.height;
4696 XtSetArg(args[j], XtNx, wp->x); j++;
4697 XtSetArg(args[j], XtNy, wp->y); j++;
4698 XtSetValues(sh, args, j);
4704 GetActualPlacement(shellWidget, &wpNew);
4705 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
4706 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
4707 return; // false alarm
4708 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
4709 if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
4710 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
4711 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
4713 XDrawPosition(boardWidget, True, NULL);
4720 static XtIntervalId delayedDragID = 0;
4721 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
4723 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
4726 /* Why is this needed on some versions of X? */
4728 EventProc (Widget widget, caddr_t unused, XEvent *event)
4730 if (!XtIsRealized(widget))
4732 switch (event->type) {
4733 case ConfigureNotify: // main window is being dragged: drag attached windows with it
4734 if(appData.useStickyWindows)
4735 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
4738 if (event->xexpose.count > 0) return; /* no clipping is done */
4739 XDrawPosition(widget, True, NULL);
4740 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4741 flipView = !flipView; partnerUp = !partnerUp;
4742 XDrawPosition(widget, True, NULL);
4743 flipView = !flipView; partnerUp = !partnerUp;
4747 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4755 DrawPosition (int fullRedraw, Board board)
4757 XDrawPosition(boardWidget, fullRedraw, board);
4760 /* Returns 1 if there are "too many" differences between b1 and b2
4761 (i.e. more than 1 move was made) */
4763 too_many_diffs (Board b1, Board b2)
4768 for (i=0; i<BOARD_HEIGHT; ++i) {
4769 for (j=0; j<BOARD_WIDTH; ++j) {
4770 if (b1[i][j] != b2[i][j]) {
4771 if (++c > 4) /* Castling causes 4 diffs */
4779 /* Matrix describing castling maneuvers */
4780 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4781 static int castling_matrix[4][5] = {
4782 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4783 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4784 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4785 { 7, 7, 4, 5, 6 } /* 0-0, black */
4788 /* Checks whether castling occurred. If it did, *rrow and *rcol
4789 are set to the destination (row,col) of the rook that moved.
4791 Returns 1 if castling occurred, 0 if not.
4793 Note: Only handles a max of 1 castling move, so be sure
4794 to call too_many_diffs() first.
4797 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4802 /* For each type of castling... */
4803 for (i=0; i<4; ++i) {
4804 r = castling_matrix[i];
4806 /* Check the 4 squares involved in the castling move */
4808 for (j=1; j<=4; ++j) {
4809 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4816 /* All 4 changed, so it must be a castling move */
4825 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4827 DrawSeekAxis (int x, int y, int xTo, int yTo)
4829 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4833 DrawSeekBackground (int left, int top, int right, int bottom)
4835 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4839 DrawSeekText (char *buf, int x, int y)
4841 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4845 DrawSeekDot (int x, int y, int colorNr)
4847 int square = colorNr & 0x80;
4850 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4852 XFillRectangle(xDisplay, xBoardWindow, color,
4853 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4855 XFillArc(xDisplay, xBoardWindow, color,
4856 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4859 static int damage[2][BOARD_RANKS][BOARD_FILES];
4862 * event handler for redrawing the board
4865 XDrawPosition (Widget w, int repaint, Board board)
4868 static int lastFlipView = 0;
4869 static int lastBoardValid[2] = {0, 0};
4870 static Board lastBoard[2];
4873 int nr = twoBoards*partnerUp;
4875 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4877 if (board == NULL) {
4878 if (!lastBoardValid[nr]) return;
4879 board = lastBoard[nr];
4881 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4882 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4883 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4888 * It would be simpler to clear the window with XClearWindow()
4889 * but this causes a very distracting flicker.
4892 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4894 if ( lineGap && IsDrawArrowEnabled())
4895 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4896 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4898 /* If too much changes (begin observing new game, etc.), don't
4900 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4902 /* Special check for castling so we don't flash both the king
4903 and the rook (just flash the king). */
4905 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4906 /* Draw rook with NO flashing. King will be drawn flashing later */
4907 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4908 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4912 /* First pass -- Draw (newly) empty squares and repair damage.
4913 This prevents you from having a piece show up twice while it
4914 is flashing on its new square */
4915 for (i = 0; i < BOARD_HEIGHT; i++)
4916 for (j = 0; j < BOARD_WIDTH; j++)
4917 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4918 || damage[nr][i][j]) {
4919 DrawSquare(i, j, board[i][j], 0);
4920 damage[nr][i][j] = False;
4923 /* Second pass -- Draw piece(s) in new position and flash them */
4924 for (i = 0; i < BOARD_HEIGHT; i++)
4925 for (j = 0; j < BOARD_WIDTH; j++)
4926 if (board[i][j] != lastBoard[nr][i][j]) {
4927 DrawSquare(i, j, board[i][j], do_flash);
4931 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4932 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4933 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4935 for (i = 0; i < BOARD_HEIGHT; i++)
4936 for (j = 0; j < BOARD_WIDTH; j++) {
4937 DrawSquare(i, j, board[i][j], 0);
4938 damage[nr][i][j] = False;
4942 CopyBoard(lastBoard[nr], board);
4943 lastBoardValid[nr] = 1;
4944 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4945 lastFlipView = flipView;
4947 /* Draw highlights */
4948 if (pm1X >= 0 && pm1Y >= 0) {
4949 drawHighlight(pm1X, pm1Y, prelineGC);
4951 if (pm2X >= 0 && pm2Y >= 0) {
4952 drawHighlight(pm2X, pm2Y, prelineGC);
4954 if (hi1X >= 0 && hi1Y >= 0) {
4955 drawHighlight(hi1X, hi1Y, highlineGC);
4957 if (hi2X >= 0 && hi2Y >= 0) {
4958 drawHighlight(hi2X, hi2Y, highlineGC);
4960 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4962 /* If piece being dragged around board, must redraw that too */
4965 XSync(xDisplay, False);
4970 * event handler for redrawing the board
4973 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4975 XDrawPosition(w, True, NULL);
4980 * event handler for parsing user moves
4982 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4983 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4984 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4985 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4986 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4987 // and at the end FinishMove() to perform the move after optional promotion popups.
4988 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4990 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4992 if (w != boardWidget || errorExitStatus != -1) return;
4993 if(nprms) shiftKey = !strcmp(prms[0], "1");
4996 if (event->type == ButtonPress) {
4997 XtPopdown(promotionShell);
4998 XtDestroyWidget(promotionShell);
4999 promotionUp = False;
5007 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
5008 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
5009 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
5013 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
5015 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
5016 DragPieceMove(event->xmotion.x, event->xmotion.y);
5020 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
5021 { // [HGM] pv: walk PV
5022 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
5025 static int savedIndex; /* gross that this is global */
5028 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
5031 XawTextPosition index, dummy;
5034 XawTextGetSelectionPos(w, &index, &dummy);
5035 XtSetArg(arg, XtNstring, &val);
5036 XtGetValues(w, &arg, 1);
5037 ReplaceComment(savedIndex, val);
5038 if(savedIndex != currentMove) ToNrEvent(savedIndex);
5039 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
5043 EditCommentPopUp (int index, char *title, char *text)
5046 if (text == NULL) text = "";
5047 NewCommentPopup(title, text, index);
5056 extern Option boxOptions[];
5066 edit = boxOptions[0].handle;
5068 XtSetArg(args[j], XtNstring, &val); j++;
5069 XtGetValues(edit, args, j);
5071 SendMultiLineToICS(val);
5072 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5073 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5077 ICSInputBoxPopDown ()
5083 CommentPopUp (char *title, char *text)
5085 savedIndex = currentMove; // [HGM] vari
5086 NewCommentPopup(title, text, currentMove);
5095 static char *openName;
5101 (void) (*fileProc)(openFP, 0, openName);
5105 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
5107 fileProc = proc; /* I can't see a way not */
5108 fileOpenMode = openMode; /* to use globals here */
5109 { // [HGM] use file-selector dialog stolen from Ghostview
5110 int index; // this is not supported yet
5111 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5112 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5113 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5114 ScheduleDelayedEvent(&DelayedLoad, 50);
5121 if (!filenameUp) return;
5122 XtPopdown(fileNameShell);
5123 XtDestroyWidget(fileNameShell);
5129 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5134 XtSetArg(args[0], XtNlabel, &name);
5135 XtGetValues(w, args, 1);
5137 if (strcmp(name, _("cancel")) == 0) {
5142 FileNameAction(w, NULL, NULL, NULL);
5146 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5154 name = XawDialogGetValueString(w = XtParent(w));
5156 if ((name != NULL) && (*name != NULLCHAR)) {
5157 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5158 XtPopdown(w = XtParent(XtParent(w)));
5162 p = strrchr(buf, ' ');
5169 fullname = ExpandPathName(buf);
5171 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5174 f = fopen(fullname, fileOpenMode);
5176 DisplayError(_("Failed to open file"), errno);
5178 (void) (*fileProc)(f, index, buf);
5185 XtPopdown(w = XtParent(XtParent(w)));
5195 Widget dialog, layout;
5197 Dimension bw_width, pw_width;
5199 char *PromoChars = "wglcqrbnkac+=\0";
5202 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5203 XtGetValues(boardWidget, args, j);
5206 XtSetArg(args[j], XtNresizable, True); j++;
5207 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5209 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5210 shellWidget, args, j);
5212 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5213 layoutArgs, XtNumber(layoutArgs));
5216 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5217 XtSetArg(args[j], XtNborderWidth, 0); j++;
5218 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5221 if(gameInfo.variant != VariantShogi) {
5222 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5223 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5224 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5225 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5226 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5228 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5229 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5230 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5231 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5233 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5234 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5235 gameInfo.variant == VariantGiveaway) {
5236 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5238 if(gameInfo.variant == VariantCapablanca ||
5239 gameInfo.variant == VariantGothic ||
5240 gameInfo.variant == VariantCapaRandom) {
5241 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5242 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5244 } else // [HGM] shogi
5246 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5247 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5249 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5251 XtRealizeWidget(promotionShell);
5252 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5255 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5256 XtGetValues(promotionShell, args, j);
5258 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5259 lineGap + squareSize/3 +
5260 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5261 0 : 6*(squareSize + lineGap)), &x, &y);
5264 XtSetArg(args[j], XtNx, x); j++;
5265 XtSetArg(args[j], XtNy, y); j++;
5266 XtSetValues(promotionShell, args, j);
5268 XtPopup(promotionShell, XtGrabNone);
5276 if (!promotionUp) return;
5277 XtPopdown(promotionShell);
5278 XtDestroyWidget(promotionShell);
5279 promotionUp = False;
5283 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5285 int promoChar = * (const char *) client_data;
5289 if (fromX == -1) return;
5296 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5298 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5299 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5305 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5307 dialogError = errorUp = False;
5308 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5310 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5317 if (!errorUp) return;
5318 dialogError = errorUp = False;
5319 XtPopdown(errorShell);
5320 XtDestroyWidget(errorShell);
5321 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5325 ErrorPopUp (char *title, char *label, int modal)
5328 Widget dialog, layout;
5332 Dimension bw_width, pw_width;
5333 Dimension pw_height;
5337 XtSetArg(args[i], XtNresizable, True); i++;
5338 XtSetArg(args[i], XtNtitle, title); i++;
5340 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5341 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
5343 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5344 layoutArgs, XtNumber(layoutArgs));
5347 XtSetArg(args[i], XtNlabel, label); i++;
5348 XtSetArg(args[i], XtNborderWidth, 0); i++;
5349 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5352 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5354 XtRealizeWidget(errorShell);
5355 CatchDeleteWindow(errorShell, "ErrorPopDown");
5358 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5359 XtGetValues(boardWidget, args, i);
5361 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5362 XtSetArg(args[i], XtNheight, &pw_height); i++;
5363 XtGetValues(errorShell, args, i);
5366 /* This code seems to tickle an X bug if it is executed too soon
5367 after xboard starts up. The coordinates get transformed as if
5368 the main window was positioned at (0, 0).
5370 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5371 0 - pw_height + squareSize / 3, &x, &y);
5373 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5374 RootWindowOfScreen(XtScreen(boardWidget)),
5375 (bw_width - pw_width) / 2,
5376 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5380 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5383 XtSetArg(args[i], XtNx, x); i++;
5384 XtSetArg(args[i], XtNy, y); i++;
5385 XtSetValues(errorShell, args, i);
5388 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5391 /* Disable all user input other than deleting the window */
5392 static int frozen = 0;
5398 /* Grab by a widget that doesn't accept input */
5399 XtAddGrab(messageWidget, TRUE, FALSE);
5403 /* Undo a FreezeUI */
5407 if (!frozen) return;
5408 XtRemoveGrab(messageWidget);
5413 ModeToWidgetName (GameMode mode)
5416 case BeginningOfGame:
5417 if (appData.icsActive)
5418 return "menuMode.ICS Client";
5419 else if (appData.noChessProgram ||
5420 *appData.cmailGameName != NULLCHAR)
5421 return "menuMode.Edit Game";
5423 return "menuMode.Machine Black";
5424 case MachinePlaysBlack:
5425 return "menuMode.Machine Black";
5426 case MachinePlaysWhite:
5427 return "menuMode.Machine White";
5429 return "menuMode.Analysis Mode";
5431 return "menuMode.Analyze File";
5432 case TwoMachinesPlay:
5433 return "menuMode.Two Machines";
5435 return "menuMode.Edit Game";
5436 case PlayFromGameFile:
5437 return "menuFile.Load Game";
5439 return "menuMode.Edit Position";
5441 return "menuMode.Training";
5442 case IcsPlayingWhite:
5443 case IcsPlayingBlack:
5447 return "menuMode.ICS Client";
5458 static int oldPausing = FALSE;
5459 static GameMode oldmode = (GameMode) -1;
5462 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5464 if (pausing != oldPausing) {
5465 oldPausing = pausing;
5467 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5469 XtSetArg(args[0], XtNleftBitmap, None);
5471 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5474 if (appData.showButtonBar) {
5475 /* Always toggle, don't set. Previous code messes up when
5476 invoked while the button is pressed, as releasing it
5477 toggles the state again. */
5480 XtSetArg(args[0], XtNbackground, &oldbg);
5481 XtSetArg(args[1], XtNforeground, &oldfg);
5482 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5484 XtSetArg(args[0], XtNbackground, oldfg);
5485 XtSetArg(args[1], XtNforeground, oldbg);
5487 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5491 wname = ModeToWidgetName(oldmode);
5492 if (wname != NULL) {
5493 XtSetArg(args[0], XtNleftBitmap, None);
5494 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5496 wname = ModeToWidgetName(gameMode);
5497 if (wname != NULL) {
5498 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5499 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5502 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5503 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5505 /* Maybe all the enables should be handled here, not just this one */
5506 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5507 gameMode == Training || gameMode == PlayFromGameFile);
5512 * Button/menu procedures
5515 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5521 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5523 cmailMsgLoaded = FALSE;
5524 if (gameNumber == 0) {
5525 int error = GameListBuild(f);
5527 DisplayError(_("Cannot build game list"), error);
5528 } else if (!ListEmpty(&gameList) &&
5529 ((ListGame *) gameList.tailPred)->number > 1) {
5530 GameListPopUp(f, title);
5536 return LoadGame(f, gameNumber, title, FALSE);
5540 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5542 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5545 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5549 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5555 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5561 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5567 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5573 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5579 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5585 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5587 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5590 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5594 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5596 FileNamePopUp(_("Save game file name?"),
5597 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5598 appData.oldSaveStyle ? ".game" : ".pgn",
5603 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5605 FileNamePopUp(_("Save position file name?"),
5606 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5607 appData.oldSaveStyle ? ".pos" : ".fen",
5612 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5614 ReloadCmailMsgEvent(FALSE);
5618 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5623 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5624 char *selected_fen_position=NULL;
5627 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5628 Atom *type_return, XtPointer *value_return,
5629 unsigned long *length_return, int *format_return)
5631 char *selection_tmp;
5633 if (!selected_fen_position) return False; /* should never happen */
5634 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5635 /* note: since no XtSelectionDoneProc was registered, Xt will
5636 * automatically call XtFree on the value returned. So have to
5637 * make a copy of it allocated with XtMalloc */
5638 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5639 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5641 *value_return=selection_tmp;
5642 *length_return=strlen(selection_tmp);
5643 *type_return=*target;
5644 *format_return = 8; /* bits per byte */
5646 } else if (*target == XA_TARGETS(xDisplay)) {
5647 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5648 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5649 targets_tmp[1] = XA_STRING;
5650 *value_return = targets_tmp;
5651 *type_return = XA_ATOM;
5654 // This code leads to a read of value_return out of bounds on 64-bit systems.
5655 // Other code which I have seen always sets *format_return to 32 independent of
5656 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5657 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5658 *format_return = 8 * sizeof(Atom);
5659 if (*format_return > 32) {
5660 *length_return *= *format_return / 32;
5661 *format_return = 32;
5664 *format_return = 32;
5672 /* note: when called from menu all parameters are NULL, so no clue what the
5673 * Widget which was clicked on was, or what the click event was
5676 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5679 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5680 * have a notion of a position that is selected but not copied.
5681 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5683 if(gameMode == EditPosition) EditPositionDone(TRUE);
5684 if (selected_fen_position) free(selected_fen_position);
5685 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5686 if (!selected_fen_position) return;
5687 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5689 SendPositionSelection,
5690 NULL/* lose_ownership_proc */ ,
5691 NULL/* transfer_done_proc */);
5692 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5694 SendPositionSelection,
5695 NULL/* lose_ownership_proc */ ,
5696 NULL/* transfer_done_proc */);
5700 CopyFENToClipboard ()
5701 { // wrapper to make call from back-end possible
5702 CopyPositionProc(NULL, NULL, NULL, NULL);
5705 /* function called when the data to Paste is ready */
5707 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5708 Atom *type, XtPointer value, unsigned long *len, int *format)
5711 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5712 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5713 EditPositionPasteFEN(fenstr);
5717 /* called when Paste Position button is pressed,
5718 * all parameters will be NULL */
5719 void PastePositionProc(w, event, prms, nprms)
5725 XtGetSelectionValue(menuBarWidget,
5726 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5727 /* (XtSelectionCallbackProc) */ PastePositionCB,
5728 NULL, /* client_data passed to PastePositionCB */
5730 /* better to use the time field from the event that triggered the
5731 * call to this function, but that isn't trivial to get
5739 SendGameSelection (Widget w, Atom *selection, Atom *target,
5740 Atom *type_return, XtPointer *value_return,
5741 unsigned long *length_return, int *format_return)
5743 char *selection_tmp;
5745 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5746 FILE* f = fopen(gameCopyFilename, "r");
5749 if (f == NULL) return False;
5753 selection_tmp = XtMalloc(len + 1);
5754 count = fread(selection_tmp, 1, len, f);
5757 XtFree(selection_tmp);
5760 selection_tmp[len] = NULLCHAR;
5761 *value_return = selection_tmp;
5762 *length_return = len;
5763 *type_return = *target;
5764 *format_return = 8; /* bits per byte */
5766 } else if (*target == XA_TARGETS(xDisplay)) {
5767 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5768 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5769 targets_tmp[1] = XA_STRING;
5770 *value_return = targets_tmp;
5771 *type_return = XA_ATOM;
5774 // This code leads to a read of value_return out of bounds on 64-bit systems.
5775 // Other code which I have seen always sets *format_return to 32 independent of
5776 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5777 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5778 *format_return = 8 * sizeof(Atom);
5779 if (*format_return > 32) {
5780 *length_return *= *format_return / 32;
5781 *format_return = 32;
5784 *format_return = 32;
5796 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5797 * have a notion of a game that is selected but not copied.
5798 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5800 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5803 NULL/* lose_ownership_proc */ ,
5804 NULL/* transfer_done_proc */);
5805 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5808 NULL/* lose_ownership_proc */ ,
5809 NULL/* transfer_done_proc */);
5812 /* note: when called from menu all parameters are NULL, so no clue what the
5813 * Widget which was clicked on was, or what the click event was
5816 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5820 ret = SaveGameToFile(gameCopyFilename, FALSE);
5827 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5829 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5833 /* function called when the data to Paste is ready */
5835 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5836 Atom *type, XtPointer value, unsigned long *len, int *format)
5839 if (value == NULL || *len == 0) {
5840 return; /* nothing had been selected to copy */
5842 f = fopen(gamePasteFilename, "w");
5844 DisplayError(_("Can't open temp file"), errno);
5847 fwrite(value, 1, *len, f);
5850 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5853 /* called when Paste Game button is pressed,
5854 * all parameters will be NULL */
5856 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5858 XtGetSelectionValue(menuBarWidget,
5859 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5860 /* (XtSelectionCallbackProc) */ PasteGameCB,
5861 NULL, /* client_data passed to PasteGameCB */
5863 /* better to use the time field from the event that triggered the
5864 * call to this function, but that isn't trivial to get
5875 SaveGameProc(NULL, NULL, NULL, NULL);
5880 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5886 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5892 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5894 MachineBlackEvent();
5898 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5900 MachineWhiteEvent();
5904 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5908 if (!first.analysisSupport) {
5909 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5910 DisplayError(buf, 0);
5913 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5914 if (appData.icsActive) {
5915 if (gameMode != IcsObserving) {
5916 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5917 DisplayError(buf, 0);
5919 if (appData.icsEngineAnalyze) {
5920 if (appData.debugMode)
5921 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5927 /* if enable, use want disable icsEngineAnalyze */
5928 if (appData.icsEngineAnalyze) {
5933 appData.icsEngineAnalyze = TRUE;
5934 if (appData.debugMode)
5935 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5937 #ifndef OPTIONSDIALOG
5938 if (!appData.showThinking)
5939 ShowThinkingProc(w,event,prms,nprms);
5946 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5948 if (!first.analysisSupport) {
5950 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5951 DisplayError(buf, 0);
5954 // Reset(FALSE, TRUE);
5955 #ifndef OPTIONSDIALOG
5956 if (!appData.showThinking)
5957 ShowThinkingProc(w,event,prms,nprms);
5960 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5961 AnalysisPeriodicEvent(1);
5965 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5971 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5977 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5983 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5989 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5991 EditPositionEvent();
5995 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6001 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6005 if (PopDown(1)) { // popdown succesful
6007 XtSetArg(args[j], XtNleftBitmap, None); j++;
6008 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6009 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6010 } else // was not up
6015 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6017 if (!PopDown(4)) ICSInputBoxPopUp();
6021 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6027 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6033 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6039 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6045 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6051 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6057 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6063 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6069 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6071 UserAdjudicationEvent(+1);
6075 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6077 UserAdjudicationEvent(-1);
6081 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6083 UserAdjudicationEvent(0);
6087 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6089 if (shellUp[4] == True)
6094 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6095 { // [HGM] input: let up-arrow recall previous line from history
6102 if (!shellUp[4]) return;
6103 edit = boxOptions[0].handle;
6105 XtSetArg(args[j], XtNstring, &val); j++;
6106 XtGetValues(edit, args, j);
6107 val = PrevInHistory(val);
6108 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6109 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6111 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6112 XawTextReplace(edit, 0, 0, &t);
6113 XawTextSetInsertionPoint(edit, 9999);
6118 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6119 { // [HGM] input: let down-arrow recall next line from history
6124 if (!shellUp[4]) return;
6125 edit = boxOptions[0].handle;
6126 val = NextInHistory();
6127 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6128 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6130 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6131 XawTextReplace(edit, 0, 0, &t);
6132 XawTextSetInsertionPoint(edit, 9999);
6137 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6139 StopObservingEvent();
6143 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6145 StopExaminingEvent();
6149 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6156 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6163 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6169 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6171 if (!TempBackwardActive) {
6172 TempBackwardActive = True;
6178 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6180 /* Check to see if triggered by a key release event for a repeating key.
6181 * If so the next queued event will be a key press of the same key at the same time */
6182 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6184 XPeekEvent(xDisplay, &next);
6185 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6186 next.xkey.keycode == event->xkey.keycode)
6190 TempBackwardActive = False;
6194 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6200 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6206 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6212 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6218 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6220 TruncateGameEvent();
6224 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6230 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6236 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6238 flipView = !flipView;
6239 DrawPosition(True, NULL);
6243 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6247 PonderNextMoveEvent(!appData.ponderNextMove);
6248 #ifndef OPTIONSDIALOG
6249 if (appData.ponderNextMove) {
6250 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6252 XtSetArg(args[0], XtNleftBitmap, None);
6254 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6259 #ifndef OPTIONSDIALOG
6261 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6265 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6267 if (appData.alwaysPromoteToQueen) {
6268 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6270 XtSetArg(args[0], XtNleftBitmap, None);
6272 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6277 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6281 appData.animateDragging = !appData.animateDragging;
6283 if (appData.animateDragging) {
6284 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6287 XtSetArg(args[0], XtNleftBitmap, None);
6289 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6294 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6298 appData.animate = !appData.animate;
6300 if (appData.animate) {
6301 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6304 XtSetArg(args[0], XtNleftBitmap, None);
6306 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6311 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6315 appData.autoCallFlag = !appData.autoCallFlag;
6317 if (appData.autoCallFlag) {
6318 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6320 XtSetArg(args[0], XtNleftBitmap, None);
6322 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6327 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6331 appData.autoFlipView = !appData.autoFlipView;
6333 if (appData.autoFlipView) {
6334 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6336 XtSetArg(args[0], XtNleftBitmap, None);
6338 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6343 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6347 appData.blindfold = !appData.blindfold;
6349 if (appData.blindfold) {
6350 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6352 XtSetArg(args[0], XtNleftBitmap, None);
6354 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6357 DrawPosition(True, NULL);
6361 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6365 appData.testLegality = !appData.testLegality;
6367 if (appData.testLegality) {
6368 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6370 XtSetArg(args[0], XtNleftBitmap, None);
6372 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6378 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6382 if (appData.flashCount == 0) {
6383 appData.flashCount = 3;
6385 appData.flashCount = -appData.flashCount;
6388 if (appData.flashCount > 0) {
6389 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6391 XtSetArg(args[0], XtNleftBitmap, None);
6393 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6399 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6403 appData.highlightDragging = !appData.highlightDragging;
6405 if (appData.highlightDragging) {
6406 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6408 XtSetArg(args[0], XtNleftBitmap, None);
6410 XtSetValues(XtNameToWidget(menuBarWidget,
6411 "menuOptions.Highlight Dragging"), args, 1);
6416 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6420 appData.highlightLastMove = !appData.highlightLastMove;
6422 if (appData.highlightLastMove) {
6423 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6425 XtSetArg(args[0], XtNleftBitmap, None);
6427 XtSetValues(XtNameToWidget(menuBarWidget,
6428 "menuOptions.Highlight Last Move"), args, 1);
6432 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6436 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6438 if (appData.highlightMoveWithArrow) {
6439 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6441 XtSetArg(args[0], XtNleftBitmap, None);
6443 XtSetValues(XtNameToWidget(menuBarWidget,
6444 "menuOptions.Arrow"), args, 1);
6449 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6453 appData.icsAlarm = !appData.icsAlarm;
6455 if (appData.icsAlarm) {
6456 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6458 XtSetArg(args[0], XtNleftBitmap, None);
6460 XtSetValues(XtNameToWidget(menuBarWidget,
6461 "menuOptions.ICS Alarm"), args, 1);
6466 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6470 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6472 if (appData.ringBellAfterMoves) {
6473 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6475 XtSetArg(args[0], XtNleftBitmap, None);
6477 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6482 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6486 appData.oneClick = !appData.oneClick;
6488 if (appData.oneClick) {
6489 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6491 XtSetArg(args[0], XtNleftBitmap, None);
6493 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6498 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6502 PeriodicUpdatesEvent(!appData.periodicUpdates);
6504 if (appData.periodicUpdates) {
6505 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6507 XtSetArg(args[0], XtNleftBitmap, None);
6509 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6514 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6518 appData.popupExitMessage = !appData.popupExitMessage;
6520 if (appData.popupExitMessage) {
6521 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6523 XtSetArg(args[0], XtNleftBitmap, None);
6525 XtSetValues(XtNameToWidget(menuBarWidget,
6526 "menuOptions.Popup Exit Message"), args, 1);
6530 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6534 appData.popupMoveErrors = !appData.popupMoveErrors;
6536 if (appData.popupMoveErrors) {
6537 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6539 XtSetArg(args[0], XtNleftBitmap, None);
6541 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6547 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6551 appData.premove = !appData.premove;
6553 if (appData.premove) {
6554 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6556 XtSetArg(args[0], XtNleftBitmap, None);
6558 XtSetValues(XtNameToWidget(menuBarWidget,
6559 "menuOptions.Premove"), args, 1);
6564 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6568 appData.showCoords = !appData.showCoords;
6570 if (appData.showCoords) {
6571 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6573 XtSetArg(args[0], XtNleftBitmap, None);
6575 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6578 DrawPosition(True, NULL);
6582 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6584 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6585 ShowThinkingEvent();
6589 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6593 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6594 ShowThinkingEvent();
6596 if (appData.hideThinkingFromHuman) {
6597 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6599 XtSetArg(args[0], XtNleftBitmap, None);
6601 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6607 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6611 saveSettingsOnExit = !saveSettingsOnExit;
6613 if (saveSettingsOnExit) {
6614 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6616 XtSetArg(args[0], XtNleftBitmap, None);
6618 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6623 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6625 SaveSettings(settingsFileName);
6629 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6632 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6638 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6642 if (nprms && *nprms > 0)
6646 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6651 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6657 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6663 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6667 char *zippy = _(" (with Zippy code)");
6671 snprintf(buf, sizeof(buf),
6673 "Copyright 1991 Digital Equipment Corporation\n"
6674 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6675 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6676 "%s is free software and carries NO WARRANTY;"
6677 "see the file COPYING for more information."),
6678 programVersion, zippy, PACKAGE);
6679 ErrorPopUp(_("About XBoard"), buf, FALSE);
6683 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6685 appData.debugMode = !appData.debugMode;
6689 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6695 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6701 DisplayMessage (char *message, char *extMessage)
6703 /* display a message in the message widget */
6712 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6717 message = extMessage;
6721 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6723 /* need to test if messageWidget already exists, since this function
6724 can also be called during the startup, if for example a Xresource
6725 is not set up correctly */
6728 XtSetArg(arg, XtNlabel, message);
6729 XtSetValues(messageWidget, &arg, 1);
6736 DisplayTitle (char *text)
6740 char title[MSG_SIZ];
6743 if (text == NULL) text = "";
6745 if (appData.titleInWindow) {
6747 XtSetArg(args[i], XtNlabel, text); i++;
6748 XtSetValues(titleWidget, args, i);
6751 if (*text != NULLCHAR) {
6752 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6753 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6754 } else if (appData.icsActive) {
6755 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6756 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6757 } else if (appData.cmailGameName[0] != NULLCHAR) {
6758 snprintf(icon, sizeof(icon), "%s", "CMail");
6759 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6761 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6762 } else if (gameInfo.variant == VariantGothic) {
6763 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6764 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6767 } else if (gameInfo.variant == VariantFalcon) {
6768 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6769 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6771 } else if (appData.noChessProgram) {
6772 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6773 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6775 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6776 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6779 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6780 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6781 XtSetValues(shellWidget, args, i);
6782 XSync(xDisplay, False);
6787 DisplayError (String message, int error)
6792 if (appData.debugMode || appData.matchMode) {
6793 fprintf(stderr, "%s: %s\n", programName, message);
6796 if (appData.debugMode || appData.matchMode) {
6797 fprintf(stderr, "%s: %s: %s\n",
6798 programName, message, strerror(error));
6800 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6803 ErrorPopUp(_("Error"), message, FALSE);
6808 DisplayMoveError (String message)
6812 DrawPosition(FALSE, NULL);
6813 if (appData.debugMode || appData.matchMode) {
6814 fprintf(stderr, "%s: %s\n", programName, message);
6816 if (appData.popupMoveErrors) {
6817 ErrorPopUp(_("Error"), message, FALSE);
6819 DisplayMessage(message, "");
6825 DisplayFatalError (String message, int error, int status)
6829 errorExitStatus = status;
6831 fprintf(stderr, "%s: %s\n", programName, message);
6833 fprintf(stderr, "%s: %s: %s\n",
6834 programName, message, strerror(error));
6835 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6838 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6839 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6846 DisplayInformation (String message)
6849 ErrorPopUp(_("Information"), message, TRUE);
6853 DisplayNote (String message)
6856 ErrorPopUp(_("Note"), message, FALSE);
6860 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6866 DisplayIcsInteractionTitle (String message)
6868 if (oldICSInteractionTitle == NULL) {
6869 /* Magic to find the old window title, adapted from vim */
6870 char *wina = getenv("WINDOWID");
6872 Window win = (Window) atoi(wina);
6873 Window root, parent, *children;
6874 unsigned int nchildren;
6875 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6877 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6878 if (!XQueryTree(xDisplay, win, &root, &parent,
6879 &children, &nchildren)) break;
6880 if (children) XFree((void *)children);
6881 if (parent == root || parent == 0) break;
6884 XSetErrorHandler(oldHandler);
6886 if (oldICSInteractionTitle == NULL) {
6887 oldICSInteractionTitle = "xterm";
6890 printf("\033]0;%s\007", message);
6894 char pendingReplyPrefix[MSG_SIZ];
6895 ProcRef pendingReplyPR;
6898 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6901 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6905 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6909 AskQuestionPopDown ()
6911 if (!askQuestionUp) return;
6912 XtPopdown(askQuestionShell);
6913 XtDestroyWidget(askQuestionShell);
6914 askQuestionUp = False;
6918 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6924 reply = XawDialogGetValueString(w = XtParent(w));
6925 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6926 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6927 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6928 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6929 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6930 AskQuestionPopDown();
6932 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6936 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6941 XtSetArg(args[0], XtNlabel, &name);
6942 XtGetValues(w, args, 1);
6944 if (strcmp(name, _("cancel")) == 0) {
6945 AskQuestionPopDown();
6947 AskQuestionReplyAction(w, NULL, NULL, NULL);
6952 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6955 Widget popup, layout, dialog, edit;
6961 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6962 pendingReplyPR = pr;
6965 XtSetArg(args[i], XtNresizable, True); i++;
6966 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6967 askQuestionShell = popup =
6968 XtCreatePopupShell(title, transientShellWidgetClass,
6969 shellWidget, args, i);
6972 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6973 layoutArgs, XtNumber(layoutArgs));
6976 XtSetArg(args[i], XtNlabel, question); i++;
6977 XtSetArg(args[i], XtNvalue, ""); i++;
6978 XtSetArg(args[i], XtNborderWidth, 0); i++;
6979 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6982 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6983 (XtPointer) dialog);
6984 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6985 (XtPointer) dialog);
6987 XtRealizeWidget(popup);
6988 CatchDeleteWindow(popup, "AskQuestionPopDown");
6990 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6991 &x, &y, &win_x, &win_y, &mask);
6993 XtSetArg(args[0], XtNx, x - 10);
6994 XtSetArg(args[1], XtNy, y - 30);
6995 XtSetValues(popup, args, 2);
6997 XtPopup(popup, XtGrabExclusive);
6998 askQuestionUp = True;
7000 edit = XtNameToWidget(dialog, "*value");
7001 XtSetKeyboardFocus(popup, edit);
7006 PlaySound (char *name)
7008 if (*name == NULLCHAR) {
7010 } else if (strcmp(name, "$") == 0) {
7011 putc(BELLCHAR, stderr);
7014 char *prefix = "", *sep = "";
7015 if(appData.soundProgram[0] == NULLCHAR) return;
7016 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7017 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7025 PlaySound(appData.soundMove);
7031 PlaySound(appData.soundIcsWin);
7037 PlaySound(appData.soundIcsLoss);
7043 PlaySound(appData.soundIcsDraw);
7047 PlayIcsUnfinishedSound ()
7049 PlaySound(appData.soundIcsUnfinished);
7055 PlaySound(appData.soundIcsAlarm);
7061 PlaySound(appData.soundTell);
7067 system("stty echo");
7074 system("stty -echo");
7079 RunCommand (char *buf)
7085 Colorize (ColorClass cc, int continuation)
7088 int count, outCount, error;
7090 if (textColors[(int)cc].bg > 0) {
7091 if (textColors[(int)cc].fg > 0) {
7092 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7093 textColors[(int)cc].fg, textColors[(int)cc].bg);
7095 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7096 textColors[(int)cc].bg);
7099 if (textColors[(int)cc].fg > 0) {
7100 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7101 textColors[(int)cc].fg);
7103 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7106 count = strlen(buf);
7107 outCount = OutputToProcess(NoProc, buf, count, &error);
7108 if (outCount < count) {
7109 DisplayFatalError(_("Error writing to display"), error, 1);
7112 if (continuation) return;
7115 PlaySound(appData.soundShout);
7118 PlaySound(appData.soundSShout);
7121 PlaySound(appData.soundChannel1);
7124 PlaySound(appData.soundChannel);
7127 PlaySound(appData.soundKibitz);
7130 PlaySound(appData.soundTell);
7132 case ColorChallenge:
7133 PlaySound(appData.soundChallenge);
7136 PlaySound(appData.soundRequest);
7139 PlaySound(appData.soundSeek);
7151 return getpwuid(getuid())->pw_name;
7155 ExpandPathName (char *path)
7157 static char static_buf[4*MSG_SIZ];
7158 char *d, *s, buf[4*MSG_SIZ];
7164 while (*s && isspace(*s))
7173 if (*(s+1) == '/') {
7174 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7178 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7179 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7180 pwd = getpwnam(buf);
7183 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7187 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7188 strcat(d, strchr(s+1, '/'));
7192 safeStrCpy(d, s, 4*MSG_SIZ );
7200 static char host_name[MSG_SIZ];
7202 #if HAVE_GETHOSTNAME
7203 gethostname(host_name, MSG_SIZ);
7205 #else /* not HAVE_GETHOSTNAME */
7206 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7207 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7209 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7211 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7212 #endif /* not HAVE_GETHOSTNAME */
7215 XtIntervalId delayedEventTimerXID = 0;
7216 DelayedEventCallback delayedEventCallback = 0;
7221 delayedEventTimerXID = 0;
7222 delayedEventCallback();
7226 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7228 if(delayedEventTimerXID && delayedEventCallback == cb)
7229 // [HGM] alive: replace, rather than add or flush identical event
7230 XtRemoveTimeOut(delayedEventTimerXID);
7231 delayedEventCallback = cb;
7232 delayedEventTimerXID =
7233 XtAppAddTimeOut(appContext, millisec,
7234 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7237 DelayedEventCallback
7240 if (delayedEventTimerXID) {
7241 return delayedEventCallback;
7248 CancelDelayedEvent ()
7250 if (delayedEventTimerXID) {
7251 XtRemoveTimeOut(delayedEventTimerXID);
7252 delayedEventTimerXID = 0;
7256 XtIntervalId loadGameTimerXID = 0;
7259 LoadGameTimerRunning ()
7261 return loadGameTimerXID != 0;
7265 StopLoadGameTimer ()
7267 if (loadGameTimerXID != 0) {
7268 XtRemoveTimeOut(loadGameTimerXID);
7269 loadGameTimerXID = 0;
7277 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7279 loadGameTimerXID = 0;
7284 StartLoadGameTimer (long millisec)
7287 XtAppAddTimeOut(appContext, millisec,
7288 (XtTimerCallbackProc) LoadGameTimerCallback,
7292 XtIntervalId analysisClockXID = 0;
7295 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7297 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7298 || appData.icsEngineAnalyze) { // [DM]
7299 AnalysisPeriodicEvent(0);
7300 StartAnalysisClock();
7305 StartAnalysisClock ()
7308 XtAppAddTimeOut(appContext, 2000,
7309 (XtTimerCallbackProc) AnalysisClockCallback,
7313 XtIntervalId clockTimerXID = 0;
7316 ClockTimerRunning ()
7318 return clockTimerXID != 0;
7324 if (clockTimerXID != 0) {
7325 XtRemoveTimeOut(clockTimerXID);
7334 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7341 StartClockTimer (long millisec)
7344 XtAppAddTimeOut(appContext, millisec,
7345 (XtTimerCallbackProc) ClockTimerCallback,
7350 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7355 /* check for low time warning */
7356 Pixel foregroundOrWarningColor = timerForegroundPixel;
7359 appData.lowTimeWarning &&
7360 (timer / 1000) < appData.icsAlarmTime)
7361 foregroundOrWarningColor = lowTimeWarningColor;
7363 if (appData.clockMode) {
7364 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7365 XtSetArg(args[0], XtNlabel, buf);
7367 snprintf(buf, MSG_SIZ, "%s ", color);
7368 XtSetArg(args[0], XtNlabel, buf);
7373 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7374 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7376 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7377 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7380 XtSetValues(w, args, 3);
7384 DisplayWhiteClock (long timeRemaining, int highlight)
7388 if(appData.noGUI) return;
7389 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7390 if (highlight && iconPixmap == bIconPixmap) {
7391 iconPixmap = wIconPixmap;
7392 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7393 XtSetValues(shellWidget, args, 1);
7398 DisplayBlackClock (long timeRemaining, int highlight)
7402 if(appData.noGUI) return;
7403 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7404 if (highlight && iconPixmap == wIconPixmap) {
7405 iconPixmap = bIconPixmap;
7406 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7407 XtSetValues(shellWidget, args, 1);
7426 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7430 int to_prog[2], from_prog[2];
7434 if (appData.debugMode) {
7435 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7438 /* We do NOT feed the cmdLine to the shell; we just
7439 parse it into blank-separated arguments in the
7440 most simple-minded way possible.
7443 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7446 while(*p == ' ') p++;
7448 if(*p == '"' || *p == '\'')
7449 p = strchr(++argv[i-1], *p);
7450 else p = strchr(p, ' ');
7451 if (p == NULL) break;
7456 SetUpChildIO(to_prog, from_prog);
7458 if ((pid = fork()) == 0) {
7460 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7461 close(to_prog[1]); // first close the unused pipe ends
7462 close(from_prog[0]);
7463 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7464 dup2(from_prog[1], 1);
7465 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7466 close(from_prog[1]); // and closing again loses one of the pipes!
7467 if(fileno(stderr) >= 2) // better safe than sorry...
7468 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7470 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7475 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7477 execvp(argv[0], argv);
7479 /* If we get here, exec failed */
7484 /* Parent process */
7486 close(from_prog[1]);
7488 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7491 cp->fdFrom = from_prog[0];
7492 cp->fdTo = to_prog[1];
7497 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7499 AlarmCallBack (int n)
7505 DestroyChildProcess (ProcRef pr, int signalType)
7507 ChildProc *cp = (ChildProc *) pr;
7509 if (cp->kind != CPReal) return;
7511 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7512 signal(SIGALRM, AlarmCallBack);
7514 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7515 kill(cp->pid, SIGKILL); // kill it forcefully
7516 wait((int *) 0); // and wait again
7520 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7522 /* Process is exiting either because of the kill or because of
7523 a quit command sent by the backend; either way, wait for it to die.
7532 InterruptChildProcess (ProcRef pr)
7534 ChildProc *cp = (ChildProc *) pr;
7536 if (cp->kind != CPReal) return;
7537 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7541 OpenTelnet (char *host, char *port, ProcRef *pr)
7543 char cmdLine[MSG_SIZ];
7545 if (port[0] == NULLCHAR) {
7546 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7548 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7550 return StartChildProcess(cmdLine, "", pr);
7554 OpenTCP (char *host, char *port, ProcRef *pr)
7557 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7558 #else /* !OMIT_SOCKETS */
7559 struct addrinfo hints;
7560 struct addrinfo *ais, *ai;
7565 memset(&hints, 0, sizeof(hints));
7566 hints.ai_family = AF_UNSPEC;
7567 hints.ai_socktype = SOCK_STREAM;
7569 error = getaddrinfo(host, port, &hints, &ais);
7571 /* a getaddrinfo error is not an errno, so can't return it */
7572 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7573 host, port, gai_strerror(error));
7577 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7578 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7582 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7595 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7601 #endif /* !OMIT_SOCKETS */
7607 OpenCommPort (char *name, ProcRef *pr)
7612 fd = open(name, 2, 0);
7613 if (fd < 0) return errno;
7615 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7626 OpenLoopback (ProcRef *pr)
7631 SetUpChildIO(to, from);
7633 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7636 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7644 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7646 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7650 #define INPUT_SOURCE_BUF_SIZE 8192
7659 char buf[INPUT_SOURCE_BUF_SIZE];
7664 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7666 InputSource *is = (InputSource *) closure;
7671 if (is->lineByLine) {
7672 count = read(is->fd, is->unused,
7673 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7675 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7678 is->unused += count;
7680 while (p < is->unused) {
7681 q = memchr(p, '\n', is->unused - p);
7682 if (q == NULL) break;
7684 (is->func)(is, is->closure, p, q - p, 0);
7688 while (p < is->unused) {
7693 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7698 (is->func)(is, is->closure, is->buf, count, error);
7703 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7706 ChildProc *cp = (ChildProc *) pr;
7708 is = (InputSource *) calloc(1, sizeof(InputSource));
7709 is->lineByLine = lineByLine;
7713 is->fd = fileno(stdin);
7715 is->kind = cp->kind;
7716 is->fd = cp->fdFrom;
7719 is->unused = is->buf;
7722 is->xid = XtAppAddInput(appContext, is->fd,
7723 (XtPointer) (XtInputReadMask),
7724 (XtInputCallbackProc) DoInputCallback,
7726 is->closure = closure;
7727 return (InputSourceRef) is;
7731 RemoveInputSource (InputSourceRef isr)
7733 InputSource *is = (InputSource *) isr;
7735 if (is->xid == 0) return;
7736 XtRemoveInput(is->xid);
7741 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7743 static int line = 0;
7744 ChildProc *cp = (ChildProc *) pr;
7749 if (appData.noJoin || !appData.useInternalWrap)
7750 outCount = fwrite(message, 1, count, stdout);
7753 int width = get_term_width();
7754 int len = wrap(NULL, message, count, width, &line);
7755 char *msg = malloc(len);
7759 outCount = fwrite(message, 1, count, stdout);
7762 dbgchk = wrap(msg, message, count, width, &line);
7763 if (dbgchk != len && appData.debugMode)
7764 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7765 outCount = fwrite(msg, 1, dbgchk, stdout);
7771 outCount = write(cp->fdTo, message, count);
7781 /* Output message to process, with "ms" milliseconds of delay
7782 between each character. This is needed when sending the logon
7783 script to ICC, which for some reason doesn't like the
7784 instantaneous send. */
7786 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7788 ChildProc *cp = (ChildProc *) pr;
7793 r = write(cp->fdTo, message++, 1);
7806 /**** Animation code by Hugh Fisher, DCS, ANU.
7808 Known problem: if a window overlapping the board is
7809 moved away while a piece is being animated underneath,
7810 the newly exposed area won't be updated properly.
7811 I can live with this.
7813 Known problem: if you look carefully at the animation
7814 of pieces in mono mode, they are being drawn as solid
7815 shapes without interior detail while moving. Fixing
7816 this would be a major complication for minimal return.
7819 /* Masks for XPM pieces. Black and white pieces can have
7820 different shapes, but in the interest of retaining my
7821 sanity pieces must have the same outline on both light
7822 and dark squares, and all pieces must use the same
7823 background square colors/images. */
7825 static int xpmDone = 0;
7828 CreateAnimMasks (int pieceDepth)
7834 unsigned long plane;
7837 /* Need a bitmap just to get a GC with right depth */
7838 buf = XCreatePixmap(xDisplay, xBoardWindow,
7840 values.foreground = 1;
7841 values.background = 0;
7842 /* Don't use XtGetGC, not read only */
7843 maskGC = XCreateGC(xDisplay, buf,
7844 GCForeground | GCBackground, &values);
7845 XFreePixmap(xDisplay, buf);
7847 buf = XCreatePixmap(xDisplay, xBoardWindow,
7848 squareSize, squareSize, pieceDepth);
7849 values.foreground = XBlackPixel(xDisplay, xScreen);
7850 values.background = XWhitePixel(xDisplay, xScreen);
7851 bufGC = XCreateGC(xDisplay, buf,
7852 GCForeground | GCBackground, &values);
7854 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7855 /* Begin with empty mask */
7856 if(!xpmDone) // [HGM] pieces: keep using existing
7857 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7858 squareSize, squareSize, 1);
7859 XSetFunction(xDisplay, maskGC, GXclear);
7860 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7861 0, 0, squareSize, squareSize);
7863 /* Take a copy of the piece */
7868 XSetFunction(xDisplay, bufGC, GXcopy);
7869 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7871 0, 0, squareSize, squareSize, 0, 0);
7873 /* XOR the background (light) over the piece */
7874 XSetFunction(xDisplay, bufGC, GXxor);
7876 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7877 0, 0, squareSize, squareSize, 0, 0);
7879 XSetForeground(xDisplay, bufGC, lightSquareColor);
7880 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7883 /* We now have an inverted piece image with the background
7884 erased. Construct mask by just selecting all the non-zero
7885 pixels - no need to reconstruct the original image. */
7886 XSetFunction(xDisplay, maskGC, GXor);
7888 /* Might be quicker to download an XImage and create bitmap
7889 data from it rather than this N copies per piece, but it
7890 only takes a fraction of a second and there is a much
7891 longer delay for loading the pieces. */
7892 for (n = 0; n < pieceDepth; n ++) {
7893 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7894 0, 0, squareSize, squareSize,
7900 XFreePixmap(xDisplay, buf);
7901 XFreeGC(xDisplay, bufGC);
7902 XFreeGC(xDisplay, maskGC);
7906 InitAnimState (AnimState *anim, XWindowAttributes *info)
7911 /* Each buffer is square size, same depth as window */
7912 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7913 squareSize, squareSize, info->depth);
7914 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7915 squareSize, squareSize, info->depth);
7917 /* Create a plain GC for blitting */
7918 mask = GCForeground | GCBackground | GCFunction |
7919 GCPlaneMask | GCGraphicsExposures;
7920 values.foreground = XBlackPixel(xDisplay, xScreen);
7921 values.background = XWhitePixel(xDisplay, xScreen);
7922 values.function = GXcopy;
7923 values.plane_mask = AllPlanes;
7924 values.graphics_exposures = False;
7925 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7927 /* Piece will be copied from an existing context at
7928 the start of each new animation/drag. */
7929 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7931 /* Outline will be a read-only copy of an existing */
7932 anim->outlineGC = None;
7938 XWindowAttributes info;
7940 if (xpmDone && gameInfo.variant == oldVariant) return;
7941 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7942 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7944 InitAnimState(&game, &info);
7945 InitAnimState(&player, &info);
7947 /* For XPM pieces, we need bitmaps to use as masks. */
7949 CreateAnimMasks(info.depth), xpmDone = 1;
7954 static Boolean frameWaiting;
7957 FrameAlarm (int sig)
7959 frameWaiting = False;
7960 /* In case System-V style signals. Needed?? */
7961 signal(SIGALRM, FrameAlarm);
7965 FrameDelay (int time)
7967 struct itimerval delay;
7969 XSync(xDisplay, False);
7972 frameWaiting = True;
7973 signal(SIGALRM, FrameAlarm);
7974 delay.it_interval.tv_sec =
7975 delay.it_value.tv_sec = time / 1000;
7976 delay.it_interval.tv_usec =
7977 delay.it_value.tv_usec = (time % 1000) * 1000;
7978 setitimer(ITIMER_REAL, &delay, NULL);
7979 while (frameWaiting) pause();
7980 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7981 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7982 setitimer(ITIMER_REAL, &delay, NULL);
7989 FrameDelay (int time)
7991 XSync(xDisplay, False);
7993 usleep(time * 1000);
8004 /* Convert board position to corner of screen rect and color */
8007 ScreenSquare (int column, int row, XPoint *pt, int *color)
8010 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8011 pt->y = lineGap + row * (squareSize + lineGap);
8013 pt->x = lineGap + column * (squareSize + lineGap);
8014 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8016 *color = SquareColor(row, column);
8019 /* Convert window coords to square */
8022 BoardSquare (int x, int y, int *column, int *row)
8024 *column = EventToSquare(x, BOARD_WIDTH);
8025 if (flipView && *column >= 0)
8026 *column = BOARD_WIDTH - 1 - *column;
8027 *row = EventToSquare(y, BOARD_HEIGHT);
8028 if (!flipView && *row >= 0)
8029 *row = BOARD_HEIGHT - 1 - *row;
8034 #undef Max /* just in case */
8036 #define Max(a, b) ((a) > (b) ? (a) : (b))
8037 #define Min(a, b) ((a) < (b) ? (a) : (b))
8040 SetRect (XRectangle *rect, int x, int y, int width, int height)
8044 rect->width = width;
8045 rect->height = height;
8048 /* Test if two frames overlap. If they do, return
8049 intersection rect within old and location of
8050 that rect within new. */
8053 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
8055 if (old->x > new->x + size || new->x > old->x + size ||
8056 old->y > new->y + size || new->y > old->y + size) {
8059 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8060 size - abs(old->x - new->x), size - abs(old->y - new->y));
8061 pt->x = Max(old->x - new->x, 0);
8062 pt->y = Max(old->y - new->y, 0);
8067 /* For two overlapping frames, return the rect(s)
8068 in the old that do not intersect with the new. */
8071 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
8075 /* If old = new (shouldn't happen) then nothing to draw */
8076 if (old->x == new->x && old->y == new->y) {
8080 /* Work out what bits overlap. Since we know the rects
8081 are the same size we don't need a full intersect calc. */
8083 /* Top or bottom edge? */
8084 if (new->y > old->y) {
8085 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8087 } else if (old->y > new->y) {
8088 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8089 size, old->y - new->y);
8092 /* Left or right edge - don't overlap any update calculated above. */
8093 if (new->x > old->x) {
8094 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8095 new->x - old->x, size - abs(new->y - old->y));
8097 } else if (old->x > new->x) {
8098 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8099 old->x - new->x, size - abs(new->y - old->y));
8106 /* Generate a series of frame coords from start->mid->finish.
8107 The movement rate doubles until the half way point is
8108 reached, then halves back down to the final destination,
8109 which gives a nice slow in/out effect. The algorithmn
8110 may seem to generate too many intermediates for short
8111 moves, but remember that the purpose is to attract the
8112 viewers attention to the piece about to be moved and
8113 then to where it ends up. Too few frames would be less
8117 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8119 int fraction, n, count;
8123 /* Slow in, stepping 1/16th, then 1/8th, ... */
8125 for (n = 0; n < factor; n++)
8127 for (n = 0; n < factor; n++) {
8128 frames[count].x = start->x + (mid->x - start->x) / fraction;
8129 frames[count].y = start->y + (mid->y - start->y) / fraction;
8131 fraction = fraction / 2;
8135 frames[count] = *mid;
8138 /* Slow out, stepping 1/2, then 1/4, ... */
8140 for (n = 0; n < factor; n++) {
8141 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8142 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8144 fraction = fraction * 2;
8149 /* Draw a piece on the screen without disturbing what's there */
8152 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8156 /* Bitmap for piece being moved. */
8157 if (appData.monoMode) {
8158 *mask = *pieceToSolid(piece);
8159 } else if (useImages) {
8161 *mask = xpmMask[piece];
8163 *mask = ximMaskPm[piece];
8166 *mask = *pieceToSolid(piece);
8169 /* GC for piece being moved. Square color doesn't matter, but
8170 since it gets modified we make a copy of the original. */
8172 if (appData.monoMode)
8177 if (appData.monoMode)
8182 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8184 /* Outline only used in mono mode and is not modified */
8186 *outline = bwPieceGC;
8188 *outline = wbPieceGC;
8192 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8197 /* Draw solid rectangle which will be clipped to shape of piece */
8198 XFillRectangle(xDisplay, dest, clip,
8199 0, 0, squareSize, squareSize);
8200 if (appData.monoMode)
8201 /* Also draw outline in contrasting color for black
8202 on black / white on white cases */
8203 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8204 0, 0, squareSize, squareSize, 0, 0, 1);
8206 /* Copy the piece */
8211 if(appData.upsideDown && flipView) kind ^= 2;
8212 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8214 0, 0, squareSize, squareSize,
8219 /* Animate the movement of a single piece */
8222 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8226 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8227 /* The old buffer is initialised with the start square (empty) */
8228 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8229 anim->prevFrame = *start;
8231 /* The piece will be drawn using its own bitmap as a matte */
8232 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8233 XSetClipMask(xDisplay, anim->pieceGC, mask);
8237 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8239 XRectangle updates[4];
8244 /* Save what we are about to draw into the new buffer */
8245 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8246 frame->x, frame->y, squareSize, squareSize,
8249 /* Erase bits of the previous frame */
8250 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8251 /* Where the new frame overlapped the previous,
8252 the contents in newBuf are wrong. */
8253 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8254 overlap.x, overlap.y,
8255 overlap.width, overlap.height,
8257 /* Repaint the areas in the old that don't overlap new */
8258 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8259 for (i = 0; i < count; i++)
8260 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8261 updates[i].x - anim->prevFrame.x,
8262 updates[i].y - anim->prevFrame.y,
8263 updates[i].width, updates[i].height,
8264 updates[i].x, updates[i].y);
8266 /* Easy when no overlap */
8267 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8268 0, 0, squareSize, squareSize,
8269 anim->prevFrame.x, anim->prevFrame.y);
8272 /* Save this frame for next time round */
8273 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8274 0, 0, squareSize, squareSize,
8276 anim->prevFrame = *frame;
8278 /* Draw piece over original screen contents, not current,
8279 and copy entire rect. Wipes out overlapping piece images. */
8280 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8281 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8282 0, 0, squareSize, squareSize,
8283 frame->x, frame->y);
8287 EndAnimation (AnimState *anim, XPoint *finish)
8289 XRectangle updates[4];
8294 /* The main code will redraw the final square, so we
8295 only need to erase the bits that don't overlap. */
8296 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8297 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8298 for (i = 0; i < count; i++)
8299 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8300 updates[i].x - anim->prevFrame.x,
8301 updates[i].y - anim->prevFrame.y,
8302 updates[i].width, updates[i].height,
8303 updates[i].x, updates[i].y);
8305 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8306 0, 0, squareSize, squareSize,
8307 anim->prevFrame.x, anim->prevFrame.y);
8312 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8316 BeginAnimation(anim, piece, startColor, start);
8317 for (n = 0; n < nFrames; n++) {
8318 AnimationFrame(anim, &(frames[n]), piece);
8319 FrameDelay(appData.animSpeed);
8321 EndAnimation(anim, finish);
8325 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8328 ChessSquare piece = board[fromY][toY];
8329 board[fromY][toY] = EmptySquare;
8330 DrawPosition(FALSE, board);
8332 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8333 y = lineGap + toY * (squareSize + lineGap);
8335 x = lineGap + toX * (squareSize + lineGap);
8336 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8338 for(i=1; i<4*kFactor; i++) {
8339 int r = squareSize * 9 * i/(20*kFactor - 5);
8340 XFillArc(xDisplay, xBoardWindow, highlineGC,
8341 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8342 FrameDelay(appData.animSpeed);
8344 board[fromY][toY] = piece;
8347 /* Main control logic for deciding what to animate and how */
8350 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8354 XPoint start, finish, mid;
8355 XPoint frames[kFactor * 2 + 1];
8356 int nFrames, startColor, endColor;
8358 /* Are we animating? */
8359 if (!appData.animate || appData.blindfold)
8362 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8363 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8364 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8366 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8367 piece = board[fromY][fromX];
8368 if (piece >= EmptySquare) return;
8373 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8376 ScreenSquare(fromX, fromY, &start, &startColor);
8377 ScreenSquare(toX, toY, &finish, &endColor);
8380 /* Knight: make straight movement then diagonal */
8381 if (abs(toY - fromY) < abs(toX - fromX)) {
8382 mid.x = start.x + (finish.x - start.x) / 2;
8386 mid.y = start.y + (finish.y - start.y) / 2;
8389 mid.x = start.x + (finish.x - start.x) / 2;
8390 mid.y = start.y + (finish.y - start.y) / 2;
8393 /* Don't use as many frames for very short moves */
8394 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8395 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8397 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8398 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8399 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8401 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8402 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8405 /* Be sure end square is redrawn */
8406 damage[0][toY][toX] = True;
8410 DragPieceBegin (int x, int y, Boolean instantly)
8412 int boardX, boardY, color;
8415 /* Are we animating? */
8416 if (!appData.animateDragging || appData.blindfold)
8419 /* Figure out which square we start in and the
8420 mouse position relative to top left corner. */
8421 BoardSquare(x, y, &boardX, &boardY);
8422 player.startBoardX = boardX;
8423 player.startBoardY = boardY;
8424 ScreenSquare(boardX, boardY, &corner, &color);
8425 player.startSquare = corner;
8426 player.startColor = color;
8427 /* As soon as we start dragging, the piece will jump slightly to
8428 be centered over the mouse pointer. */
8429 player.mouseDelta.x = squareSize/2;
8430 player.mouseDelta.y = squareSize/2;
8431 /* Initialise animation */
8432 player.dragPiece = PieceForSquare(boardX, boardY);
8434 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8435 player.dragActive = True;
8436 BeginAnimation(&player, player.dragPiece, color, &corner);
8437 /* Mark this square as needing to be redrawn. Note that
8438 we don't remove the piece though, since logically (ie
8439 as seen by opponent) the move hasn't been made yet. */
8440 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8441 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8442 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8443 corner.x, corner.y, squareSize, squareSize,
8444 0, 0); // [HGM] zh: unstack in stead of grab
8445 if(gatingPiece != EmptySquare) {
8446 /* Kludge alert: When gating we want the introduced
8447 piece to appear on the from square. To generate an
8448 image of it, we draw it on the board, copy the image,
8449 and draw the original piece again. */
8450 ChessSquare piece = boards[currentMove][boardY][boardX];
8451 DrawSquare(boardY, boardX, gatingPiece, 0);
8452 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8453 corner.x, corner.y, squareSize, squareSize, 0, 0);
8454 DrawSquare(boardY, boardX, piece, 0);
8456 damage[0][boardY][boardX] = True;
8458 player.dragActive = False;
8463 ChangeDragPiece (ChessSquare piece)
8466 player.dragPiece = piece;
8467 /* The piece will be drawn using its own bitmap as a matte */
8468 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8469 XSetClipMask(xDisplay, player.pieceGC, mask);
8473 DragPieceMove (int x, int y)
8477 /* Are we animating? */
8478 if (!appData.animateDragging || appData.blindfold)
8482 if (! player.dragActive)
8484 /* Move piece, maintaining same relative position
8485 of mouse within square */
8486 corner.x = x - player.mouseDelta.x;
8487 corner.y = y - player.mouseDelta.y;
8488 AnimationFrame(&player, &corner, player.dragPiece);
8490 if (appData.highlightDragging) {
8492 BoardSquare(x, y, &boardX, &boardY);
8493 SetHighlights(fromX, fromY, boardX, boardY);
8499 DragPieceEnd (int x, int y)
8501 int boardX, boardY, color;
8504 /* Are we animating? */
8505 if (!appData.animateDragging || appData.blindfold)
8509 if (! player.dragActive)
8511 /* Last frame in sequence is square piece is
8512 placed on, which may not match mouse exactly. */
8513 BoardSquare(x, y, &boardX, &boardY);
8514 ScreenSquare(boardX, boardY, &corner, &color);
8515 EndAnimation(&player, &corner);
8517 /* Be sure end square is redrawn */
8518 damage[0][boardY][boardX] = True;
8520 /* This prevents weird things happening with fast successive
8521 clicks which on my Sun at least can cause motion events
8522 without corresponding press/release. */
8523 player.dragActive = False;
8526 /* Handle expose event while piece being dragged */
8531 if (!player.dragActive || appData.blindfold)
8534 /* What we're doing: logically, the move hasn't been made yet,
8535 so the piece is still in it's original square. But visually
8536 it's being dragged around the board. So we erase the square
8537 that the piece is on and draw it at the last known drag point. */
8538 BlankSquare(player.startSquare.x, player.startSquare.y,
8539 player.startColor, EmptySquare, xBoardWindow, 1);
8540 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8541 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8544 #include <sys/ioctl.h>
8548 int fd, default_width;
8551 default_width = 79; // this is FICS default anyway...
8553 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8555 if (!ioctl(fd, TIOCGSIZE, &win))
8556 default_width = win.ts_cols;
8557 #elif defined(TIOCGWINSZ)
8559 if (!ioctl(fd, TIOCGWINSZ, &win))
8560 default_width = win.ws_col;
8562 return default_width;
8568 static int old_width = 0;
8569 int new_width = get_term_width();
8571 if (old_width != new_width)
8572 ics_printf("set width %d\n", new_width);
8573 old_width = new_width;
8577 NotifyFrontendLogin ()
8582 /* [AS] Arrow highlighting support */
8584 static double A_WIDTH = 5; /* Width of arrow body */
8586 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8587 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8598 return (int) (x + 0.5);
8602 SquareToPos (int rank, int file, int *x, int *y)
8605 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8606 *y = lineGap + rank * (squareSize + lineGap);
8608 *x = lineGap + file * (squareSize + lineGap);
8609 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8613 /* Draw an arrow between two points using current settings */
8615 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8618 double dx, dy, j, k, x, y;
8621 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8623 arrow[0].x = s_x + A_WIDTH + 0.5;
8626 arrow[1].x = s_x + A_WIDTH + 0.5;
8627 arrow[1].y = d_y - h;
8629 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8630 arrow[2].y = d_y - h;
8635 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8636 arrow[5].y = d_y - h;
8638 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8639 arrow[4].y = d_y - h;
8641 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8644 else if( d_y == s_y ) {
8645 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8648 arrow[0].y = s_y + A_WIDTH + 0.5;
8650 arrow[1].x = d_x - w;
8651 arrow[1].y = s_y + A_WIDTH + 0.5;
8653 arrow[2].x = d_x - w;
8654 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8659 arrow[5].x = d_x - w;
8660 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8662 arrow[4].x = d_x - w;
8663 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8666 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8669 /* [AS] Needed a lot of paper for this! :-) */
8670 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8671 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8673 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8675 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8680 arrow[0].x = Round(x - j);
8681 arrow[0].y = Round(y + j*dx);
8683 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8684 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8687 x = (double) d_x - k;
8688 y = (double) d_y - k*dy;
8691 x = (double) d_x + k;
8692 y = (double) d_y + k*dy;
8695 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8697 arrow[6].x = Round(x - j);
8698 arrow[6].y = Round(y + j*dx);
8700 arrow[2].x = Round(arrow[6].x + 2*j);
8701 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8703 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8704 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8709 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8710 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8713 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8714 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8715 // Polygon( hdc, arrow, 7 );
8719 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8722 hor = 64*s_col + 32; vert = 64*s_row + 32;
8723 for(i=0; i<= 64; i++) {
8724 damage[0][vert+6>>6][hor+6>>6] = True;
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 hor += d_col - s_col; vert += d_row - s_row;
8732 /* [AS] Draw an arrow between two squares */
8734 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8736 int s_x, s_y, d_x, d_y;
8738 if( s_col == d_col && s_row == d_row ) {
8742 /* Get source and destination points */
8743 SquareToPos( s_row, s_col, &s_x, &s_y);
8744 SquareToPos( d_row, d_col, &d_x, &d_y);
8747 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8749 else if( d_y < s_y ) {
8750 d_y += squareSize / 2 + squareSize / 4;
8753 d_y += squareSize / 2;
8757 d_x += squareSize / 2 - squareSize / 4;
8759 else if( d_x < s_x ) {
8760 d_x += squareSize / 2 + squareSize / 4;
8763 d_x += squareSize / 2;
8766 s_x += squareSize / 2;
8767 s_y += squareSize / 2;
8770 A_WIDTH = squareSize / 14.; //[HGM] make float
8772 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8773 ArrowDamage(s_col, s_row, d_col, d_row);
8777 IsDrawArrowEnabled ()
8779 return appData.highlightMoveWithArrow && squareSize >= 32;
8783 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8785 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8786 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8790 UpdateLogos (int displ)
8792 return; // no logos in XBoard yet