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 GuideProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void HomePageProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void NewsPageProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void BugReportProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
453 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
454 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
455 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void DisplayMove P((int moveNumber));
458 void DisplayTitle P((char *title));
459 void ICSInitScript P((void));
460 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
461 void ErrorPopUp P((char *title, char *text, int modal));
462 void ErrorPopDown P((void));
463 static char *ExpandPathName P((char *path));
464 static void CreateAnimVars P((void));
465 static void DragPieceMove P((int x, int y));
466 static void DrawDragPiece P((void));
467 char *ModeToWidgetName P((GameMode mode));
468 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
477 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
478 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
479 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
480 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
481 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
482 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
483 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
484 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
485 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
486 void GameListOptionsPopDown P(());
487 void GenericPopDown P(());
488 void update_ics_width P(());
489 int get_term_width P(());
490 int CopyMemoProc P(());
491 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
492 Boolean IsDrawArrowEnabled P(());
495 * XBoard depends on Xt R4 or higher
497 int xtVersion = XtSpecificationRelease;
502 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
503 jailSquareColor, highlightSquareColor, premoveHighlightColor;
504 Pixel lowTimeWarningColor;
505 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
506 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
507 wjPieceGC, bjPieceGC, prelineGC, countGC;
508 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
509 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
510 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
511 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
512 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
513 ICSInputShell, fileNameShell, askQuestionShell;
514 Widget historyShell, evalGraphShell, gameListShell;
515 int hOffset; // [HGM] dual
516 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
517 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
518 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
520 XFontSet fontSet, clockFontSet;
523 XFontStruct *clockFontStruct;
525 Font coordFontID, countFontID;
526 XFontStruct *coordFontStruct, *countFontStruct;
527 XtAppContext appContext;
529 char *oldICSInteractionTitle;
533 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
535 Position commentX = -1, commentY = -1;
536 Dimension commentW, commentH;
537 typedef unsigned int BoardSize;
539 Boolean chessProgram;
541 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
542 int squareSize, smallLayout = 0, tinyLayout = 0,
543 marginW, marginH, // [HGM] for run-time resizing
544 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
545 ICSInputBoxUp = False, askQuestionUp = False,
546 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
547 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
548 Dimension textHeight;
549 Pixel timerForegroundPixel, timerBackgroundPixel;
550 Pixel buttonForegroundPixel, buttonBackgroundPixel;
551 char *chessDir, *programName, *programVersion,
552 *gameCopyFilename, *gamePasteFilename;
553 Boolean alwaysOnTop = False;
554 Boolean saveSettingsOnExit;
555 char *settingsFileName;
556 char *icsTextMenuString;
558 char *firstChessProgramNames;
559 char *secondChessProgramNames;
561 WindowPlacement wpMain;
562 WindowPlacement wpConsole;
563 WindowPlacement wpComment;
564 WindowPlacement wpMoveHistory;
565 WindowPlacement wpEvalGraph;
566 WindowPlacement wpEngineOutput;
567 WindowPlacement wpGameList;
568 WindowPlacement wpTags;
570 extern Widget shells[];
571 extern Boolean shellUp[];
575 Pixmap pieceBitmap[2][(int)BlackPawn];
576 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
577 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
578 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
579 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
580 Pixmap xpmBoardBitmap[2];
581 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
582 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
583 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
584 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
585 XImage *ximLightSquare, *ximDarkSquare;
588 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
589 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
591 #define White(piece) ((int)(piece) < (int)BlackPawn)
593 /* Variables for doing smooth animation. This whole thing
594 would be much easier if the board was double-buffered,
595 but that would require a fairly major rewrite. */
600 GC blitGC, pieceGC, outlineGC;
601 XPoint startSquare, prevFrame, mouseDelta;
605 int startBoardX, startBoardY;
608 /* There can be two pieces being animated at once: a player
609 can begin dragging a piece before the remote opponent has moved. */
611 static AnimState game, player;
613 /* Bitmaps for use as masks when drawing XPM pieces.
614 Need one for each black and white piece. */
615 static Pixmap xpmMask[BlackKing + 1];
617 /* This magic number is the number of intermediate frames used
618 in each half of the animation. For short moves it's reduced
619 by 1. The total number of frames will be factor * 2 + 1. */
622 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
624 MenuItem fileMenu[] = {
625 {N_("New Game Ctrl+N"), "New Game", ResetProc},
626 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
627 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
628 {"----", NULL, NothingProc},
629 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
630 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
631 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
632 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
633 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
634 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
635 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
636 {"----", NULL, NothingProc},
637 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
638 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
639 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
640 {"----", NULL, NothingProc},
641 {N_("Mail Move"), "Mail Move", MailMoveProc},
642 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
643 {"----", NULL, NothingProc},
644 {N_("Quit Ctr+Q"), "Exit", QuitProc},
648 MenuItem editMenu[] = {
649 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
650 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
651 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
652 {"----", NULL, NothingProc},
653 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
654 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
655 {"----", NULL, NothingProc},
656 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
657 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
658 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
659 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
660 {N_("Edit Book"), "Edit Book", EditBookProc},
661 {"----", NULL, NothingProc},
662 {N_("Revert Home"), "Revert", RevertProc},
663 {N_("Annotate"), "Annotate", AnnotateProc},
664 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
665 {"----", NULL, NothingProc},
666 {N_("Backward Alt+Left"), "Backward", BackwardProc},
667 {N_("Forward Alt+Right"), "Forward", ForwardProc},
668 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
669 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
673 MenuItem viewMenu[] = {
674 {N_("Flip View F2"), "Flip View", FlipViewProc},
675 {"----", NULL, NothingProc},
676 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
677 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
678 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
679 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
680 {N_("ICS text menu"), "ICStex", IcsTextProc},
681 {"----", NULL, NothingProc},
682 {N_("Tags"), "Show Tags", EditTagsProc},
683 {N_("Comments"), "Show Comments", EditCommentProc},
684 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
685 {"----", NULL, NothingProc},
686 {N_("Board..."), "Board Options", BoardOptionsProc},
687 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
691 MenuItem modeMenu[] = {
692 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
693 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
694 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
695 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
696 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
697 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
698 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
699 {N_("Training"), "Training", TrainingProc},
700 {N_("ICS Client"), "ICS Client", IcsClientProc},
701 {"----", NULL, NothingProc},
702 {N_("Machine Match"), "Machine Match", MatchProc},
703 {N_("Pause Pause"), "Pause", PauseProc},
707 MenuItem actionMenu[] = {
708 {N_("Accept F3"), "Accept", AcceptProc},
709 {N_("Decline F4"), "Decline", DeclineProc},
710 {N_("Rematch F12"), "Rematch", RematchProc},
711 {"----", NULL, NothingProc},
712 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
713 {N_("Draw F6"), "Draw", DrawProc},
714 {N_("Adjourn F7"), "Adjourn", AdjournProc},
715 {N_("Abort F8"),"Abort", AbortProc},
716 {N_("Resign F9"), "Resign", ResignProc},
717 {"----", NULL, NothingProc},
718 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
719 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
720 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
721 {"----", NULL, NothingProc},
722 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
723 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
724 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
728 MenuItem engineMenu[] = {
729 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
730 {"----", NULL, NothingProc},
731 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
732 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
733 {"----", NULL, NothingProc},
734 {N_("Hint"), "Hint", HintProc},
735 {N_("Book"), "Book", BookProc},
736 {"----", NULL, NothingProc},
737 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
738 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
742 MenuItem optionsMenu[] = {
743 #define OPTIONSDIALOG
745 {N_("General ..."), "General", OptionsProc},
747 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
748 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
749 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
750 {N_("ICS ..."), "ICS", IcsOptionsProc},
751 {N_("Match ..."), "Match", MatchOptionsProc},
752 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
753 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
754 // {N_(" ..."), "", OptionsProc},
755 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
756 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
757 {"----", NULL, NothingProc},
758 #ifndef OPTIONSDIALOG
759 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
760 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
761 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
762 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
763 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
764 {N_("Blindfold"), "Blindfold", BlindfoldProc},
765 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
767 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
769 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
770 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
771 {N_("Move Sound"), "Move Sound", MoveSoundProc},
772 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
773 {N_("One-Click Moving"), "OneClick", OneClickProc},
774 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
775 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
776 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
777 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
778 // {N_("Premove"), "Premove", PremoveProc},
779 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
780 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
781 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
782 {"----", NULL, NothingProc},
784 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
785 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
789 MenuItem helpMenu[] = {
790 {N_("Info XBoard"), "Info XBoard", InfoProc},
791 {N_("Man XBoard F1"), "Man XBoard", ManProc},
792 {"----", NULL, NothingProc},
793 {N_("XBoard Home Page"), "Home Page", HomePageProc},
794 {N_("On-line User Guide"), "User Guide", GuideProc},
795 {N_("Development News"), "News Page", NewsPageProc},
796 {N_("e-Mail Bug Report"), "Bug Report", BugReportProc},
797 {"----", NULL, NothingProc},
798 {N_("About XBoard"), "About XBoard", AboutProc},
803 {N_("File"), "File", fileMenu},
804 {N_("Edit"), "Edit", editMenu},
805 {N_("View"), "View", viewMenu},
806 {N_("Mode"), "Mode", modeMenu},
807 {N_("Action"), "Action", actionMenu},
808 {N_("Engine"), "Engine", engineMenu},
809 {N_("Options"), "Options", optionsMenu},
810 {N_("Help"), "Help", helpMenu},
814 #define PAUSE_BUTTON "P"
815 MenuItem buttonBar[] = {
816 {"<<", "<<", ToStartProc},
817 {"<", "<", BackwardProc},
818 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
819 {">", ">", ForwardProc},
820 {">>", ">>", ToEndProc},
824 #define PIECE_MENU_SIZE 18
825 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
826 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
827 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
828 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
829 N_("Empty square"), N_("Clear board") },
830 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
831 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
832 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
833 N_("Empty square"), N_("Clear board") }
835 /* must be in same order as pieceMenuStrings! */
836 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
837 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
838 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
839 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
840 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
841 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
842 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
843 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
844 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
847 #define DROP_MENU_SIZE 6
848 String dropMenuStrings[DROP_MENU_SIZE] = {
849 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
851 /* must be in same order as dropMenuStrings! */
852 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
853 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
854 WhiteRook, WhiteQueen
862 DropMenuEnables dmEnables[] = {
880 { XtNborderWidth, 0 },
881 { XtNdefaultDistance, 0 },
885 { XtNborderWidth, 0 },
886 { XtNresizable, (XtArgVal) True },
890 { XtNborderWidth, 0 },
896 { XtNjustify, (XtArgVal) XtJustifyRight },
897 { XtNlabel, (XtArgVal) "..." },
898 { XtNresizable, (XtArgVal) True },
899 { XtNresize, (XtArgVal) False }
902 Arg messageArgs[] = {
903 { XtNjustify, (XtArgVal) XtJustifyLeft },
904 { XtNlabel, (XtArgVal) "..." },
905 { XtNresizable, (XtArgVal) True },
906 { XtNresize, (XtArgVal) False }
910 { XtNborderWidth, 0 },
911 { XtNjustify, (XtArgVal) XtJustifyLeft }
914 XtResource clientResources[] = {
915 { "flashCount", "flashCount", XtRInt, sizeof(int),
916 XtOffset(AppDataPtr, flashCount), XtRImmediate,
917 (XtPointer) FLASH_COUNT },
920 XrmOptionDescRec shellOptions[] = {
921 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
922 { "-flash", "flashCount", XrmoptionNoArg, "3" },
923 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
926 XtActionsRec boardActions[] = {
927 { "DrawPosition", DrawPositionProc },
928 { "HandleUserMove", HandleUserMove },
929 { "AnimateUserMove", AnimateUserMove },
930 { "HandlePV", HandlePV },
931 { "SelectPV", SelectPV },
932 { "StopPV", StopPV },
933 { "FileNameAction", FileNameAction },
934 { "AskQuestionProc", AskQuestionProc },
935 { "AskQuestionReplyAction", AskQuestionReplyAction },
936 { "PieceMenuPopup", PieceMenuPopup },
937 { "WhiteClock", WhiteClock },
938 { "BlackClock", BlackClock },
939 { "ResetProc", ResetProc },
940 { "NewVariantProc", NewVariantProc },
941 { "LoadGameProc", LoadGameProc },
942 { "LoadNextGameProc", LoadNextGameProc },
943 { "LoadPrevGameProc", LoadPrevGameProc },
944 { "LoadSelectedProc", LoadSelectedProc },
945 { "SetFilterProc", SetFilterProc },
946 { "ReloadGameProc", ReloadGameProc },
947 { "LoadPositionProc", LoadPositionProc },
948 { "LoadNextPositionProc", LoadNextPositionProc },
949 { "LoadPrevPositionProc", LoadPrevPositionProc },
950 { "ReloadPositionProc", ReloadPositionProc },
951 { "CopyPositionProc", CopyPositionProc },
952 { "PastePositionProc", PastePositionProc },
953 { "CopyGameProc", CopyGameProc },
954 { "CopyGameListProc", CopyGameListProc },
955 { "PasteGameProc", PasteGameProc },
956 { "SaveGameProc", SaveGameProc },
957 { "SavePositionProc", SavePositionProc },
958 { "MailMoveProc", MailMoveProc },
959 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
960 { "QuitProc", QuitProc },
961 { "MachineWhiteProc", MachineWhiteProc },
962 { "MachineBlackProc", MachineBlackProc },
963 { "AnalysisModeProc", AnalyzeModeProc },
964 { "AnalyzeFileProc", AnalyzeFileProc },
965 { "TwoMachinesProc", TwoMachinesProc },
966 { "IcsClientProc", IcsClientProc },
967 { "EditGameProc", EditGameProc },
968 { "EditPositionProc", EditPositionProc },
969 { "TrainingProc", EditPositionProc },
970 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
971 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
972 { "ShowGameListProc", ShowGameListProc },
973 { "ShowMoveListProc", HistoryShowProc},
974 { "EditTagsProc", EditTagsProc },
975 { "EditBookProc", EditBookProc },
976 { "EditCommentProc", EditCommentProc },
977 { "IcsInputBoxProc", IcsInputBoxProc },
978 { "PauseProc", PauseProc },
979 { "AcceptProc", AcceptProc },
980 { "DeclineProc", DeclineProc },
981 { "RematchProc", RematchProc },
982 { "CallFlagProc", CallFlagProc },
983 { "DrawProc", DrawProc },
984 { "AdjournProc", AdjournProc },
985 { "AbortProc", AbortProc },
986 { "ResignProc", ResignProc },
987 { "AdjuWhiteProc", AdjuWhiteProc },
988 { "AdjuBlackProc", AdjuBlackProc },
989 { "AdjuDrawProc", AdjuDrawProc },
990 { "TypeInProc", TypeInProc },
991 { "EnterKeyProc", EnterKeyProc },
992 { "UpKeyProc", UpKeyProc },
993 { "DownKeyProc", DownKeyProc },
994 { "StopObservingProc", StopObservingProc },
995 { "StopExaminingProc", StopExaminingProc },
996 { "UploadProc", UploadProc },
997 { "BackwardProc", BackwardProc },
998 { "ForwardProc", ForwardProc },
999 { "TempBackwardProc", TempBackwardProc },
1000 { "TempForwardProc", TempForwardProc },
1001 { "ToStartProc", ToStartProc },
1002 { "ToEndProc", ToEndProc },
1003 { "RevertProc", RevertProc },
1004 { "AnnotateProc", AnnotateProc },
1005 { "TruncateGameProc", TruncateGameProc },
1006 { "MoveNowProc", MoveNowProc },
1007 { "RetractMoveProc", RetractMoveProc },
1008 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
1009 { "UciMenuProc", (XtActionProc) UciMenuProc },
1010 { "TimeControlProc", (XtActionProc) TimeControlProc },
1011 { "FlipViewProc", FlipViewProc },
1012 { "PonderNextMoveProc", PonderNextMoveProc },
1013 #ifndef OPTIONSDIALOG
1014 { "AlwaysQueenProc", AlwaysQueenProc },
1015 { "AnimateDraggingProc", AnimateDraggingProc },
1016 { "AnimateMovingProc", AnimateMovingProc },
1017 { "AutoflagProc", AutoflagProc },
1018 { "AutoflipProc", AutoflipProc },
1019 { "BlindfoldProc", BlindfoldProc },
1020 { "FlashMovesProc", FlashMovesProc },
1022 { "HighlightDraggingProc", HighlightDraggingProc },
1024 { "HighlightLastMoveProc", HighlightLastMoveProc },
1025 // { "IcsAlarmProc", IcsAlarmProc },
1026 { "MoveSoundProc", MoveSoundProc },
1027 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1028 { "PopupExitMessageProc", PopupExitMessageProc },
1029 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1030 // { "PremoveProc", PremoveProc },
1031 { "ShowCoordsProc", ShowCoordsProc },
1032 { "ShowThinkingProc", ShowThinkingProc },
1033 { "HideThinkingProc", HideThinkingProc },
1034 { "TestLegalityProc", TestLegalityProc },
1036 { "SaveSettingsProc", SaveSettingsProc },
1037 { "SaveOnExitProc", SaveOnExitProc },
1038 { "InfoProc", InfoProc },
1039 { "ManProc", ManProc },
1040 { "HintProc", HintProc },
1041 { "BookProc", BookProc },
1042 { "AboutGameProc", AboutGameProc },
1043 { "AboutProc", AboutProc },
1044 { "DebugProc", DebugProc },
1045 { "NothingProc", NothingProc },
1046 { "CommentClick", (XtActionProc) CommentClick },
1047 { "CommentPopDown", (XtActionProc) CommentPopDown },
1048 { "TagsPopDown", (XtActionProc) TagsPopDown },
1049 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1050 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1051 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1052 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1053 { "GameListPopDown", (XtActionProc) GameListPopDown },
1054 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1055 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1056 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1057 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1058 { "GenericPopDown", (XtActionProc) GenericPopDown },
1059 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1060 { "SelectMove", (XtActionProc) SelectMove },
1063 char globalTranslations[] =
1064 ":<Key>F9: ResignProc() \n \
1065 :Ctrl<Key>n: ResetProc() \n \
1066 :Meta<Key>V: NewVariantProc() \n \
1067 :Ctrl<Key>o: LoadGameProc() \n \
1068 :Meta<Key>Next: LoadNextGameProc() \n \
1069 :Meta<Key>Prior: LoadPrevGameProc() \n \
1070 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1071 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1072 :Ctrl<Key>s: SaveGameProc() \n \
1073 :Ctrl<Key>c: CopyGameProc() \n \
1074 :Ctrl<Key>v: PasteGameProc() \n \
1075 :Ctrl<Key>O: LoadPositionProc() \n \
1076 :Shift<Key>Next: LoadNextPositionProc() \n \
1077 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1078 :Ctrl<Key>S: SavePositionProc() \n \
1079 :Ctrl<Key>C: CopyPositionProc() \n \
1080 :Ctrl<Key>V: PastePositionProc() \n \
1081 :Ctrl<Key>q: QuitProc() \n \
1082 :Ctrl<Key>w: MachineWhiteProc() \n \
1083 :Ctrl<Key>b: MachineBlackProc() \n \
1084 :Ctrl<Key>t: TwoMachinesProc() \n \
1085 :Ctrl<Key>a: AnalysisModeProc() \n \
1086 :Ctrl<Key>g: AnalyzeFileProc() \n \
1087 :Ctrl<Key>e: EditGameProc() \n \
1088 :Ctrl<Key>E: EditPositionProc() \n \
1089 :Meta<Key>O: EngineOutputProc() \n \
1090 :Meta<Key>E: EvalGraphProc() \n \
1091 :Meta<Key>G: ShowGameListProc() \n \
1092 :Meta<Key>H: ShowMoveListProc() \n \
1093 :<Key>Pause: PauseProc() \n \
1094 :<Key>F3: AcceptProc() \n \
1095 :<Key>F4: DeclineProc() \n \
1096 :<Key>F12: RematchProc() \n \
1097 :<Key>F5: CallFlagProc() \n \
1098 :<Key>F6: DrawProc() \n \
1099 :<Key>F7: AdjournProc() \n \
1100 :<Key>F8: AbortProc() \n \
1101 :<Key>F10: StopObservingProc() \n \
1102 :<Key>F11: StopExaminingProc() \n \
1103 :Meta Ctrl<Key>F12: DebugProc() \n \
1104 :Meta<Key>End: ToEndProc() \n \
1105 :Meta<Key>Right: ForwardProc() \n \
1106 :Meta<Key>Home: ToStartProc() \n \
1107 :Meta<Key>Left: BackwardProc() \n \
1108 :<Key>Left: BackwardProc() \n \
1109 :<Key>Right: ForwardProc() \n \
1110 :<Key>Home: RevertProc() \n \
1111 :<Key>End: TruncateGameProc() \n \
1112 :Ctrl<Key>m: MoveNowProc() \n \
1113 :Ctrl<Key>x: RetractMoveProc() \n \
1114 :Meta<Key>J: EngineMenuProc() \n \
1115 :Meta<Key>U: UciMenuProc() \n \
1116 :Meta<Key>T: TimeControlProc() \n \
1117 :Ctrl<Key>P: PonderNextMoveProc() \n "
1118 #ifndef OPTIONSDIALOG
1120 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1121 :Ctrl<Key>F: AutoflagProc() \n \
1122 :Ctrl<Key>A: AnimateMovingProc() \n \
1123 :Ctrl<Key>L: TestLegalityProc() \n \
1124 :Ctrl<Key>H: HideThinkingProc() \n "
1127 :<Key>F1: ManProc() \n \
1128 :<Key>F2: FlipViewProc() \n \
1129 :<KeyDown>Return: TempBackwardProc() \n \
1130 :<KeyUp>Return: TempForwardProc() \n";
1132 char boardTranslations[] =
1133 "<Btn1Down>: HandleUserMove(0) \n \
1134 Shift<Btn1Up>: HandleUserMove(1) \n \
1135 <Btn1Up>: HandleUserMove(0) \n \
1136 <Btn1Motion>: AnimateUserMove() \n \
1137 <Btn3Motion>: HandlePV() \n \
1138 <Btn2Motion>: HandlePV() \n \
1139 <Btn3Up>: PieceMenuPopup(menuB) \n \
1140 <Btn2Up>: PieceMenuPopup(menuB) \n \
1141 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1142 PieceMenuPopup(menuB) \n \
1143 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1144 PieceMenuPopup(menuW) \n \
1145 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1146 PieceMenuPopup(menuW) \n \
1147 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1148 PieceMenuPopup(menuB) \n";
1150 char whiteTranslations[] =
1151 "Shift<BtnDown>: WhiteClock(1)\n \
1152 <BtnDown>: WhiteClock(0)\n";
1153 char blackTranslations[] =
1154 "Shift<BtnDown>: BlackClock(1)\n \
1155 <BtnDown>: BlackClock(0)\n";
1157 char ICSInputTranslations[] =
1158 "<Key>Up: UpKeyProc() \n "
1159 "<Key>Down: DownKeyProc() \n "
1160 "<Key>Return: EnterKeyProc() \n";
1162 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1163 // as the widget is destroyed before the up-click can call extend-end
1164 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1166 String xboardResources[] = {
1167 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1168 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1169 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1174 /* Max possible square size */
1175 #define MAXSQSIZE 256
1177 static int xpm_avail[MAXSQSIZE];
1179 #ifdef HAVE_DIR_STRUCT
1181 /* Extract piece size from filename */
1183 xpm_getsize (char *name, int len, char *ext)
1191 if ((p=strchr(name, '.')) == NULL ||
1192 StrCaseCmp(p+1, ext) != 0)
1198 while (*p && isdigit(*p))
1205 /* Setup xpm_avail */
1207 xpm_getavail (char *dirname, char *ext)
1213 for (i=0; i<MAXSQSIZE; ++i)
1216 if (appData.debugMode)
1217 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1219 dir = opendir(dirname);
1222 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1223 programName, dirname);
1227 while ((ent=readdir(dir)) != NULL) {
1228 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1229 if (i > 0 && i < MAXSQSIZE)
1239 xpm_print_avail (FILE *fp, char *ext)
1243 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1244 for (i=1; i<MAXSQSIZE; ++i) {
1250 /* Return XPM piecesize closest to size */
1252 xpm_closest_to (char *dirname, int size, char *ext)
1255 int sm_diff = MAXSQSIZE;
1259 xpm_getavail(dirname, ext);
1261 if (appData.debugMode)
1262 xpm_print_avail(stderr, ext);
1264 for (i=1; i<MAXSQSIZE; ++i) {
1267 diff = (diff<0) ? -diff : diff;
1268 if (diff < sm_diff) {
1276 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1282 #else /* !HAVE_DIR_STRUCT */
1283 /* If we are on a system without a DIR struct, we can't
1284 read the directory, so we can't collect a list of
1285 filenames, etc., so we can't do any size-fitting. */
1287 xpm_closest_to (char *dirname, int size, char *ext)
1289 fprintf(stderr, _("\
1290 Warning: No DIR structure found on this system --\n\
1291 Unable to autosize for XPM/XIM pieces.\n\
1292 Please report this error to %s.\n\
1293 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1296 #endif /* HAVE_DIR_STRUCT */
1298 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1299 "magenta", "cyan", "white" };
1303 TextColors textColors[(int)NColorClasses];
1305 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1307 parse_color (char *str, int which)
1309 char *p, buf[100], *d;
1312 if (strlen(str) > 99) /* watch bounds on buf */
1317 for (i=0; i<which; ++i) {
1324 /* Could be looking at something like:
1326 .. in which case we want to stop on a comma also */
1327 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1331 return -1; /* Use default for empty field */
1334 if (which == 2 || isdigit(*p))
1337 while (*p && isalpha(*p))
1342 for (i=0; i<8; ++i) {
1343 if (!StrCaseCmp(buf, cnames[i]))
1344 return which? (i+40) : (i+30);
1346 if (!StrCaseCmp(buf, "default")) return -1;
1348 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1353 parse_cpair (ColorClass cc, char *str)
1355 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1356 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1361 /* bg and attr are optional */
1362 textColors[(int)cc].bg = parse_color(str, 1);
1363 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1364 textColors[(int)cc].attr = 0;
1370 /* Arrange to catch delete-window events */
1371 Atom wm_delete_window;
1373 CatchDeleteWindow (Widget w, String procname)
1376 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1377 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1378 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1385 XtSetArg(args[0], XtNiconic, False);
1386 XtSetValues(shellWidget, args, 1);
1388 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1391 //---------------------------------------------------------------------------------------------------------
1392 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1395 #define CW_USEDEFAULT (1<<31)
1396 #define ICS_TEXT_MENU_SIZE 90
1397 #define DEBUG_FILE "xboard.debug"
1398 #define SetCurrentDirectory chdir
1399 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1403 // these two must some day move to frontend.h, when they are implemented
1404 Boolean GameListIsUp();
1406 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1409 // front-end part of option handling
1411 // [HGM] This platform-dependent table provides the location for storing the color info
1412 extern char *crWhite, * crBlack;
1416 &appData.whitePieceColor,
1417 &appData.blackPieceColor,
1418 &appData.lightSquareColor,
1419 &appData.darkSquareColor,
1420 &appData.highlightSquareColor,
1421 &appData.premoveHighlightColor,
1422 &appData.lowTimeWarningColor,
1433 // [HGM] font: keep a font for each square size, even non-stndard ones
1434 #define NUM_SIZES 18
1435 #define MAX_SIZE 130
1436 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1437 char *fontTable[NUM_FONTS][MAX_SIZE];
1440 ParseFont (char *name, int number)
1441 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1443 if(sscanf(name, "size%d:", &size)) {
1444 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1445 // defer processing it until we know if it matches our board size
1446 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1447 fontTable[number][size] = strdup(strchr(name, ':')+1);
1448 fontValid[number][size] = True;
1453 case 0: // CLOCK_FONT
1454 appData.clockFont = strdup(name);
1456 case 1: // MESSAGE_FONT
1457 appData.font = strdup(name);
1459 case 2: // COORD_FONT
1460 appData.coordFont = strdup(name);
1465 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1470 { // only 2 fonts currently
1471 appData.clockFont = CLOCK_FONT_NAME;
1472 appData.coordFont = COORD_FONT_NAME;
1473 appData.font = DEFAULT_FONT_NAME;
1478 { // no-op, until we identify the code for this already in XBoard and move it here
1482 ParseColor (int n, char *name)
1483 { // in XBoard, just copy the color-name string
1484 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1488 ParseTextAttribs (ColorClass cc, char *s)
1490 (&appData.colorShout)[cc] = strdup(s);
1494 ParseBoardSize (void *addr, char *name)
1496 appData.boardSize = strdup(name);
1501 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1505 SetCommPortDefaults ()
1506 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1509 // [HGM] args: these three cases taken out to stay in front-end
1511 SaveFontArg (FILE *f, ArgDescriptor *ad)
1514 int i, n = (int)(intptr_t)ad->argLoc;
1516 case 0: // CLOCK_FONT
1517 name = appData.clockFont;
1519 case 1: // MESSAGE_FONT
1520 name = appData.font;
1522 case 2: // COORD_FONT
1523 name = appData.coordFont;
1528 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1529 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1530 fontTable[n][squareSize] = strdup(name);
1531 fontValid[n][squareSize] = True;
1534 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1535 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1540 { // nothing to do, as the sounds are at all times represented by their text-string names already
1544 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1545 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1546 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1550 SaveColor (FILE *f, ArgDescriptor *ad)
1551 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1552 if(colorVariable[(int)(intptr_t)ad->argLoc])
1553 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1557 SaveBoardSize (FILE *f, char *name, void *addr)
1558 { // wrapper to shield back-end from BoardSize & sizeInfo
1559 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1563 ParseCommPortSettings (char *s)
1564 { // no such option in XBoard (yet)
1567 extern Widget engineOutputShell;
1571 GetActualPlacement (Widget wg, WindowPlacement *wp)
1576 XWindowAttributes winAt;
1583 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1584 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1585 wp->x = rx - winAt.x;
1586 wp->y = ry - winAt.y;
1587 wp->height = winAt.height;
1588 wp->width = winAt.width;
1589 frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1594 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1595 // In XBoard this will have to wait until awareness of window parameters is implemented
1596 GetActualPlacement(shellWidget, &wpMain);
1597 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1598 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1599 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1600 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1601 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1602 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1606 PrintCommPortSettings (FILE *f, char *name)
1607 { // This option does not exist in XBoard
1611 MySearchPath (char *installDir, char *name, char *fullname)
1612 { // just append installDir and name. Perhaps ExpandPath should be used here?
1613 name = ExpandPathName(name);
1614 if(name && name[0] == '/')
1615 safeStrCpy(fullname, name, MSG_SIZ );
1617 sprintf(fullname, "%s%c%s", installDir, '/', name);
1623 MyGetFullPathName (char *name, char *fullname)
1624 { // should use ExpandPath?
1625 name = ExpandPathName(name);
1626 safeStrCpy(fullname, name, MSG_SIZ );
1631 EnsureOnScreen (int *x, int *y, int minX, int minY)
1638 { // [HGM] args: allows testing if main window is realized from back-end
1639 return xBoardWindow != 0;
1643 PopUpStartupDialog ()
1644 { // start menu not implemented in XBoard
1648 ConvertToLine (int argc, char **argv)
1650 static char line[128*1024], buf[1024];
1654 for(i=1; i<argc; i++)
1656 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1657 && argv[i][0] != '{' )
1658 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1660 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1661 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1664 line[strlen(line)-1] = NULLCHAR;
1668 //--------------------------------------------------------------------------------------------
1670 extern Boolean twoBoards, partnerUp;
1673 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1675 #define BoardSize int
1677 InitDrawingSizes (BoardSize boardSize, int flags)
1678 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1679 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1681 XtGeometryResult gres;
1683 static Dimension oldWidth, oldHeight;
1684 static VariantClass oldVariant;
1685 static int oldDual = -1, oldMono = -1;
1687 if(!formWidget) return;
1689 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1690 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1691 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1693 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1695 * Enable shell resizing.
1697 shellArgs[0].value = (XtArgVal) &w;
1698 shellArgs[1].value = (XtArgVal) &h;
1699 XtGetValues(shellWidget, shellArgs, 2);
1701 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1702 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1703 XtSetValues(shellWidget, &shellArgs[2], 4);
1705 XtSetArg(args[0], XtNdefaultDistance, &sep);
1706 XtGetValues(formWidget, args, 1);
1708 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1710 hOffset = boardWidth + 10;
1711 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1712 secondSegments[i] = gridSegments[i];
1713 secondSegments[i].x1 += hOffset;
1714 secondSegments[i].x2 += hOffset;
1717 XtSetArg(args[0], XtNwidth, boardWidth);
1718 XtSetArg(args[1], XtNheight, boardHeight);
1719 XtSetValues(boardWidget, args, 2);
1721 timerWidth = (boardWidth - sep) / 2;
1722 XtSetArg(args[0], XtNwidth, timerWidth);
1723 XtSetValues(whiteTimerWidget, args, 1);
1724 XtSetValues(blackTimerWidget, args, 1);
1726 XawFormDoLayout(formWidget, False);
1728 if (appData.titleInWindow) {
1730 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1731 XtSetArg(args[i], XtNheight, &h); i++;
1732 XtGetValues(titleWidget, args, i);
1734 w = boardWidth - 2*bor;
1736 XtSetArg(args[0], XtNwidth, &w);
1737 XtGetValues(menuBarWidget, args, 1);
1738 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1741 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1742 if (gres != XtGeometryYes && appData.debugMode) {
1744 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1745 programName, gres, w, h, wr, hr);
1749 XawFormDoLayout(formWidget, True);
1752 * Inhibit shell resizing.
1754 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1755 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1756 shellArgs[4].value = shellArgs[2].value = w;
1757 shellArgs[5].value = shellArgs[3].value = h;
1758 XtSetValues(shellWidget, &shellArgs[0], 6);
1760 XSync(xDisplay, False);
1764 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1767 if(gameInfo.variant != oldVariant) { // and only if variant changed
1770 for(i=0; i<4; i++) {
1772 for(p=0; p<=(int)WhiteKing; p++)
1773 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1774 if(gameInfo.variant == VariantShogi) {
1775 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1776 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1777 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1778 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1779 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1782 if(gameInfo.variant == VariantGothic) {
1783 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1786 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1787 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1788 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1791 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1792 for(p=0; p<=(int)WhiteKing; p++)
1793 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1794 if(gameInfo.variant == VariantShogi) {
1795 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1796 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1797 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1798 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1799 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1802 if(gameInfo.variant == VariantGothic) {
1803 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1806 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1807 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1808 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1813 for(i=0; i<2; i++) {
1815 for(p=0; p<=(int)WhiteKing; p++)
1816 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1817 if(gameInfo.variant == VariantShogi) {
1818 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1819 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1820 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1821 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1822 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1825 if(gameInfo.variant == VariantGothic) {
1826 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1829 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1830 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1831 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1835 oldMono = -10; // kludge to force recreation of animation masks
1836 oldVariant = gameInfo.variant;
1839 if(appData.monoMode != oldMono)
1842 oldMono = appData.monoMode;
1847 ParseIcsTextColors ()
1848 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1849 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1850 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1851 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1852 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1853 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1854 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1855 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1856 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1857 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1858 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1860 if (appData.colorize) {
1862 _("%s: can't parse color names; disabling colorization\n"),
1865 appData.colorize = FALSE;
1871 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1872 XrmValue vFrom, vTo;
1873 int forceMono = False;
1875 if (!appData.monoMode) {
1876 vFrom.addr = (caddr_t) appData.lightSquareColor;
1877 vFrom.size = strlen(appData.lightSquareColor);
1878 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1879 if (vTo.addr == NULL) {
1880 appData.monoMode = True;
1883 lightSquareColor = *(Pixel *) vTo.addr;
1886 if (!appData.monoMode) {
1887 vFrom.addr = (caddr_t) appData.darkSquareColor;
1888 vFrom.size = strlen(appData.darkSquareColor);
1889 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1890 if (vTo.addr == NULL) {
1891 appData.monoMode = True;
1894 darkSquareColor = *(Pixel *) vTo.addr;
1897 if (!appData.monoMode) {
1898 vFrom.addr = (caddr_t) appData.whitePieceColor;
1899 vFrom.size = strlen(appData.whitePieceColor);
1900 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1901 if (vTo.addr == NULL) {
1902 appData.monoMode = True;
1905 whitePieceColor = *(Pixel *) vTo.addr;
1908 if (!appData.monoMode) {
1909 vFrom.addr = (caddr_t) appData.blackPieceColor;
1910 vFrom.size = strlen(appData.blackPieceColor);
1911 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1912 if (vTo.addr == NULL) {
1913 appData.monoMode = True;
1916 blackPieceColor = *(Pixel *) vTo.addr;
1920 if (!appData.monoMode) {
1921 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1922 vFrom.size = strlen(appData.highlightSquareColor);
1923 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1924 if (vTo.addr == NULL) {
1925 appData.monoMode = True;
1928 highlightSquareColor = *(Pixel *) vTo.addr;
1932 if (!appData.monoMode) {
1933 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1934 vFrom.size = strlen(appData.premoveHighlightColor);
1935 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1936 if (vTo.addr == NULL) {
1937 appData.monoMode = True;
1940 premoveHighlightColor = *(Pixel *) vTo.addr;
1948 { // [HGM] taken out of main
1950 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1951 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1952 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1954 if (appData.bitmapDirectory[0] != NULLCHAR) {
1958 CreateXPMBoard(appData.liteBackTextureFile, 1);
1959 CreateXPMBoard(appData.darkBackTextureFile, 0);
1963 /* Create regular pieces */
1964 if (!useImages) CreatePieces();
1969 main (int argc, char **argv)
1971 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1972 XSetWindowAttributes window_attributes;
1974 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1975 XrmValue vFrom, vTo;
1976 XtGeometryResult gres;
1979 int forceMono = False;
1981 srandom(time(0)); // [HGM] book: make random truly random
1983 setbuf(stdout, NULL);
1984 setbuf(stderr, NULL);
1987 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1988 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1992 programName = strrchr(argv[0], '/');
1993 if (programName == NULL)
1994 programName = argv[0];
1999 XtSetLanguageProc(NULL, NULL, NULL);
2000 bindtextdomain(PACKAGE, LOCALEDIR);
2001 textdomain(PACKAGE);
2005 XtAppInitialize(&appContext, "XBoard", shellOptions,
2006 XtNumber(shellOptions),
2007 &argc, argv, xboardResources, NULL, 0);
2008 appData.boardSize = "";
2009 InitAppData(ConvertToLine(argc, argv));
2011 if (p == NULL) p = "/tmp";
2012 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
2013 gameCopyFilename = (char*) malloc(i);
2014 gamePasteFilename = (char*) malloc(i);
2015 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2016 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2018 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2019 clientResources, XtNumber(clientResources),
2022 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2023 static char buf[MSG_SIZ];
2024 EscapeExpand(buf, appData.firstInitString);
2025 appData.firstInitString = strdup(buf);
2026 EscapeExpand(buf, appData.secondInitString);
2027 appData.secondInitString = strdup(buf);
2028 EscapeExpand(buf, appData.firstComputerString);
2029 appData.firstComputerString = strdup(buf);
2030 EscapeExpand(buf, appData.secondComputerString);
2031 appData.secondComputerString = strdup(buf);
2034 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2037 if (chdir(chessDir) != 0) {
2038 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2044 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2045 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2046 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2047 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2050 setbuf(debugFP, NULL);
2054 if (appData.debugMode) {
2055 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2059 /* [HGM,HR] make sure board size is acceptable */
2060 if(appData.NrFiles > BOARD_FILES ||
2061 appData.NrRanks > BOARD_RANKS )
2062 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2065 /* This feature does not work; animation needs a rewrite */
2066 appData.highlightDragging = FALSE;
2070 xDisplay = XtDisplay(shellWidget);
2071 xScreen = DefaultScreen(xDisplay);
2072 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2074 gameInfo.variant = StringToVariant(appData.variant);
2075 InitPosition(FALSE);
2078 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2080 if (isdigit(appData.boardSize[0])) {
2081 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2082 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2083 &fontPxlSize, &smallLayout, &tinyLayout);
2085 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2086 programName, appData.boardSize);
2090 /* Find some defaults; use the nearest known size */
2091 SizeDefaults *szd, *nearest;
2092 int distance = 99999;
2093 nearest = szd = sizeDefaults;
2094 while (szd->name != NULL) {
2095 if (abs(szd->squareSize - squareSize) < distance) {
2097 distance = abs(szd->squareSize - squareSize);
2098 if (distance == 0) break;
2102 if (i < 2) lineGap = nearest->lineGap;
2103 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2104 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2105 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2106 if (i < 6) smallLayout = nearest->smallLayout;
2107 if (i < 7) tinyLayout = nearest->tinyLayout;
2110 SizeDefaults *szd = sizeDefaults;
2111 if (*appData.boardSize == NULLCHAR) {
2112 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2113 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2116 if (szd->name == NULL) szd--;
2117 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2119 while (szd->name != NULL &&
2120 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2121 if (szd->name == NULL) {
2122 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2123 programName, appData.boardSize);
2127 squareSize = szd->squareSize;
2128 lineGap = szd->lineGap;
2129 clockFontPxlSize = szd->clockFontPxlSize;
2130 coordFontPxlSize = szd->coordFontPxlSize;
2131 fontPxlSize = szd->fontPxlSize;
2132 smallLayout = szd->smallLayout;
2133 tinyLayout = szd->tinyLayout;
2134 // [HGM] font: use defaults from settings file if available and not overruled
2136 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2137 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2138 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2139 appData.font = fontTable[MESSAGE_FONT][squareSize];
2140 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2141 appData.coordFont = fontTable[COORD_FONT][squareSize];
2143 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2144 if (strlen(appData.pixmapDirectory) > 0) {
2145 p = ExpandPathName(appData.pixmapDirectory);
2147 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2148 appData.pixmapDirectory);
2151 if (appData.debugMode) {
2152 fprintf(stderr, _("\
2153 XBoard square size (hint): %d\n\
2154 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2156 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2157 if (appData.debugMode) {
2158 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2161 defaultLineGap = lineGap;
2162 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2164 /* [HR] height treated separately (hacked) */
2165 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2166 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2167 if (appData.showJail == 1) {
2168 /* Jail on top and bottom */
2169 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2170 XtSetArg(boardArgs[2], XtNheight,
2171 boardHeight + 2*(lineGap + squareSize));
2172 } else if (appData.showJail == 2) {
2174 XtSetArg(boardArgs[1], XtNwidth,
2175 boardWidth + 2*(lineGap + squareSize));
2176 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2179 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2180 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2184 * Determine what fonts to use.
2187 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2188 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2189 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2190 fontSet = CreateFontSet(appData.font);
2191 clockFontSet = CreateFontSet(appData.clockFont);
2193 /* For the coordFont, use the 0th font of the fontset. */
2194 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2195 XFontStruct **font_struct_list;
2196 XFontSetExtents *fontSize;
2197 char **font_name_list;
2198 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2199 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2200 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2201 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
2202 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
2205 appData.font = FindFont(appData.font, fontPxlSize);
2206 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2207 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2208 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2209 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2210 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2211 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2213 countFontID = coordFontID; // [HGM] holdings
2214 countFontStruct = coordFontStruct;
2216 xdb = XtDatabase(xDisplay);
2218 XrmPutLineResource(&xdb, "*international: True");
2219 vTo.size = sizeof(XFontSet);
2220 vTo.addr = (XtPointer) &fontSet;
2221 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2223 XrmPutStringResource(&xdb, "*font", appData.font);
2227 * Detect if there are not enough colors available and adapt.
2229 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2230 appData.monoMode = True;
2233 forceMono = MakeColors();
2236 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2238 appData.monoMode = True;
2241 if (appData.lowTimeWarning && !appData.monoMode) {
2242 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2243 vFrom.size = strlen(appData.lowTimeWarningColor);
2244 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2245 if (vTo.addr == NULL)
2246 appData.monoMode = True;
2248 lowTimeWarningColor = *(Pixel *) vTo.addr;
2251 if (appData.monoMode && appData.debugMode) {
2252 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2253 (unsigned long) XWhitePixel(xDisplay, xScreen),
2254 (unsigned long) XBlackPixel(xDisplay, xScreen));
2257 ParseIcsTextColors();
2258 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2259 textColors[ColorNone].attr = 0;
2261 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2267 layoutName = "tinyLayout";
2268 } else if (smallLayout) {
2269 layoutName = "smallLayout";
2271 layoutName = "normalLayout";
2273 /* Outer layoutWidget is there only to provide a name for use in
2274 resources that depend on the layout style */
2276 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2277 layoutArgs, XtNumber(layoutArgs));
2279 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2280 formArgs, XtNumber(formArgs));
2281 XtSetArg(args[0], XtNdefaultDistance, &sep);
2282 XtGetValues(formWidget, args, 1);
2285 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2286 XtSetArg(args[0], XtNtop, XtChainTop);
2287 XtSetArg(args[1], XtNbottom, XtChainTop);
2288 XtSetArg(args[2], XtNright, XtChainLeft);
2289 XtSetValues(menuBarWidget, args, 3);
2291 widgetList[j++] = whiteTimerWidget =
2292 XtCreateWidget("whiteTime", labelWidgetClass,
2293 formWidget, timerArgs, XtNumber(timerArgs));
2295 XtSetArg(args[0], XtNfontSet, clockFontSet);
2297 XtSetArg(args[0], XtNfont, clockFontStruct);
2299 XtSetArg(args[1], XtNtop, XtChainTop);
2300 XtSetArg(args[2], XtNbottom, XtChainTop);
2301 XtSetValues(whiteTimerWidget, args, 3);
2303 widgetList[j++] = blackTimerWidget =
2304 XtCreateWidget("blackTime", labelWidgetClass,
2305 formWidget, timerArgs, XtNumber(timerArgs));
2307 XtSetArg(args[0], XtNfontSet, clockFontSet);
2309 XtSetArg(args[0], XtNfont, clockFontStruct);
2311 XtSetArg(args[1], XtNtop, XtChainTop);
2312 XtSetArg(args[2], XtNbottom, XtChainTop);
2313 XtSetValues(blackTimerWidget, args, 3);
2315 if (appData.titleInWindow) {
2316 widgetList[j++] = titleWidget =
2317 XtCreateWidget("title", labelWidgetClass, formWidget,
2318 titleArgs, XtNumber(titleArgs));
2319 XtSetArg(args[0], XtNtop, XtChainTop);
2320 XtSetArg(args[1], XtNbottom, XtChainTop);
2321 XtSetValues(titleWidget, args, 2);
2324 if (appData.showButtonBar) {
2325 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2326 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2327 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2328 XtSetArg(args[2], XtNtop, XtChainTop);
2329 XtSetArg(args[3], XtNbottom, XtChainTop);
2330 XtSetValues(buttonBarWidget, args, 4);
2333 widgetList[j++] = messageWidget =
2334 XtCreateWidget("message", labelWidgetClass, formWidget,
2335 messageArgs, XtNumber(messageArgs));
2336 XtSetArg(args[0], XtNtop, XtChainTop);
2337 XtSetArg(args[1], XtNbottom, XtChainTop);
2338 XtSetValues(messageWidget, args, 2);
2340 widgetList[j++] = boardWidget =
2341 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2342 XtNumber(boardArgs));
2344 XtManageChildren(widgetList, j);
2346 timerWidth = (boardWidth - sep) / 2;
2347 XtSetArg(args[0], XtNwidth, timerWidth);
2348 XtSetValues(whiteTimerWidget, args, 1);
2349 XtSetValues(blackTimerWidget, args, 1);
2351 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2352 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2353 XtGetValues(whiteTimerWidget, args, 2);
2355 if (appData.showButtonBar) {
2356 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2357 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2358 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2362 * formWidget uses these constraints but they are stored
2366 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2367 XtSetValues(menuBarWidget, args, i);
2368 if (appData.titleInWindow) {
2371 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2372 XtSetValues(whiteTimerWidget, args, i);
2374 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2375 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2376 XtSetValues(blackTimerWidget, args, i);
2378 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2379 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2380 XtSetValues(titleWidget, args, i);
2382 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2383 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2384 XtSetValues(messageWidget, args, i);
2385 if (appData.showButtonBar) {
2387 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2388 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2389 XtSetValues(buttonBarWidget, args, i);
2393 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2394 XtSetValues(whiteTimerWidget, args, i);
2396 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2397 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2398 XtSetValues(blackTimerWidget, args, i);
2400 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2401 XtSetValues(titleWidget, args, i);
2403 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2404 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2405 XtSetValues(messageWidget, args, i);
2406 if (appData.showButtonBar) {
2408 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2409 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2410 XtSetValues(buttonBarWidget, args, i);
2415 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2416 XtSetValues(whiteTimerWidget, args, i);
2418 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2419 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2420 XtSetValues(blackTimerWidget, args, i);
2422 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2423 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2424 XtSetValues(messageWidget, args, i);
2425 if (appData.showButtonBar) {
2427 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2428 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2429 XtSetValues(buttonBarWidget, args, i);
2433 XtSetArg(args[0], XtNfromVert, messageWidget);
2434 XtSetArg(args[1], XtNtop, XtChainTop);
2435 XtSetArg(args[2], XtNbottom, XtChainBottom);
2436 XtSetArg(args[3], XtNleft, XtChainLeft);
2437 XtSetArg(args[4], XtNright, XtChainRight);
2438 XtSetValues(boardWidget, args, 5);
2440 XtRealizeWidget(shellWidget);
2443 XtSetArg(args[0], XtNx, wpMain.x);
2444 XtSetArg(args[1], XtNy, wpMain.y);
2445 XtSetValues(shellWidget, args, 2);
2449 * Correct the width of the message and title widgets.
2450 * It is not known why some systems need the extra fudge term.
2451 * The value "2" is probably larger than needed.
2453 XawFormDoLayout(formWidget, False);
2455 #define WIDTH_FUDGE 2
2457 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2458 XtSetArg(args[i], XtNheight, &h); i++;
2459 XtGetValues(messageWidget, args, i);
2460 if (appData.showButtonBar) {
2462 XtSetArg(args[i], XtNwidth, &w); i++;
2463 XtGetValues(buttonBarWidget, args, i);
2464 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2466 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
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 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2476 /* The size used for the child widget in layout lags one resize behind
2477 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2479 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2480 if (gres != XtGeometryYes && appData.debugMode) {
2481 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2482 programName, gres, w, h, wr, hr);
2485 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2486 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2487 XtSetArg(args[1], XtNright, XtChainRight);
2488 XtSetValues(messageWidget, args, 2);
2490 if (appData.titleInWindow) {
2492 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2493 XtSetArg(args[i], XtNheight, &h); i++;
2494 XtGetValues(titleWidget, args, i);
2496 w = boardWidth - 2*bor;
2498 XtSetArg(args[0], XtNwidth, &w);
2499 XtGetValues(menuBarWidget, args, 1);
2500 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2503 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2504 if (gres != XtGeometryYes && appData.debugMode) {
2506 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2507 programName, gres, w, h, wr, hr);
2510 XawFormDoLayout(formWidget, True);
2512 xBoardWindow = XtWindow(boardWidget);
2514 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2515 // not need to go into InitDrawingSizes().
2519 * Create X checkmark bitmap and initialize option menu checks.
2521 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2522 checkmark_bits, checkmark_width, checkmark_height);
2523 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2524 #ifndef OPTIONSDIALOG
2525 if (appData.alwaysPromoteToQueen) {
2526 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2529 if (appData.animateDragging) {
2530 XtSetValues(XtNameToWidget(menuBarWidget,
2531 "menuOptions.Animate Dragging"),
2534 if (appData.animate) {
2535 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2538 if (appData.autoCallFlag) {
2539 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2542 if (appData.autoFlipView) {
2543 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2546 if (appData.blindfold) {
2547 XtSetValues(XtNameToWidget(menuBarWidget,
2548 "menuOptions.Blindfold"), args, 1);
2550 if (appData.flashCount > 0) {
2551 XtSetValues(XtNameToWidget(menuBarWidget,
2552 "menuOptions.Flash Moves"),
2556 if (appData.highlightDragging) {
2557 XtSetValues(XtNameToWidget(menuBarWidget,
2558 "menuOptions.Highlight Dragging"),
2562 if (appData.highlightLastMove) {
2563 XtSetValues(XtNameToWidget(menuBarWidget,
2564 "menuOptions.Highlight Last Move"),
2567 if (appData.highlightMoveWithArrow) {
2568 XtSetValues(XtNameToWidget(menuBarWidget,
2569 "menuOptions.Arrow"),
2572 // if (appData.icsAlarm) {
2573 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2576 if (appData.ringBellAfterMoves) {
2577 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2580 if (appData.oneClick) {
2581 XtSetValues(XtNameToWidget(menuBarWidget,
2582 "menuOptions.OneClick"), args, 1);
2584 if (appData.periodicUpdates) {
2585 XtSetValues(XtNameToWidget(menuBarWidget,
2586 "menuOptions.Periodic Updates"), args, 1);
2588 if (appData.ponderNextMove) {
2589 XtSetValues(XtNameToWidget(menuBarWidget,
2590 "menuOptions.Ponder Next Move"), args, 1);
2592 if (appData.popupExitMessage) {
2593 XtSetValues(XtNameToWidget(menuBarWidget,
2594 "menuOptions.Popup Exit Message"), args, 1);
2596 if (appData.popupMoveErrors) {
2597 XtSetValues(XtNameToWidget(menuBarWidget,
2598 "menuOptions.Popup Move Errors"), args, 1);
2600 // if (appData.premove) {
2601 // XtSetValues(XtNameToWidget(menuBarWidget,
2602 // "menuOptions.Premove"), args, 1);
2604 if (appData.showCoords) {
2605 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2608 if (appData.hideThinkingFromHuman) {
2609 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2612 if (appData.testLegality) {
2613 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2617 if (saveSettingsOnExit) {
2618 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2625 ReadBitmap(&wIconPixmap, "icon_white.bm",
2626 icon_white_bits, icon_white_width, icon_white_height);
2627 ReadBitmap(&bIconPixmap, "icon_black.bm",
2628 icon_black_bits, icon_black_width, icon_black_height);
2629 iconPixmap = wIconPixmap;
2631 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2632 XtSetValues(shellWidget, args, i);
2635 * Create a cursor for the board widget.
2637 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2638 XChangeWindowAttributes(xDisplay, xBoardWindow,
2639 CWCursor, &window_attributes);
2642 * Inhibit shell resizing.
2644 shellArgs[0].value = (XtArgVal) &w;
2645 shellArgs[1].value = (XtArgVal) &h;
2646 XtGetValues(shellWidget, shellArgs, 2);
2647 shellArgs[4].value = shellArgs[2].value = w;
2648 shellArgs[5].value = shellArgs[3].value = h;
2649 XtSetValues(shellWidget, &shellArgs[2], 4);
2650 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2651 marginH = h - boardHeight;
2653 CatchDeleteWindow(shellWidget, "QuitProc");
2661 if (appData.animate || appData.animateDragging)
2664 XtAugmentTranslations(formWidget,
2665 XtParseTranslationTable(globalTranslations));
2666 XtAugmentTranslations(boardWidget,
2667 XtParseTranslationTable(boardTranslations));
2668 XtAugmentTranslations(whiteTimerWidget,
2669 XtParseTranslationTable(whiteTranslations));
2670 XtAugmentTranslations(blackTimerWidget,
2671 XtParseTranslationTable(blackTranslations));
2673 /* Why is the following needed on some versions of X instead
2674 * of a translation? */
2675 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2676 (XtEventHandler) EventProc, NULL);
2678 XtAddEventHandler(formWidget, KeyPressMask, False,
2679 (XtEventHandler) MoveTypeInProc, NULL);
2680 XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2681 (XtEventHandler) EventProc, NULL);
2683 /* [AS] Restore layout */
2684 if( wpMoveHistory.visible ) {
2688 if( wpEvalGraph.visible )
2693 if( wpEngineOutput.visible ) {
2694 EngineOutputPopUp();
2699 if (errorExitStatus == -1) {
2700 if (appData.icsActive) {
2701 /* We now wait until we see "login:" from the ICS before
2702 sending the logon script (problems with timestamp otherwise) */
2703 /*ICSInitScript();*/
2704 if (appData.icsInputBox) ICSInputBoxPopUp();
2708 signal(SIGWINCH, TermSizeSigHandler);
2710 signal(SIGINT, IntSigHandler);
2711 signal(SIGTERM, IntSigHandler);
2712 if (*appData.cmailGameName != NULLCHAR) {
2713 signal(SIGUSR1, CmailSigHandler);
2716 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2718 // XtSetKeyboardFocus(shellWidget, formWidget);
2719 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2721 XtAppMainLoop(appContext);
2722 if (appData.debugMode) fclose(debugFP); // [DM] debug
2726 static Boolean noEcho;
2731 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2732 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2734 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2735 unlink(gameCopyFilename);
2736 unlink(gamePasteFilename);
2737 if(noEcho) EchoOn();
2741 TermSizeSigHandler (int sig)
2747 IntSigHandler (int sig)
2753 CmailSigHandler (int sig)
2758 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2760 /* Activate call-back function CmailSigHandlerCallBack() */
2761 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2763 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2767 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2770 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2772 /**** end signal code ****/
2778 /* try to open the icsLogon script, either in the location given
2779 * or in the users HOME directory
2786 f = fopen(appData.icsLogon, "r");
2789 homedir = getenv("HOME");
2790 if (homedir != NULL)
2792 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2793 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2794 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2795 f = fopen(buf, "r");
2800 ProcessICSInitScript(f);
2802 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2821 GreyRevert (Boolean grey)
2824 if (!menuBarWidget) return;
2825 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2827 DisplayError("menuEdit.Revert", 0);
2829 XtSetSensitive(w, !grey);
2831 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2833 DisplayError("menuEdit.Annotate", 0);
2835 XtSetSensitive(w, !grey);
2840 SetMenuEnables (Enables *enab)
2843 if (!menuBarWidget) return;
2844 while (enab->name != NULL) {
2845 w = XtNameToWidget(menuBarWidget, enab->name);
2847 DisplayError(enab->name, 0);
2849 XtSetSensitive(w, enab->value);
2855 Enables icsEnables[] = {
2856 { "menuFile.Mail Move", False },
2857 { "menuFile.Reload CMail Message", False },
2858 { "menuMode.Machine Black", False },
2859 { "menuMode.Machine White", False },
2860 { "menuMode.Analysis Mode", False },
2861 { "menuMode.Analyze File", False },
2862 { "menuMode.Two Machines", False },
2863 { "menuMode.Machine Match", False },
2865 { "menuEngine.Hint", False },
2866 { "menuEngine.Book", False },
2867 { "menuEngine.Move Now", False },
2868 #ifndef OPTIONSDIALOG
2869 { "menuOptions.Periodic Updates", False },
2870 { "menuOptions.Hide Thinking", False },
2871 { "menuOptions.Ponder Next Move", False },
2874 { "menuEngine.Engine #1 Settings", False },
2875 { "menuEngine.Engine #2 Settings", False },
2876 { "menuEngine.Load Engine", False },
2877 { "menuEdit.Annotate", False },
2878 { "menuOptions.Match", False },
2882 Enables ncpEnables[] = {
2883 { "menuFile.Mail Move", False },
2884 { "menuFile.Reload CMail Message", False },
2885 { "menuMode.Machine White", False },
2886 { "menuMode.Machine Black", False },
2887 { "menuMode.Analysis Mode", False },
2888 { "menuMode.Analyze File", False },
2889 { "menuMode.Two Machines", False },
2890 { "menuMode.Machine Match", False },
2891 { "menuMode.ICS Client", False },
2892 { "menuView.ICStex", False },
2893 { "menuView.ICS Input Box", False },
2894 { "Action", False },
2895 { "menuEdit.Revert", False },
2896 { "menuEdit.Annotate", False },
2897 { "menuEngine.Engine #1 Settings", False },
2898 { "menuEngine.Engine #2 Settings", False },
2899 { "menuEngine.Move Now", False },
2900 { "menuEngine.Retract Move", False },
2901 { "menuOptions.ICS", False },
2902 #ifndef OPTIONSDIALOG
2903 { "menuOptions.Auto Flag", False },
2904 { "menuOptions.Auto Flip View", False },
2905 // { "menuOptions.ICS Alarm", False },
2906 { "menuOptions.Move Sound", False },
2907 { "menuOptions.Hide Thinking", False },
2908 { "menuOptions.Periodic Updates", False },
2909 { "menuOptions.Ponder Next Move", False },
2911 { "menuEngine.Hint", False },
2912 { "menuEngine.Book", False },
2916 Enables gnuEnables[] = {
2917 { "menuMode.ICS Client", False },
2918 { "menuView.ICStex", False },
2919 { "menuView.ICS Input Box", False },
2920 { "menuAction.Accept", False },
2921 { "menuAction.Decline", False },
2922 { "menuAction.Rematch", False },
2923 { "menuAction.Adjourn", False },
2924 { "menuAction.Stop Examining", False },
2925 { "menuAction.Stop Observing", False },
2926 { "menuAction.Upload to Examine", False },
2927 { "menuEdit.Revert", False },
2928 { "menuEdit.Annotate", False },
2929 { "menuOptions.ICS", False },
2931 /* The next two options rely on SetCmailMode being called *after* */
2932 /* SetGNUMode so that when GNU is being used to give hints these */
2933 /* menu options are still available */
2935 { "menuFile.Mail Move", False },
2936 { "menuFile.Reload CMail Message", False },
2937 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2938 { "menuMode.Machine White", True },
2939 { "menuMode.Machine Black", True },
2940 { "menuMode.Analysis Mode", True },
2941 { "menuMode.Analyze File", True },
2942 { "menuMode.Two Machines", True },
2943 { "menuMode.Machine Match", True },
2944 { "menuEngine.Engine #1 Settings", True },
2945 { "menuEngine.Engine #2 Settings", True },
2946 { "menuEngine.Hint", True },
2947 { "menuEngine.Book", True },
2948 { "menuEngine.Move Now", True },
2949 { "menuEngine.Retract Move", True },
2954 Enables cmailEnables[] = {
2956 { "menuAction.Call Flag", False },
2957 { "menuAction.Draw", True },
2958 { "menuAction.Adjourn", False },
2959 { "menuAction.Abort", False },
2960 { "menuAction.Stop Observing", False },
2961 { "menuAction.Stop Examining", False },
2962 { "menuFile.Mail Move", True },
2963 { "menuFile.Reload CMail Message", True },
2967 Enables trainingOnEnables[] = {
2968 { "menuMode.Edit Comment", False },
2969 { "menuMode.Pause", False },
2970 { "menuEdit.Forward", False },
2971 { "menuEdit.Backward", False },
2972 { "menuEdit.Forward to End", False },
2973 { "menuEdit.Back to Start", False },
2974 { "menuEngine.Move Now", False },
2975 { "menuEdit.Truncate Game", False },
2979 Enables trainingOffEnables[] = {
2980 { "menuMode.Edit Comment", True },
2981 { "menuMode.Pause", True },
2982 { "menuEdit.Forward", True },
2983 { "menuEdit.Backward", True },
2984 { "menuEdit.Forward to End", True },
2985 { "menuEdit.Back to Start", True },
2986 { "menuEngine.Move Now", True },
2987 { "menuEdit.Truncate Game", True },
2991 Enables machineThinkingEnables[] = {
2992 { "menuFile.Load Game", False },
2993 // { "menuFile.Load Next Game", False },
2994 // { "menuFile.Load Previous Game", False },
2995 // { "menuFile.Reload Same Game", False },
2996 { "menuEdit.Paste Game", False },
2997 { "menuFile.Load Position", False },
2998 // { "menuFile.Load Next Position", False },
2999 // { "menuFile.Load Previous Position", False },
3000 // { "menuFile.Reload Same Position", False },
3001 { "menuEdit.Paste Position", False },
3002 { "menuMode.Machine White", False },
3003 { "menuMode.Machine Black", False },
3004 { "menuMode.Two Machines", False },
3005 // { "menuMode.Machine Match", False },
3006 { "menuEngine.Retract Move", False },
3010 Enables userThinkingEnables[] = {
3011 { "menuFile.Load Game", True },
3012 // { "menuFile.Load Next Game", True },
3013 // { "menuFile.Load Previous Game", True },
3014 // { "menuFile.Reload Same Game", True },
3015 { "menuEdit.Paste Game", True },
3016 { "menuFile.Load Position", True },
3017 // { "menuFile.Load Next Position", True },
3018 // { "menuFile.Load Previous Position", True },
3019 // { "menuFile.Reload Same Position", True },
3020 { "menuEdit.Paste Position", True },
3021 { "menuMode.Machine White", True },
3022 { "menuMode.Machine Black", True },
3023 { "menuMode.Two Machines", True },
3024 // { "menuMode.Machine Match", True },
3025 { "menuEngine.Retract Move", True },
3032 SetMenuEnables(icsEnables);
3035 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3036 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3037 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3045 SetMenuEnables(ncpEnables);
3051 SetMenuEnables(gnuEnables);
3057 SetMenuEnables(cmailEnables);
3061 SetTrainingModeOn ()
3063 SetMenuEnables(trainingOnEnables);
3064 if (appData.showButtonBar) {
3065 XtSetSensitive(buttonBarWidget, False);
3071 SetTrainingModeOff ()
3073 SetMenuEnables(trainingOffEnables);
3074 if (appData.showButtonBar) {
3075 XtSetSensitive(buttonBarWidget, True);
3080 SetUserThinkingEnables ()
3082 if (appData.noChessProgram) return;
3083 SetMenuEnables(userThinkingEnables);
3087 SetMachineThinkingEnables ()
3089 if (appData.noChessProgram) return;
3090 SetMenuEnables(machineThinkingEnables);
3092 case MachinePlaysBlack:
3093 case MachinePlaysWhite:
3094 case TwoMachinesPlay:
3095 XtSetSensitive(XtNameToWidget(menuBarWidget,
3096 ModeToWidgetName(gameMode)), True);
3103 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3104 #define HISTORY_SIZE 64
3105 static char *history[HISTORY_SIZE];
3106 int histIn = 0, histP = 0;
3109 SaveInHistory (char *cmd)
3111 if (history[histIn] != NULL) {
3112 free(history[histIn]);
3113 history[histIn] = NULL;
3115 if (*cmd == NULLCHAR) return;
3116 history[histIn] = StrSave(cmd);
3117 histIn = (histIn + 1) % HISTORY_SIZE;
3118 if (history[histIn] != NULL) {
3119 free(history[histIn]);
3120 history[histIn] = NULL;
3126 PrevInHistory (char *cmd)
3129 if (histP == histIn) {
3130 if (history[histIn] != NULL) free(history[histIn]);
3131 history[histIn] = StrSave(cmd);
3133 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3134 if (newhp == histIn || history[newhp] == NULL) return NULL;
3136 return history[histP];
3142 if (histP == histIn) return NULL;
3143 histP = (histP + 1) % HISTORY_SIZE;
3144 return history[histP];
3146 // end of borrowed code
3148 #define Abs(n) ((n)<0 ? -(n) : (n))
3152 InsertPxlSize (char *pattern, int targetPxlSize)
3154 char *base_fnt_lst, strInt[12], *p, *q;
3155 int alternatives, i, len, strIntLen;
3158 * Replace the "*" (if present) in the pixel-size slot of each
3159 * alternative with the targetPxlSize.
3163 while ((p = strchr(p, ',')) != NULL) {
3167 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3168 strIntLen = strlen(strInt);
3169 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3173 while (alternatives--) {
3174 char *comma = strchr(p, ',');
3175 for (i=0; i<14; i++) {
3176 char *hyphen = strchr(p, '-');
3178 if (comma && hyphen > comma) break;
3179 len = hyphen + 1 - p;
3180 if (i == 7 && *p == '*' && len == 2) {
3182 memcpy(q, strInt, strIntLen);
3192 len = comma + 1 - p;
3199 return base_fnt_lst;
3203 CreateFontSet (char *base_fnt_lst)
3206 char **missing_list;
3210 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3211 &missing_list, &missing_count, &def_string);
3212 if (appData.debugMode) {
3214 XFontStruct **font_struct_list;
3215 char **font_name_list;
3216 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3218 fprintf(debugFP, " got list %s, locale %s\n",
3219 XBaseFontNameListOfFontSet(fntSet),
3220 XLocaleOfFontSet(fntSet));
3221 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3222 for (i = 0; i < count; i++) {
3223 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3226 for (i = 0; i < missing_count; i++) {
3227 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3230 if (fntSet == NULL) {
3231 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3236 #else // not ENABLE_NLS
3238 * Find a font that matches "pattern" that is as close as
3239 * possible to the targetPxlSize. Prefer fonts that are k
3240 * pixels smaller to fonts that are k pixels larger. The
3241 * pattern must be in the X Consortium standard format,
3242 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3243 * The return value should be freed with XtFree when no
3247 FindFont (char *pattern, int targetPxlSize)
3249 char **fonts, *p, *best, *scalable, *scalableTail;
3250 int i, j, nfonts, minerr, err, pxlSize;
3252 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3254 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3255 programName, pattern);
3262 for (i=0; i<nfonts; i++) {
3265 if (*p != '-') continue;
3267 if (*p == NULLCHAR) break;
3268 if (*p++ == '-') j++;
3270 if (j < 7) continue;
3273 scalable = fonts[i];
3276 err = pxlSize - targetPxlSize;
3277 if (Abs(err) < Abs(minerr) ||
3278 (minerr > 0 && err < 0 && -err == minerr)) {
3284 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3285 /* If the error is too big and there is a scalable font,
3286 use the scalable font. */
3287 int headlen = scalableTail - scalable;
3288 p = (char *) XtMalloc(strlen(scalable) + 10);
3289 while (isdigit(*scalableTail)) scalableTail++;
3290 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3292 p = (char *) XtMalloc(strlen(best) + 2);
3293 safeStrCpy(p, best, strlen(best)+1 );
3295 if (appData.debugMode) {
3296 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3297 pattern, targetPxlSize, p);
3299 XFreeFontNames(fonts);
3306 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3307 // must be called before all non-first callse to CreateGCs()
3308 XtReleaseGC(shellWidget, highlineGC);
3309 XtReleaseGC(shellWidget, lightSquareGC);
3310 XtReleaseGC(shellWidget, darkSquareGC);
3311 XtReleaseGC(shellWidget, lineGC);
3312 if (appData.monoMode) {
3313 if (DefaultDepth(xDisplay, xScreen) == 1) {
3314 XtReleaseGC(shellWidget, wbPieceGC);
3316 XtReleaseGC(shellWidget, bwPieceGC);
3319 XtReleaseGC(shellWidget, prelineGC);
3320 XtReleaseGC(shellWidget, jailSquareGC);
3321 XtReleaseGC(shellWidget, wdPieceGC);
3322 XtReleaseGC(shellWidget, wlPieceGC);
3323 XtReleaseGC(shellWidget, wjPieceGC);
3324 XtReleaseGC(shellWidget, bdPieceGC);
3325 XtReleaseGC(shellWidget, blPieceGC);
3326 XtReleaseGC(shellWidget, bjPieceGC);
3331 CreateGCs (int redo)
3333 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3334 | GCBackground | GCFunction | GCPlaneMask;
3335 XGCValues gc_values;
3338 gc_values.plane_mask = AllPlanes;
3339 gc_values.line_width = lineGap;
3340 gc_values.line_style = LineSolid;
3341 gc_values.function = GXcopy;
3344 DeleteGCs(); // called a second time; clean up old GCs first
3345 } else { // [HGM] grid and font GCs created on first call only
3346 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3347 gc_values.background = XWhitePixel(xDisplay, xScreen);
3348 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3349 XSetFont(xDisplay, coordGC, coordFontID);
3351 // [HGM] make font for holdings counts (white on black)
3352 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3353 gc_values.background = XBlackPixel(xDisplay, xScreen);
3354 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3355 XSetFont(xDisplay, countGC, countFontID);
3357 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3358 gc_values.background = XBlackPixel(xDisplay, xScreen);
3359 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3361 if (appData.monoMode) {
3362 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3363 gc_values.background = XWhitePixel(xDisplay, xScreen);
3364 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3366 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3367 gc_values.background = XBlackPixel(xDisplay, xScreen);
3368 lightSquareGC = wbPieceGC
3369 = XtGetGC(shellWidget, value_mask, &gc_values);
3371 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3372 gc_values.background = XWhitePixel(xDisplay, xScreen);
3373 darkSquareGC = bwPieceGC
3374 = XtGetGC(shellWidget, value_mask, &gc_values);
3376 if (DefaultDepth(xDisplay, xScreen) == 1) {
3377 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3378 gc_values.function = GXcopyInverted;
3379 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3380 gc_values.function = GXcopy;
3381 if (XBlackPixel(xDisplay, xScreen) == 1) {
3382 bwPieceGC = darkSquareGC;
3383 wbPieceGC = copyInvertedGC;
3385 bwPieceGC = copyInvertedGC;
3386 wbPieceGC = lightSquareGC;
3390 gc_values.foreground = highlightSquareColor;
3391 gc_values.background = highlightSquareColor;
3392 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3394 gc_values.foreground = premoveHighlightColor;
3395 gc_values.background = premoveHighlightColor;
3396 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3398 gc_values.foreground = lightSquareColor;
3399 gc_values.background = darkSquareColor;
3400 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3402 gc_values.foreground = darkSquareColor;
3403 gc_values.background = lightSquareColor;
3404 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3406 gc_values.foreground = jailSquareColor;
3407 gc_values.background = jailSquareColor;
3408 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3410 gc_values.foreground = whitePieceColor;
3411 gc_values.background = darkSquareColor;
3412 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3414 gc_values.foreground = whitePieceColor;
3415 gc_values.background = lightSquareColor;
3416 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3418 gc_values.foreground = whitePieceColor;
3419 gc_values.background = jailSquareColor;
3420 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3422 gc_values.foreground = blackPieceColor;
3423 gc_values.background = darkSquareColor;
3424 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3426 gc_values.foreground = blackPieceColor;
3427 gc_values.background = lightSquareColor;
3428 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3430 gc_values.foreground = blackPieceColor;
3431 gc_values.background = jailSquareColor;
3432 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3437 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3445 fp = fopen(filename, "rb");
3447 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3454 for (y=0; y<h; ++y) {
3455 for (x=0; x<h; ++x) {
3460 XPutPixel(xim, x, y, blackPieceColor);
3462 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3465 XPutPixel(xim, x, y, darkSquareColor);
3467 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3470 XPutPixel(xim, x, y, whitePieceColor);
3472 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3475 XPutPixel(xim, x, y, lightSquareColor);
3477 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3485 /* create Pixmap of piece */
3486 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3488 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3491 /* create Pixmap of clipmask
3492 Note: We assume the white/black pieces have the same
3493 outline, so we make only 6 masks. This is okay
3494 since the XPM clipmask routines do the same. */
3496 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3498 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3501 /* now create the 1-bit version */
3502 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3505 values.foreground = 1;
3506 values.background = 0;
3508 /* Don't use XtGetGC, not read only */
3509 maskGC = XCreateGC(xDisplay, *mask,
3510 GCForeground | GCBackground, &values);
3511 XCopyPlane(xDisplay, temp, *mask, maskGC,
3512 0, 0, squareSize, squareSize, 0, 0, 1);
3513 XFreePixmap(xDisplay, temp);
3518 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3526 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3531 /* The XSynchronize calls were copied from CreatePieces.
3532 Not sure if needed, but can't hurt */
3533 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3536 /* temp needed by loadXIM() */
3537 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3538 0, 0, ss, ss, AllPlanes, XYPixmap);
3540 if (strlen(appData.pixmapDirectory) == 0) {
3544 if (appData.monoMode) {
3545 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3549 fprintf(stderr, _("\nLoading XIMs...\n"));
3551 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3552 fprintf(stderr, "%d", piece+1);
3553 for (kind=0; kind<4; kind++) {
3554 fprintf(stderr, ".");
3555 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3556 ExpandPathName(appData.pixmapDirectory),
3557 piece <= (int) WhiteKing ? "" : "w",
3558 pieceBitmapNames[piece],
3560 ximPieceBitmap[kind][piece] =
3561 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3562 0, 0, ss, ss, AllPlanes, XYPixmap);
3563 if (appData.debugMode)
3564 fprintf(stderr, _("(File:%s:) "), buf);
3565 loadXIM(ximPieceBitmap[kind][piece],
3567 &(xpmPieceBitmap2[kind][piece]),
3568 &(ximMaskPm2[piece]));
3569 if(piece <= (int)WhiteKing)
3570 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3572 fprintf(stderr," ");
3574 /* Load light and dark squares */
3575 /* If the LSQ and DSQ pieces don't exist, we will
3576 draw them with solid squares. */
3577 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3578 if (access(buf, 0) != 0) {
3582 fprintf(stderr, _("light square "));
3584 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3585 0, 0, ss, ss, AllPlanes, XYPixmap);
3586 if (appData.debugMode)
3587 fprintf(stderr, _("(File:%s:) "), buf);
3589 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3590 fprintf(stderr, _("dark square "));
3591 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3592 ExpandPathName(appData.pixmapDirectory), ss);
3593 if (appData.debugMode)
3594 fprintf(stderr, _("(File:%s:) "), buf);
3596 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3597 0, 0, ss, ss, AllPlanes, XYPixmap);
3598 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3599 xpmJailSquare = xpmLightSquare;
3601 fprintf(stderr, _("Done.\n"));
3603 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3606 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3610 CreateXPMBoard (char *s, int kind)
3614 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3615 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3616 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3622 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3623 // thisroutine has to be called t free the old piece pixmaps
3625 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3626 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3628 XFreePixmap(xDisplay, xpmLightSquare);
3629 XFreePixmap(xDisplay, xpmDarkSquare);
3638 u_int ss = squareSize;
3640 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3641 XpmColorSymbol symbols[4];
3642 static int redo = False;
3644 if(redo) FreeXPMPieces(); else redo = 1;
3646 /* The XSynchronize calls were copied from CreatePieces.
3647 Not sure if needed, but can't hurt */
3648 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3650 /* Setup translations so piece colors match square colors */
3651 symbols[0].name = "light_piece";
3652 symbols[0].value = appData.whitePieceColor;
3653 symbols[1].name = "dark_piece";
3654 symbols[1].value = appData.blackPieceColor;
3655 symbols[2].name = "light_square";
3656 symbols[2].value = appData.lightSquareColor;
3657 symbols[3].name = "dark_square";
3658 symbols[3].value = appData.darkSquareColor;
3660 attr.valuemask = XpmColorSymbols;
3661 attr.colorsymbols = symbols;
3662 attr.numsymbols = 4;
3664 if (appData.monoMode) {
3665 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3669 if (strlen(appData.pixmapDirectory) == 0) {
3670 XpmPieces* pieces = builtInXpms;
3673 while (pieces->size != squareSize && pieces->size) pieces++;
3674 if (!pieces->size) {
3675 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3678 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3679 for (kind=0; kind<4; kind++) {
3681 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3682 pieces->xpm[piece][kind],
3683 &(xpmPieceBitmap2[kind][piece]),
3684 NULL, &attr)) != 0) {
3685 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3689 if(piece <= (int) WhiteKing)
3690 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3694 xpmJailSquare = xpmLightSquare;
3698 fprintf(stderr, _("\nLoading XPMs...\n"));
3701 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3702 fprintf(stderr, "%d ", piece+1);
3703 for (kind=0; kind<4; kind++) {
3704 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3705 ExpandPathName(appData.pixmapDirectory),
3706 piece > (int) WhiteKing ? "w" : "",
3707 pieceBitmapNames[piece],
3709 if (appData.debugMode) {
3710 fprintf(stderr, _("(File:%s:) "), buf);
3712 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3713 &(xpmPieceBitmap2[kind][piece]),
3714 NULL, &attr)) != 0) {
3715 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3716 // [HGM] missing: read of unorthodox piece failed; substitute King.
3717 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3718 ExpandPathName(appData.pixmapDirectory),
3720 if (appData.debugMode) {
3721 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3723 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3724 &(xpmPieceBitmap2[kind][piece]),
3728 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3733 if(piece <= (int) WhiteKing)
3734 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3737 /* Load light and dark squares */
3738 /* If the LSQ and DSQ pieces don't exist, we will
3739 draw them with solid squares. */
3740 fprintf(stderr, _("light square "));
3741 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3742 if (access(buf, 0) != 0) {
3746 if (appData.debugMode)
3747 fprintf(stderr, _("(File:%s:) "), buf);
3749 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3750 &xpmLightSquare, NULL, &attr)) != 0) {
3751 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3754 fprintf(stderr, _("dark square "));
3755 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3756 ExpandPathName(appData.pixmapDirectory), ss);
3757 if (appData.debugMode) {
3758 fprintf(stderr, _("(File:%s:) "), buf);
3760 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3761 &xpmDarkSquare, NULL, &attr)) != 0) {
3762 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3766 xpmJailSquare = xpmLightSquare;
3767 fprintf(stderr, _("Done.\n"));
3769 oldVariant = -1; // kludge to force re-makig of animation masks
3770 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3773 #endif /* HAVE_LIBXPM */
3776 /* No built-in bitmaps */
3781 u_int ss = squareSize;
3783 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3786 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3787 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3788 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3789 pieceBitmapNames[piece],
3790 ss, kind == SOLID ? 's' : 'o');
3791 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3792 if(piece <= (int)WhiteKing)
3793 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3797 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3801 /* With built-in bitmaps */
3805 BuiltInBits* bib = builtInBits;
3808 u_int ss = squareSize;
3810 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3813 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3815 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3816 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3817 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3818 pieceBitmapNames[piece],
3819 ss, kind == SOLID ? 's' : 'o');
3820 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3821 bib->bits[kind][piece], ss, ss);
3822 if(piece <= (int)WhiteKing)
3823 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3827 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3833 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3838 char msg[MSG_SIZ], fullname[MSG_SIZ];
3840 if (*appData.bitmapDirectory != NULLCHAR) {
3841 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3842 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3843 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3844 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3845 &w, &h, pm, &x_hot, &y_hot);
3846 fprintf(stderr, "load %s\n", name);
3847 if (errcode != BitmapSuccess) {
3849 case BitmapOpenFailed:
3850 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3852 case BitmapFileInvalid:
3853 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3855 case BitmapNoMemory:
3856 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3860 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3864 fprintf(stderr, _("%s: %s...using built-in\n"),
3866 } else if (w != wreq || h != hreq) {
3868 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3869 programName, fullname, w, h, wreq, hreq);
3875 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3885 if (lineGap == 0) return;
3887 /* [HR] Split this into 2 loops for non-square boards. */
3889 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3890 gridSegments[i].x1 = 0;
3891 gridSegments[i].x2 =
3892 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3893 gridSegments[i].y1 = gridSegments[i].y2
3894 = lineGap / 2 + (i * (squareSize + lineGap));
3897 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3898 gridSegments[j + i].y1 = 0;
3899 gridSegments[j + i].y2 =
3900 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3901 gridSegments[j + i].x1 = gridSegments[j + i].x2
3902 = lineGap / 2 + (j * (squareSize + lineGap));
3907 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3909 XtActionProc proc = (XtActionProc) addr;
3911 (proc)(NULL, NULL, NULL, NULL);
3915 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3917 RecentEngineEvent((int) (intptr_t) addr);
3921 AppendEnginesToMenu (Widget menu, char *list)
3929 if(appData.icsActive || appData.recentEngines <= 0) return;
3930 recentEngines = strdup(list);
3932 XtSetArg(args[j], XtNleftMargin, 20); j++;
3933 XtSetArg(args[j], XtNrightMargin, 20); j++;
3935 p = strchr(list, '\n'); if(p == NULL) break;
3936 if(i == 0) XtCreateManagedWidget(_("----"), smeLineObjectClass, menu, args, j); // at least one valid item to add
3938 XtSetArg(args[j], XtNlabel, XtNewString(list));
3939 entry = XtCreateManagedWidget("engine", smeBSBObjectClass, menu, args, j+1);
3940 XtAddCallback(entry, XtNcallback,
3941 (XtCallbackProc) MenuEngineSelect,
3942 (caddr_t) (intptr_t) i);
3943 i++; *p = '\n'; list = p + 1;
3948 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3955 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3958 XtSetArg(args[j], XtNleftMargin, 20); j++;
3959 XtSetArg(args[j], XtNrightMargin, 20); j++;
3961 while (mi->string != NULL) {
3962 if (strcmp(mi->string, "----") == 0) {
3963 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3966 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3967 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3969 XtAddCallback(entry, XtNcallback,
3970 (XtCallbackProc) MenuBarSelect,
3971 (caddr_t) mi->proc);
3975 if(!strcmp(mb->name, "Engine")) AppendEnginesToMenu(menu, appData.recentEngineList);
3979 CreateMenuBar (Menu *mb, int boardWidth)
3981 int i, j, nr = 0, wtot = 0, widths[10];
3984 char menuName[MSG_SIZ];
3989 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3990 XtSetArg(args[j], XtNvSpace, 0); j++;
3991 XtSetArg(args[j], XtNborderWidth, 0); j++;
3992 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3993 formWidget, args, j);
3995 while (mb->name != NULL) {
3996 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3997 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3999 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
4000 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
4001 XtSetArg(args[j], XtNborderWidth, 0); j++;
4002 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
4004 CreateMenuBarPopup(menuBar, menuName, mb);
4006 XtSetArg(args[j], XtNwidth, &w); j++;
4007 XtGetValues(mb->subMenu, args, j);
4008 wtot += mb->textWidth = widths[nr++] = w;
4011 while(wtot > boardWidth - 40) {
4013 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
4017 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
4019 XtSetArg(args[j], XtNwidth, widths[i]); j++;
4020 XtSetValues(ma[i].subMenu, args, j);
4026 CreateButtonBar (MenuItem *mi)
4029 Widget button, buttonBar;
4033 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4035 XtSetArg(args[j], XtNhSpace, 0); j++;
4037 XtSetArg(args[j], XtNborderWidth, 0); j++;
4038 XtSetArg(args[j], XtNvSpace, 0); j++;
4039 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4040 formWidget, args, j);
4042 while (mi->string != NULL) {
4045 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4046 XtSetArg(args[j], XtNborderWidth, 0); j++;
4048 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4049 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4050 buttonBar, args, j);
4051 XtAddCallback(button, XtNcallback,
4052 (XtCallbackProc) MenuBarSelect,
4053 (caddr_t) mi->proc);
4060 CreatePieceMenu (char *name, int color)
4065 ChessSquare selection;
4067 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4068 boardWidget, args, 0);
4070 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4071 String item = pieceMenuStrings[color][i];
4073 if (strcmp(item, "----") == 0) {
4074 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4077 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4078 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4080 selection = pieceMenuTranslation[color][i];
4081 XtAddCallback(entry, XtNcallback,
4082 (XtCallbackProc) PieceMenuSelect,
4083 (caddr_t) selection);
4084 if (selection == WhitePawn || selection == BlackPawn) {
4085 XtSetArg(args[0], XtNpopupOnEntry, entry);
4086 XtSetValues(menu, args, 1);
4099 ChessSquare selection;
4101 whitePieceMenu = CreatePieceMenu("menuW", 0);
4102 blackPieceMenu = CreatePieceMenu("menuB", 1);
4104 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
4105 XtRegisterGrabAction(PieceMenuPopup, True,
4106 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4107 GrabModeAsync, GrabModeAsync);
4109 XtSetArg(args[0], XtNlabel, _("Drop"));
4110 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4111 boardWidget, args, 1);
4112 for (i = 0; i < DROP_MENU_SIZE; i++) {
4113 String item = dropMenuStrings[i];
4115 if (strcmp(item, "----") == 0) {
4116 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4119 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4120 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4122 selection = dropMenuTranslation[i];
4123 XtAddCallback(entry, XtNcallback,
4124 (XtCallbackProc) DropMenuSelect,
4125 (caddr_t) selection);
4139 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4140 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4141 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4142 dmEnables[i].piece);
4143 XtSetSensitive(entry, p != NULL || !appData.testLegality
4144 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4145 && !appData.icsActive));
4147 while (p && *p++ == dmEnables[i].piece) count++;
4148 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4150 XtSetArg(args[j], XtNlabel, label); j++;
4151 XtSetValues(entry, args, j);
4156 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4158 String whichMenu; int menuNr = -2;
4159 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4160 if (event->type == ButtonRelease)
4161 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4162 else if (event->type == ButtonPress)
4163 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4165 case 0: whichMenu = params[0]; break;
4166 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4168 case -1: if (errorUp) ErrorPopDown();
4171 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4175 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4177 if (pmFromX < 0 || pmFromY < 0) return;
4178 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4182 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4184 if (pmFromX < 0 || pmFromY < 0) return;
4185 DropMenuEvent(piece, pmFromX, pmFromY);
4189 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4191 shiftKey = prms[0][0] & 1;
4196 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4198 shiftKey = prms[0][0] & 1;
4204 * If the user selects on a border boundary, return -1; if off the board,
4205 * return -2. Otherwise map the event coordinate to the square.
4208 EventToSquare (int x, int limit)
4215 if ((x % (squareSize + lineGap)) >= squareSize)
4217 x /= (squareSize + lineGap);
4224 do_flash_delay (unsigned long msec)
4230 drawHighlight (int file, int rank, GC gc)
4234 if (lineGap == 0) return;
4237 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4238 (squareSize + lineGap);
4239 y = lineGap/2 + rank * (squareSize + lineGap);
4241 x = lineGap/2 + file * (squareSize + lineGap);
4242 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4243 (squareSize + lineGap);
4246 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4247 squareSize+lineGap, squareSize+lineGap);
4250 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4251 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4254 SetHighlights (int fromX, int fromY, int toX, int toY)
4256 if (hi1X != fromX || hi1Y != fromY) {
4257 if (hi1X >= 0 && hi1Y >= 0) {
4258 drawHighlight(hi1X, hi1Y, lineGC);
4260 } // [HGM] first erase both, then draw new!
4261 if (hi2X != toX || hi2Y != toY) {
4262 if (hi2X >= 0 && hi2Y >= 0) {
4263 drawHighlight(hi2X, hi2Y, lineGC);
4266 if (hi1X != fromX || hi1Y != fromY) {
4267 if (fromX >= 0 && fromY >= 0) {
4268 drawHighlight(fromX, fromY, highlineGC);
4271 if (hi2X != toX || hi2Y != toY) {
4272 if (toX >= 0 && toY >= 0) {
4273 drawHighlight(toX, toY, highlineGC);
4276 if(toX<0) // clearing the highlights must have damaged arrow
4277 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4287 SetHighlights(-1, -1, -1, -1);
4292 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4294 if (pm1X != fromX || pm1Y != fromY) {
4295 if (pm1X >= 0 && pm1Y >= 0) {
4296 drawHighlight(pm1X, pm1Y, lineGC);
4298 if (fromX >= 0 && fromY >= 0) {
4299 drawHighlight(fromX, fromY, prelineGC);
4302 if (pm2X != toX || pm2Y != toY) {
4303 if (pm2X >= 0 && pm2Y >= 0) {
4304 drawHighlight(pm2X, pm2Y, lineGC);
4306 if (toX >= 0 && toY >= 0) {
4307 drawHighlight(toX, toY, prelineGC);
4317 ClearPremoveHighlights ()
4319 SetPremoveHighlights(-1, -1, -1, -1);
4323 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4325 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4326 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4328 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4329 if(textureW[kind] < W*squareSize)
4330 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4332 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4333 if(textureH[kind] < H*squareSize)
4334 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4336 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4341 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4342 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4344 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4345 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4346 squareSize, squareSize, x*fac, y*fac);
4348 if (useImages && useImageSqs) {
4352 pm = xpmLightSquare;
4357 case 2: /* neutral */
4362 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4363 squareSize, squareSize, x*fac, y*fac);
4373 case 2: /* neutral */
4378 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4383 I split out the routines to draw a piece so that I could
4384 make a generic flash routine.
4387 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4389 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4390 switch (square_color) {
4392 case 2: /* neutral */
4394 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4395 ? *pieceToOutline(piece)
4396 : *pieceToSolid(piece),
4397 dest, bwPieceGC, 0, 0,
4398 squareSize, squareSize, x, y);
4401 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4402 ? *pieceToSolid(piece)
4403 : *pieceToOutline(piece),
4404 dest, wbPieceGC, 0, 0,
4405 squareSize, squareSize, x, y);
4411 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4413 switch (square_color) {
4415 case 2: /* neutral */
4417 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4418 ? *pieceToOutline(piece)
4419 : *pieceToSolid(piece),
4420 dest, bwPieceGC, 0, 0,
4421 squareSize, squareSize, x, y, 1);
4424 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4425 ? *pieceToSolid(piece)
4426 : *pieceToOutline(piece),
4427 dest, wbPieceGC, 0, 0,
4428 squareSize, squareSize, x, y, 1);
4434 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4436 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4437 switch (square_color) {
4439 XCopyPlane(xDisplay, *pieceToSolid(piece),
4440 dest, (int) piece < (int) BlackPawn
4441 ? wlPieceGC : blPieceGC, 0, 0,
4442 squareSize, squareSize, x, y, 1);
4445 XCopyPlane(xDisplay, *pieceToSolid(piece),
4446 dest, (int) piece < (int) BlackPawn
4447 ? wdPieceGC : bdPieceGC, 0, 0,
4448 squareSize, squareSize, x, y, 1);
4450 case 2: /* neutral */
4452 XCopyPlane(xDisplay, *pieceToSolid(piece),
4453 dest, (int) piece < (int) BlackPawn
4454 ? wjPieceGC : bjPieceGC, 0, 0,
4455 squareSize, squareSize, x, y, 1);
4461 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4463 int kind, p = piece;
4465 switch (square_color) {
4467 case 2: /* neutral */
4469 if ((int)piece < (int) BlackPawn) {
4477 if ((int)piece < (int) BlackPawn) {
4485 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4486 if(useTexture & square_color+1) {
4487 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4488 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4489 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4490 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4491 XSetClipMask(xDisplay, wlPieceGC, None);
4492 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4494 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4495 dest, wlPieceGC, 0, 0,
4496 squareSize, squareSize, x, y);
4499 typedef void (*DrawFunc)();
4504 if (appData.monoMode) {
4505 if (DefaultDepth(xDisplay, xScreen) == 1) {
4506 return monoDrawPiece_1bit;
4508 return monoDrawPiece;
4512 return colorDrawPieceImage;
4514 return colorDrawPiece;
4518 /* [HR] determine square color depending on chess variant. */
4520 SquareColor (int row, int column)
4524 if (gameInfo.variant == VariantXiangqi) {
4525 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4527 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4529 } else if (row <= 4) {
4535 square_color = ((column + row) % 2) == 1;
4538 /* [hgm] holdings: next line makes all holdings squares light */
4539 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4541 return square_color;
4545 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4547 int square_color, x, y, direction, font_ascent, font_descent;
4550 XCharStruct overall;
4554 /* Calculate delay in milliseconds (2-delays per complete flash) */
4555 flash_delay = 500 / appData.flashRate;
4558 x = lineGap + ((BOARD_WIDTH-1)-column) *
4559 (squareSize + lineGap);
4560 y = lineGap + row * (squareSize + lineGap);
4562 x = lineGap + column * (squareSize + lineGap);
4563 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4564 (squareSize + lineGap);
4567 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4569 square_color = SquareColor(row, column);
4571 if ( // [HGM] holdings: blank out area between board and holdings
4572 column == BOARD_LEFT-1 || column == BOARD_RGHT
4573 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4574 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4575 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4577 // [HGM] print piece counts next to holdings
4578 string[1] = NULLCHAR;
4579 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4580 string[0] = '0' + piece;
4581 XTextExtents(countFontStruct, string, 1, &direction,
4582 &font_ascent, &font_descent, &overall);
4583 if (appData.monoMode) {
4584 XDrawImageString(xDisplay, xBoardWindow, countGC,
4585 x + squareSize - overall.width - 2,
4586 y + font_ascent + 1, string, 1);
4588 XDrawString(xDisplay, xBoardWindow, countGC,
4589 x + squareSize - overall.width - 2,
4590 y + font_ascent + 1, string, 1);
4593 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4594 string[0] = '0' + piece;
4595 XTextExtents(countFontStruct, string, 1, &direction,
4596 &font_ascent, &font_descent, &overall);
4597 if (appData.monoMode) {
4598 XDrawImageString(xDisplay, xBoardWindow, countGC,
4599 x + 2, y + font_ascent + 1, string, 1);
4601 XDrawString(xDisplay, xBoardWindow, countGC,
4602 x + 2, y + font_ascent + 1, string, 1);
4606 if (piece == EmptySquare || appData.blindfold) {
4607 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4609 drawfunc = ChooseDrawFunc();
4611 if (do_flash && appData.flashCount > 0) {
4612 for (i=0; i<appData.flashCount; ++i) {
4613 drawfunc(piece, square_color, x, y, xBoardWindow);
4614 XSync(xDisplay, False);
4615 do_flash_delay(flash_delay);
4617 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4618 XSync(xDisplay, False);
4619 do_flash_delay(flash_delay);
4622 drawfunc(piece, square_color, x, y, xBoardWindow);
4626 string[1] = NULLCHAR;
4627 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4628 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4629 string[0] = 'a' + column - BOARD_LEFT;
4630 XTextExtents(coordFontStruct, string, 1, &direction,
4631 &font_ascent, &font_descent, &overall);
4632 if (appData.monoMode) {
4633 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4634 x + squareSize - overall.width - 2,
4635 y + squareSize - font_descent - 1, string, 1);
4637 XDrawString(xDisplay, xBoardWindow, coordGC,
4638 x + squareSize - overall.width - 2,
4639 y + squareSize - font_descent - 1, string, 1);
4642 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4643 string[0] = ONE + row;
4644 XTextExtents(coordFontStruct, string, 1, &direction,
4645 &font_ascent, &font_descent, &overall);
4646 if (appData.monoMode) {
4647 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4648 x + 2, y + font_ascent + 1, string, 1);
4650 XDrawString(xDisplay, xBoardWindow, coordGC,
4651 x + 2, y + font_ascent + 1, string, 1);
4654 if(!partnerUp && marker[row][column]) {
4655 if(appData.monoMode) {
4656 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4657 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4658 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4659 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4661 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4662 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4667 Fraction (int x, int start, int stop)
4669 double f = ((double) x - start)/(stop - start);
4670 if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
4674 static WindowPlacement wpNew;
4677 CoDrag (Widget sh, WindowPlacement *wp)
4680 int j=0, touch=0, fudge = 2;
4681 GetActualPlacement(sh, wp);
4682 if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x) < fudge) touch = 1; else // right touch
4683 if(abs(wp->x + wp->width + 2*frameX - wpMain.x) < fudge) touch = 2; else // left touch
4684 if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
4685 if(abs(wp->y + wp->height + frameX + frameY - wpMain.y) < fudge) touch = 4; // top touch
4686 if(!touch ) return; // only windows that touch co-move
4687 if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
4688 int heightInc = wpNew.height - wpMain.height;
4689 double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
4690 double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
4691 wp->y += fracTop * heightInc;
4692 heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
4693 if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
4694 } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
4695 int widthInc = wpNew.width - wpMain.width;
4696 double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
4697 double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
4698 wp->y += fracLeft * widthInc;
4699 widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
4700 if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
4702 wp->x += wpNew.x - wpMain.x;
4703 wp->y += wpNew.y - wpMain.y;
4704 if(touch == 1) wp->x += wpNew.width - wpMain.width; else
4705 if(touch == 3) wp->y += wpNew.height - wpMain.height;
4706 XtSetArg(args[j], XtNx, wp->x); j++;
4707 XtSetArg(args[j], XtNy, wp->y); j++;
4708 XtSetValues(sh, args, j);
4714 GetActualPlacement(shellWidget, &wpNew);
4715 if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
4716 wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
4717 return; // false alarm
4718 if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
4719 if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
4720 if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
4721 if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
4723 XDrawPosition(boardWidget, True, NULL);
4730 static XtIntervalId delayedDragID = 0;
4731 if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
4733 XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
4736 /* Why is this needed on some versions of X? */
4738 EventProc (Widget widget, caddr_t unused, XEvent *event)
4740 if (!XtIsRealized(widget))
4742 switch (event->type) {
4743 case ConfigureNotify: // main window is being dragged: drag attached windows with it
4744 if(appData.useStickyWindows)
4745 DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
4748 if (event->xexpose.count > 0) return; /* no clipping is done */
4749 XDrawPosition(widget, True, NULL);
4750 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4751 flipView = !flipView; partnerUp = !partnerUp;
4752 XDrawPosition(widget, True, NULL);
4753 flipView = !flipView; partnerUp = !partnerUp;
4757 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4765 DrawPosition (int fullRedraw, Board board)
4767 XDrawPosition(boardWidget, fullRedraw, board);
4770 /* Returns 1 if there are "too many" differences between b1 and b2
4771 (i.e. more than 1 move was made) */
4773 too_many_diffs (Board b1, Board b2)
4778 for (i=0; i<BOARD_HEIGHT; ++i) {
4779 for (j=0; j<BOARD_WIDTH; ++j) {
4780 if (b1[i][j] != b2[i][j]) {
4781 if (++c > 4) /* Castling causes 4 diffs */
4789 /* Matrix describing castling maneuvers */
4790 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4791 static int castling_matrix[4][5] = {
4792 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4793 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4794 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4795 { 7, 7, 4, 5, 6 } /* 0-0, black */
4798 /* Checks whether castling occurred. If it did, *rrow and *rcol
4799 are set to the destination (row,col) of the rook that moved.
4801 Returns 1 if castling occurred, 0 if not.
4803 Note: Only handles a max of 1 castling move, so be sure
4804 to call too_many_diffs() first.
4807 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4812 /* For each type of castling... */
4813 for (i=0; i<4; ++i) {
4814 r = castling_matrix[i];
4816 /* Check the 4 squares involved in the castling move */
4818 for (j=1; j<=4; ++j) {
4819 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4826 /* All 4 changed, so it must be a castling move */
4835 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4837 DrawSeekAxis (int x, int y, int xTo, int yTo)
4839 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4843 DrawSeekBackground (int left, int top, int right, int bottom)
4845 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4849 DrawSeekText (char *buf, int x, int y)
4851 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4855 DrawSeekDot (int x, int y, int colorNr)
4857 int square = colorNr & 0x80;
4860 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4862 XFillRectangle(xDisplay, xBoardWindow, color,
4863 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4865 XFillArc(xDisplay, xBoardWindow, color,
4866 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4869 static int damage[2][BOARD_RANKS][BOARD_FILES];
4872 * event handler for redrawing the board
4875 XDrawPosition (Widget w, int repaint, Board board)
4878 static int lastFlipView = 0;
4879 static int lastBoardValid[2] = {0, 0};
4880 static Board lastBoard[2];
4883 int nr = twoBoards*partnerUp;
4885 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4887 if (board == NULL) {
4888 if (!lastBoardValid[nr]) return;
4889 board = lastBoard[nr];
4891 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4892 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4893 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4898 * It would be simpler to clear the window with XClearWindow()
4899 * but this causes a very distracting flicker.
4902 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4904 if ( lineGap && IsDrawArrowEnabled())
4905 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4906 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4908 /* If too much changes (begin observing new game, etc.), don't
4910 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4912 /* Special check for castling so we don't flash both the king
4913 and the rook (just flash the king). */
4915 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4916 /* Draw rook with NO flashing. King will be drawn flashing later */
4917 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4918 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4922 /* First pass -- Draw (newly) empty squares and repair damage.
4923 This prevents you from having a piece show up twice while it
4924 is flashing on its new square */
4925 for (i = 0; i < BOARD_HEIGHT; i++)
4926 for (j = 0; j < BOARD_WIDTH; j++)
4927 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4928 || damage[nr][i][j]) {
4929 DrawSquare(i, j, board[i][j], 0);
4930 damage[nr][i][j] = False;
4933 /* Second pass -- Draw piece(s) in new position and flash them */
4934 for (i = 0; i < BOARD_HEIGHT; i++)
4935 for (j = 0; j < BOARD_WIDTH; j++)
4936 if (board[i][j] != lastBoard[nr][i][j]) {
4937 DrawSquare(i, j, board[i][j], do_flash);
4941 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4942 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4943 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4945 for (i = 0; i < BOARD_HEIGHT; i++)
4946 for (j = 0; j < BOARD_WIDTH; j++) {
4947 DrawSquare(i, j, board[i][j], 0);
4948 damage[nr][i][j] = False;
4952 CopyBoard(lastBoard[nr], board);
4953 lastBoardValid[nr] = 1;
4954 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4955 lastFlipView = flipView;
4957 /* Draw highlights */
4958 if (pm1X >= 0 && pm1Y >= 0) {
4959 drawHighlight(pm1X, pm1Y, prelineGC);
4961 if (pm2X >= 0 && pm2Y >= 0) {
4962 drawHighlight(pm2X, pm2Y, prelineGC);
4964 if (hi1X >= 0 && hi1Y >= 0) {
4965 drawHighlight(hi1X, hi1Y, highlineGC);
4967 if (hi2X >= 0 && hi2Y >= 0) {
4968 drawHighlight(hi2X, hi2Y, highlineGC);
4970 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4972 /* If piece being dragged around board, must redraw that too */
4975 XSync(xDisplay, False);
4980 * event handler for redrawing the board
4983 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4985 XDrawPosition(w, True, NULL);
4990 * event handler for parsing user moves
4992 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4993 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4994 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4995 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4996 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4997 // and at the end FinishMove() to perform the move after optional promotion popups.
4998 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
5000 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5002 if (w != boardWidget || errorExitStatus != -1) return;
5003 if(nprms) shiftKey = !strcmp(prms[0], "1");
5006 if (event->type == ButtonPress) {
5007 XtPopdown(promotionShell);
5008 XtDestroyWidget(promotionShell);
5009 promotionUp = False;
5017 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
5018 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
5019 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
5023 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
5025 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
5026 DragPieceMove(event->xmotion.x, event->xmotion.y);
5030 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
5031 { // [HGM] pv: walk PV
5032 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
5035 static int savedIndex; /* gross that this is global */
5038 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
5041 XawTextPosition index, dummy;
5044 XawTextGetSelectionPos(w, &index, &dummy);
5045 XtSetArg(arg, XtNstring, &val);
5046 XtGetValues(w, &arg, 1);
5047 ReplaceComment(savedIndex, val);
5048 if(savedIndex != currentMove) ToNrEvent(savedIndex);
5049 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
5053 EditCommentPopUp (int index, char *title, char *text)
5056 if (text == NULL) text = "";
5057 NewCommentPopup(title, text, index);
5066 extern Option boxOptions[];
5076 edit = boxOptions[0].handle;
5078 XtSetArg(args[j], XtNstring, &val); j++;
5079 XtGetValues(edit, args, j);
5081 SendMultiLineToICS(val);
5082 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5083 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5087 ICSInputBoxPopDown ()
5093 CommentPopUp (char *title, char *text)
5095 savedIndex = currentMove; // [HGM] vari
5096 NewCommentPopup(title, text, currentMove);
5105 static char *openName;
5111 (void) (*fileProc)(openFP, 0, openName);
5115 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
5117 fileProc = proc; /* I can't see a way not */
5118 fileOpenMode = openMode; /* to use globals here */
5119 { // [HGM] use file-selector dialog stolen from Ghostview
5120 int index; // this is not supported yet
5121 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5122 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5123 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5124 ScheduleDelayedEvent(&DelayedLoad, 50);
5131 if (!filenameUp) return;
5132 XtPopdown(fileNameShell);
5133 XtDestroyWidget(fileNameShell);
5139 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5144 XtSetArg(args[0], XtNlabel, &name);
5145 XtGetValues(w, args, 1);
5147 if (strcmp(name, _("cancel")) == 0) {
5152 FileNameAction(w, NULL, NULL, NULL);
5156 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5164 name = XawDialogGetValueString(w = XtParent(w));
5166 if ((name != NULL) && (*name != NULLCHAR)) {
5167 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5168 XtPopdown(w = XtParent(XtParent(w)));
5172 p = strrchr(buf, ' ');
5179 fullname = ExpandPathName(buf);
5181 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5184 f = fopen(fullname, fileOpenMode);
5186 DisplayError(_("Failed to open file"), errno);
5188 (void) (*fileProc)(f, index, buf);
5195 XtPopdown(w = XtParent(XtParent(w)));
5205 Widget dialog, layout;
5207 Dimension bw_width, pw_width;
5209 char *PromoChars = "wglcqrbnkac+=\0";
5212 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5213 XtGetValues(boardWidget, args, j);
5216 XtSetArg(args[j], XtNresizable, True); j++;
5217 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5219 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5220 shellWidget, args, j);
5222 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5223 layoutArgs, XtNumber(layoutArgs));
5226 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5227 XtSetArg(args[j], XtNborderWidth, 0); j++;
5228 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5231 if(gameInfo.variant != VariantShogi) {
5232 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5233 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5234 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5235 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5236 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5238 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5239 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5240 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5241 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5243 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5244 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5245 gameInfo.variant == VariantGiveaway) {
5246 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5248 if(gameInfo.variant == VariantCapablanca ||
5249 gameInfo.variant == VariantGothic ||
5250 gameInfo.variant == VariantCapaRandom) {
5251 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5252 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5254 } else // [HGM] shogi
5256 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5257 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5259 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5261 XtRealizeWidget(promotionShell);
5262 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5265 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5266 XtGetValues(promotionShell, args, j);
5268 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5269 lineGap + squareSize/3 +
5270 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5271 0 : 6*(squareSize + lineGap)), &x, &y);
5274 XtSetArg(args[j], XtNx, x); j++;
5275 XtSetArg(args[j], XtNy, y); j++;
5276 XtSetValues(promotionShell, args, j);
5278 XtPopup(promotionShell, XtGrabNone);
5286 if (!promotionUp) return;
5287 XtPopdown(promotionShell);
5288 XtDestroyWidget(promotionShell);
5289 promotionUp = False;
5293 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5295 int promoChar = * (const char *) client_data;
5299 if (fromX == -1) return;
5306 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5308 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5309 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5315 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5317 dialogError = errorUp = False;
5318 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5320 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5327 if (!errorUp) return;
5328 dialogError = errorUp = False;
5329 XtPopdown(errorShell);
5330 XtDestroyWidget(errorShell);
5331 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5335 ErrorPopUp (char *title, char *label, int modal)
5338 Widget dialog, layout;
5342 Dimension bw_width, pw_width;
5343 Dimension pw_height;
5347 XtSetArg(args[i], XtNresizable, True); i++;
5348 XtSetArg(args[i], XtNtitle, title); i++;
5350 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5351 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
5353 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5354 layoutArgs, XtNumber(layoutArgs));
5357 XtSetArg(args[i], XtNlabel, label); i++;
5358 XtSetArg(args[i], XtNborderWidth, 0); i++;
5359 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5362 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5364 XtRealizeWidget(errorShell);
5365 CatchDeleteWindow(errorShell, "ErrorPopDown");
5368 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5369 XtGetValues(boardWidget, args, i);
5371 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5372 XtSetArg(args[i], XtNheight, &pw_height); i++;
5373 XtGetValues(errorShell, args, i);
5376 /* This code seems to tickle an X bug if it is executed too soon
5377 after xboard starts up. The coordinates get transformed as if
5378 the main window was positioned at (0, 0).
5380 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5381 0 - pw_height + squareSize / 3, &x, &y);
5383 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5384 RootWindowOfScreen(XtScreen(boardWidget)),
5385 (bw_width - pw_width) / 2,
5386 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5390 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5393 XtSetArg(args[i], XtNx, x); i++;
5394 XtSetArg(args[i], XtNy, y); i++;
5395 XtSetValues(errorShell, args, i);
5398 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5401 /* Disable all user input other than deleting the window */
5402 static int frozen = 0;
5408 /* Grab by a widget that doesn't accept input */
5409 XtAddGrab(messageWidget, TRUE, FALSE);
5413 /* Undo a FreezeUI */
5417 if (!frozen) return;
5418 XtRemoveGrab(messageWidget);
5423 ModeToWidgetName (GameMode mode)
5426 case BeginningOfGame:
5427 if (appData.icsActive)
5428 return "menuMode.ICS Client";
5429 else if (appData.noChessProgram ||
5430 *appData.cmailGameName != NULLCHAR)
5431 return "menuMode.Edit Game";
5433 return "menuMode.Machine Black";
5434 case MachinePlaysBlack:
5435 return "menuMode.Machine Black";
5436 case MachinePlaysWhite:
5437 return "menuMode.Machine White";
5439 return "menuMode.Analysis Mode";
5441 return "menuMode.Analyze File";
5442 case TwoMachinesPlay:
5443 return "menuMode.Two Machines";
5445 return "menuMode.Edit Game";
5446 case PlayFromGameFile:
5447 return "menuFile.Load Game";
5449 return "menuMode.Edit Position";
5451 return "menuMode.Training";
5452 case IcsPlayingWhite:
5453 case IcsPlayingBlack:
5457 return "menuMode.ICS Client";
5468 static int oldPausing = FALSE;
5469 static GameMode oldmode = (GameMode) -1;
5472 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5474 if (pausing != oldPausing) {
5475 oldPausing = pausing;
5477 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5479 XtSetArg(args[0], XtNleftBitmap, None);
5481 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5484 if (appData.showButtonBar) {
5485 /* Always toggle, don't set. Previous code messes up when
5486 invoked while the button is pressed, as releasing it
5487 toggles the state again. */
5490 XtSetArg(args[0], XtNbackground, &oldbg);
5491 XtSetArg(args[1], XtNforeground, &oldfg);
5492 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5494 XtSetArg(args[0], XtNbackground, oldfg);
5495 XtSetArg(args[1], XtNforeground, oldbg);
5497 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5501 wname = ModeToWidgetName(oldmode);
5502 if (wname != NULL) {
5503 XtSetArg(args[0], XtNleftBitmap, None);
5504 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5506 wname = ModeToWidgetName(gameMode);
5507 if (wname != NULL) {
5508 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5509 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5512 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5513 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5515 /* Maybe all the enables should be handled here, not just this one */
5516 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5517 gameMode == Training || gameMode == PlayFromGameFile);
5522 * Button/menu procedures
5525 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5531 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5533 cmailMsgLoaded = FALSE;
5534 if (gameNumber == 0) {
5535 int error = GameListBuild(f);
5537 DisplayError(_("Cannot build game list"), error);
5538 } else if (!ListEmpty(&gameList) &&
5539 ((ListGame *) gameList.tailPred)->number > 1) {
5540 GameListPopUp(f, title);
5546 return LoadGame(f, gameNumber, title, FALSE);
5550 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5552 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5555 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5559 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5565 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5571 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5577 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5583 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5589 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5595 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5597 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5600 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5604 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5606 FileNamePopUp(_("Save game file name?"),
5607 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5608 appData.oldSaveStyle ? ".game" : ".pgn",
5613 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5615 FileNamePopUp(_("Save position file name?"),
5616 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5617 appData.oldSaveStyle ? ".pos" : ".fen",
5622 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5624 ReloadCmailMsgEvent(FALSE);
5628 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5633 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5634 char *selected_fen_position=NULL;
5637 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5638 Atom *type_return, XtPointer *value_return,
5639 unsigned long *length_return, int *format_return)
5641 char *selection_tmp;
5643 if (!selected_fen_position) return False; /* should never happen */
5644 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5645 /* note: since no XtSelectionDoneProc was registered, Xt will
5646 * automatically call XtFree on the value returned. So have to
5647 * make a copy of it allocated with XtMalloc */
5648 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5649 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5651 *value_return=selection_tmp;
5652 *length_return=strlen(selection_tmp);
5653 *type_return=*target;
5654 *format_return = 8; /* bits per byte */
5656 } else if (*target == XA_TARGETS(xDisplay)) {
5657 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5658 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5659 targets_tmp[1] = XA_STRING;
5660 *value_return = targets_tmp;
5661 *type_return = XA_ATOM;
5664 // This code leads to a read of value_return out of bounds on 64-bit systems.
5665 // Other code which I have seen always sets *format_return to 32 independent of
5666 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5667 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5668 *format_return = 8 * sizeof(Atom);
5669 if (*format_return > 32) {
5670 *length_return *= *format_return / 32;
5671 *format_return = 32;
5674 *format_return = 32;
5682 /* note: when called from menu all parameters are NULL, so no clue what the
5683 * Widget which was clicked on was, or what the click event was
5686 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5689 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5690 * have a notion of a position that is selected but not copied.
5691 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5693 if(gameMode == EditPosition) EditPositionDone(TRUE);
5694 if (selected_fen_position) free(selected_fen_position);
5695 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5696 if (!selected_fen_position) return;
5697 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5699 SendPositionSelection,
5700 NULL/* lose_ownership_proc */ ,
5701 NULL/* transfer_done_proc */);
5702 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5704 SendPositionSelection,
5705 NULL/* lose_ownership_proc */ ,
5706 NULL/* transfer_done_proc */);
5710 CopyFENToClipboard ()
5711 { // wrapper to make call from back-end possible
5712 CopyPositionProc(NULL, NULL, NULL, NULL);
5715 /* function called when the data to Paste is ready */
5717 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5718 Atom *type, XtPointer value, unsigned long *len, int *format)
5721 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5722 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5723 EditPositionPasteFEN(fenstr);
5727 /* called when Paste Position button is pressed,
5728 * all parameters will be NULL */
5729 void PastePositionProc(w, event, prms, nprms)
5735 XtGetSelectionValue(menuBarWidget,
5736 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5737 /* (XtSelectionCallbackProc) */ PastePositionCB,
5738 NULL, /* client_data passed to PastePositionCB */
5740 /* better to use the time field from the event that triggered the
5741 * call to this function, but that isn't trivial to get
5749 SendGameSelection (Widget w, Atom *selection, Atom *target,
5750 Atom *type_return, XtPointer *value_return,
5751 unsigned long *length_return, int *format_return)
5753 char *selection_tmp;
5755 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5756 FILE* f = fopen(gameCopyFilename, "r");
5759 if (f == NULL) return False;
5763 selection_tmp = XtMalloc(len + 1);
5764 count = fread(selection_tmp, 1, len, f);
5767 XtFree(selection_tmp);
5770 selection_tmp[len] = NULLCHAR;
5771 *value_return = selection_tmp;
5772 *length_return = len;
5773 *type_return = *target;
5774 *format_return = 8; /* bits per byte */
5776 } else if (*target == XA_TARGETS(xDisplay)) {
5777 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5778 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5779 targets_tmp[1] = XA_STRING;
5780 *value_return = targets_tmp;
5781 *type_return = XA_ATOM;
5784 // This code leads to a read of value_return out of bounds on 64-bit systems.
5785 // Other code which I have seen always sets *format_return to 32 independent of
5786 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5787 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5788 *format_return = 8 * sizeof(Atom);
5789 if (*format_return > 32) {
5790 *length_return *= *format_return / 32;
5791 *format_return = 32;
5794 *format_return = 32;
5806 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5807 * have a notion of a game that is selected but not copied.
5808 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5810 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5813 NULL/* lose_ownership_proc */ ,
5814 NULL/* transfer_done_proc */);
5815 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5818 NULL/* lose_ownership_proc */ ,
5819 NULL/* transfer_done_proc */);
5822 /* note: when called from menu all parameters are NULL, so no clue what the
5823 * Widget which was clicked on was, or what the click event was
5826 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5830 ret = SaveGameToFile(gameCopyFilename, FALSE);
5837 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5839 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5843 /* function called when the data to Paste is ready */
5845 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5846 Atom *type, XtPointer value, unsigned long *len, int *format)
5849 if (value == NULL || *len == 0) {
5850 return; /* nothing had been selected to copy */
5852 f = fopen(gamePasteFilename, "w");
5854 DisplayError(_("Can't open temp file"), errno);
5857 fwrite(value, 1, *len, f);
5860 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5863 /* called when Paste Game button is pressed,
5864 * all parameters will be NULL */
5866 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5868 XtGetSelectionValue(menuBarWidget,
5869 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5870 /* (XtSelectionCallbackProc) */ PasteGameCB,
5871 NULL, /* client_data passed to PasteGameCB */
5873 /* better to use the time field from the event that triggered the
5874 * call to this function, but that isn't trivial to get
5885 SaveGameProc(NULL, NULL, NULL, NULL);
5890 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5896 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5902 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5904 MachineBlackEvent();
5908 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5910 MachineWhiteEvent();
5914 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5918 if (!first.analysisSupport) {
5919 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5920 DisplayError(buf, 0);
5923 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5924 if (appData.icsActive) {
5925 if (gameMode != IcsObserving) {
5926 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5927 DisplayError(buf, 0);
5929 if (appData.icsEngineAnalyze) {
5930 if (appData.debugMode)
5931 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5937 /* if enable, use want disable icsEngineAnalyze */
5938 if (appData.icsEngineAnalyze) {
5943 appData.icsEngineAnalyze = TRUE;
5944 if (appData.debugMode)
5945 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5947 #ifndef OPTIONSDIALOG
5948 if (!appData.showThinking)
5949 ShowThinkingProc(w,event,prms,nprms);
5956 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5958 if (!first.analysisSupport) {
5960 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5961 DisplayError(buf, 0);
5964 // Reset(FALSE, TRUE);
5965 #ifndef OPTIONSDIALOG
5966 if (!appData.showThinking)
5967 ShowThinkingProc(w,event,prms,nprms);
5970 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5971 AnalysisPeriodicEvent(1);
5975 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5981 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5987 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5993 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5999 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6001 EditPositionEvent();
6005 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6011 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6015 if (PopDown(1)) { // popdown succesful
6017 XtSetArg(args[j], XtNleftBitmap, None); j++;
6018 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6019 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6020 } else // was not up
6025 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6027 if (!PopDown(4)) ICSInputBoxPopUp();
6031 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6037 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6043 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6049 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6055 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6061 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6067 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6073 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6079 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6081 UserAdjudicationEvent(+1);
6085 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6087 UserAdjudicationEvent(-1);
6091 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6093 UserAdjudicationEvent(0);
6097 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6099 if (shellUp[4] == True)
6104 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6105 { // [HGM] input: let up-arrow recall previous line from history
6112 if (!shellUp[4]) return;
6113 edit = boxOptions[0].handle;
6115 XtSetArg(args[j], XtNstring, &val); j++;
6116 XtGetValues(edit, args, j);
6117 val = PrevInHistory(val);
6118 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6119 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6121 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6122 XawTextReplace(edit, 0, 0, &t);
6123 XawTextSetInsertionPoint(edit, 9999);
6128 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6129 { // [HGM] input: let down-arrow recall next line from history
6134 if (!shellUp[4]) return;
6135 edit = boxOptions[0].handle;
6136 val = NextInHistory();
6137 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6138 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6140 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6141 XawTextReplace(edit, 0, 0, &t);
6142 XawTextSetInsertionPoint(edit, 9999);
6147 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6149 StopObservingEvent();
6153 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6155 StopExaminingEvent();
6159 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6166 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6173 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6179 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6181 if (!TempBackwardActive) {
6182 TempBackwardActive = True;
6188 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6190 /* Check to see if triggered by a key release event for a repeating key.
6191 * If so the next queued event will be a key press of the same key at the same time */
6192 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6194 XPeekEvent(xDisplay, &next);
6195 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6196 next.xkey.keycode == event->xkey.keycode)
6200 TempBackwardActive = False;
6204 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6210 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6216 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6222 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6228 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6230 TruncateGameEvent();
6234 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6240 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6246 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6248 flipView = !flipView;
6249 DrawPosition(True, NULL);
6253 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6257 PonderNextMoveEvent(!appData.ponderNextMove);
6258 #ifndef OPTIONSDIALOG
6259 if (appData.ponderNextMove) {
6260 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6262 XtSetArg(args[0], XtNleftBitmap, None);
6264 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6269 #ifndef OPTIONSDIALOG
6271 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6275 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6277 if (appData.alwaysPromoteToQueen) {
6278 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6280 XtSetArg(args[0], XtNleftBitmap, None);
6282 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6287 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6291 appData.animateDragging = !appData.animateDragging;
6293 if (appData.animateDragging) {
6294 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6297 XtSetArg(args[0], XtNleftBitmap, None);
6299 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6304 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6308 appData.animate = !appData.animate;
6310 if (appData.animate) {
6311 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6314 XtSetArg(args[0], XtNleftBitmap, None);
6316 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6321 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6325 appData.autoCallFlag = !appData.autoCallFlag;
6327 if (appData.autoCallFlag) {
6328 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6330 XtSetArg(args[0], XtNleftBitmap, None);
6332 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6337 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6341 appData.autoFlipView = !appData.autoFlipView;
6343 if (appData.autoFlipView) {
6344 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6346 XtSetArg(args[0], XtNleftBitmap, None);
6348 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6353 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6357 appData.blindfold = !appData.blindfold;
6359 if (appData.blindfold) {
6360 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6362 XtSetArg(args[0], XtNleftBitmap, None);
6364 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6367 DrawPosition(True, NULL);
6371 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6375 appData.testLegality = !appData.testLegality;
6377 if (appData.testLegality) {
6378 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6380 XtSetArg(args[0], XtNleftBitmap, None);
6382 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6388 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6392 if (appData.flashCount == 0) {
6393 appData.flashCount = 3;
6395 appData.flashCount = -appData.flashCount;
6398 if (appData.flashCount > 0) {
6399 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6401 XtSetArg(args[0], XtNleftBitmap, None);
6403 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6409 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6413 appData.highlightDragging = !appData.highlightDragging;
6415 if (appData.highlightDragging) {
6416 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6418 XtSetArg(args[0], XtNleftBitmap, None);
6420 XtSetValues(XtNameToWidget(menuBarWidget,
6421 "menuOptions.Highlight Dragging"), args, 1);
6426 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6430 appData.highlightLastMove = !appData.highlightLastMove;
6432 if (appData.highlightLastMove) {
6433 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6435 XtSetArg(args[0], XtNleftBitmap, None);
6437 XtSetValues(XtNameToWidget(menuBarWidget,
6438 "menuOptions.Highlight Last Move"), args, 1);
6442 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6446 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6448 if (appData.highlightMoveWithArrow) {
6449 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6451 XtSetArg(args[0], XtNleftBitmap, None);
6453 XtSetValues(XtNameToWidget(menuBarWidget,
6454 "menuOptions.Arrow"), args, 1);
6459 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6463 appData.icsAlarm = !appData.icsAlarm;
6465 if (appData.icsAlarm) {
6466 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6468 XtSetArg(args[0], XtNleftBitmap, None);
6470 XtSetValues(XtNameToWidget(menuBarWidget,
6471 "menuOptions.ICS Alarm"), args, 1);
6476 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6480 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6482 if (appData.ringBellAfterMoves) {
6483 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6485 XtSetArg(args[0], XtNleftBitmap, None);
6487 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6492 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6496 appData.oneClick = !appData.oneClick;
6498 if (appData.oneClick) {
6499 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6501 XtSetArg(args[0], XtNleftBitmap, None);
6503 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6508 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6512 PeriodicUpdatesEvent(!appData.periodicUpdates);
6514 if (appData.periodicUpdates) {
6515 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6517 XtSetArg(args[0], XtNleftBitmap, None);
6519 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6524 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6528 appData.popupExitMessage = !appData.popupExitMessage;
6530 if (appData.popupExitMessage) {
6531 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6533 XtSetArg(args[0], XtNleftBitmap, None);
6535 XtSetValues(XtNameToWidget(menuBarWidget,
6536 "menuOptions.Popup Exit Message"), args, 1);
6540 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6544 appData.popupMoveErrors = !appData.popupMoveErrors;
6546 if (appData.popupMoveErrors) {
6547 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6549 XtSetArg(args[0], XtNleftBitmap, None);
6551 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6557 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6561 appData.premove = !appData.premove;
6563 if (appData.premove) {
6564 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6566 XtSetArg(args[0], XtNleftBitmap, None);
6568 XtSetValues(XtNameToWidget(menuBarWidget,
6569 "menuOptions.Premove"), args, 1);
6574 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6578 appData.showCoords = !appData.showCoords;
6580 if (appData.showCoords) {
6581 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6583 XtSetArg(args[0], XtNleftBitmap, None);
6585 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6588 DrawPosition(True, NULL);
6592 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6594 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6595 ShowThinkingEvent();
6599 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6603 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6604 ShowThinkingEvent();
6606 if (appData.hideThinkingFromHuman) {
6607 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6609 XtSetArg(args[0], XtNleftBitmap, None);
6611 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6617 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6621 saveSettingsOnExit = !saveSettingsOnExit;
6623 if (saveSettingsOnExit) {
6624 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6626 XtSetArg(args[0], XtNleftBitmap, None);
6628 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6633 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6635 SaveSettings(settingsFileName);
6639 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6642 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6648 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6652 if (nprms && *nprms > 0)
6656 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6661 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6667 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6673 BugReportProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6676 snprintf(buf, MSG_SIZ, "%s mailto:bug-xboard@gnu.org", appData.sysOpen);
6681 GuideProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6684 snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/user_guide/UserGuide.html", appData.sysOpen);
6689 HomePageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6692 snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/", appData.sysOpen);
6697 NewsPageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6700 snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/whats_new/portal.html", appData.sysOpen);
6705 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6707 char buf[2 * MSG_SIZ];
6709 char *zippy = _(" (with Zippy code)");
6713 snprintf(buf, sizeof(buf),
6715 "Copyright 1991 Digital Equipment Corporation\n"
6716 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6717 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6718 "%s is free software and carries NO WARRANTY;"
6719 "see the file COPYING for more information.\n\n"
6720 "Visit XBoard on the web at: http://www.gnu.org/software/xboard/\n"
6721 "Check out the newest features at: http://www.gnu.org/software/xboard/whats_new.html\n\n"
6722 "Report bugs via email at: <bug-xboard@gnu.org>\n\n"
6724 programVersion, zippy, PACKAGE);
6725 ErrorPopUp(_("About XBoard"), buf, FALSE);
6729 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6731 appData.debugMode = !appData.debugMode;
6735 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6741 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6747 DisplayMessage (char *message, char *extMessage)
6749 /* display a message in the message widget */
6758 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6763 message = extMessage;
6767 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6769 /* need to test if messageWidget already exists, since this function
6770 can also be called during the startup, if for example a Xresource
6771 is not set up correctly */
6774 XtSetArg(arg, XtNlabel, message);
6775 XtSetValues(messageWidget, &arg, 1);
6782 DisplayTitle (char *text)
6786 char title[MSG_SIZ];
6789 if (text == NULL) text = "";
6791 if (appData.titleInWindow) {
6793 XtSetArg(args[i], XtNlabel, text); i++;
6794 XtSetValues(titleWidget, args, i);
6797 if (*text != NULLCHAR) {
6798 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6799 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6800 } else if (appData.icsActive) {
6801 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6802 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6803 } else if (appData.cmailGameName[0] != NULLCHAR) {
6804 snprintf(icon, sizeof(icon), "%s", "CMail");
6805 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6807 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6808 } else if (gameInfo.variant == VariantGothic) {
6809 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6810 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6813 } else if (gameInfo.variant == VariantFalcon) {
6814 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6815 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6817 } else if (appData.noChessProgram) {
6818 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6819 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6821 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6822 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6825 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6826 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6827 XtSetValues(shellWidget, args, i);
6828 XSync(xDisplay, False);
6833 DisplayError (String message, int error)
6838 if (appData.debugMode || appData.matchMode) {
6839 fprintf(stderr, "%s: %s\n", programName, message);
6842 if (appData.debugMode || appData.matchMode) {
6843 fprintf(stderr, "%s: %s: %s\n",
6844 programName, message, strerror(error));
6846 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6849 ErrorPopUp(_("Error"), message, FALSE);
6854 DisplayMoveError (String message)
6858 DrawPosition(FALSE, NULL);
6859 if (appData.debugMode || appData.matchMode) {
6860 fprintf(stderr, "%s: %s\n", programName, message);
6862 if (appData.popupMoveErrors) {
6863 ErrorPopUp(_("Error"), message, FALSE);
6865 DisplayMessage(message, "");
6871 DisplayFatalError (String message, int error, int status)
6875 errorExitStatus = status;
6877 fprintf(stderr, "%s: %s\n", programName, message);
6879 fprintf(stderr, "%s: %s: %s\n",
6880 programName, message, strerror(error));
6881 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6884 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6885 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6892 DisplayInformation (String message)
6895 ErrorPopUp(_("Information"), message, TRUE);
6899 DisplayNote (String message)
6902 ErrorPopUp(_("Note"), message, FALSE);
6906 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6912 DisplayIcsInteractionTitle (String message)
6914 if (oldICSInteractionTitle == NULL) {
6915 /* Magic to find the old window title, adapted from vim */
6916 char *wina = getenv("WINDOWID");
6918 Window win = (Window) atoi(wina);
6919 Window root, parent, *children;
6920 unsigned int nchildren;
6921 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6923 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6924 if (!XQueryTree(xDisplay, win, &root, &parent,
6925 &children, &nchildren)) break;
6926 if (children) XFree((void *)children);
6927 if (parent == root || parent == 0) break;
6930 XSetErrorHandler(oldHandler);
6932 if (oldICSInteractionTitle == NULL) {
6933 oldICSInteractionTitle = "xterm";
6936 printf("\033]0;%s\007", message);
6940 char pendingReplyPrefix[MSG_SIZ];
6941 ProcRef pendingReplyPR;
6944 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6947 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6951 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6955 AskQuestionPopDown ()
6957 if (!askQuestionUp) return;
6958 XtPopdown(askQuestionShell);
6959 XtDestroyWidget(askQuestionShell);
6960 askQuestionUp = False;
6964 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6970 reply = XawDialogGetValueString(w = XtParent(w));
6971 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6972 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6973 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6974 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6975 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6976 AskQuestionPopDown();
6978 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6982 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6987 XtSetArg(args[0], XtNlabel, &name);
6988 XtGetValues(w, args, 1);
6990 if (strcmp(name, _("cancel")) == 0) {
6991 AskQuestionPopDown();
6993 AskQuestionReplyAction(w, NULL, NULL, NULL);
6998 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
7001 Widget popup, layout, dialog, edit;
7007 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7008 pendingReplyPR = pr;
7011 XtSetArg(args[i], XtNresizable, True); i++;
7012 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7013 askQuestionShell = popup =
7014 XtCreatePopupShell(title, transientShellWidgetClass,
7015 shellWidget, args, i);
7018 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7019 layoutArgs, XtNumber(layoutArgs));
7022 XtSetArg(args[i], XtNlabel, question); i++;
7023 XtSetArg(args[i], XtNvalue, ""); i++;
7024 XtSetArg(args[i], XtNborderWidth, 0); i++;
7025 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7028 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7029 (XtPointer) dialog);
7030 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7031 (XtPointer) dialog);
7033 XtRealizeWidget(popup);
7034 CatchDeleteWindow(popup, "AskQuestionPopDown");
7036 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7037 &x, &y, &win_x, &win_y, &mask);
7039 XtSetArg(args[0], XtNx, x - 10);
7040 XtSetArg(args[1], XtNy, y - 30);
7041 XtSetValues(popup, args, 2);
7043 XtPopup(popup, XtGrabExclusive);
7044 askQuestionUp = True;
7046 edit = XtNameToWidget(dialog, "*value");
7047 XtSetKeyboardFocus(popup, edit);
7052 PlaySound (char *name)
7054 if (*name == NULLCHAR) {
7056 } else if (strcmp(name, "$") == 0) {
7057 putc(BELLCHAR, stderr);
7060 char *prefix = "", *sep = "";
7061 if(appData.soundProgram[0] == NULLCHAR) return;
7062 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7063 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7071 PlaySound(appData.soundMove);
7077 PlaySound(appData.soundIcsWin);
7083 PlaySound(appData.soundIcsLoss);
7089 PlaySound(appData.soundIcsDraw);
7093 PlayIcsUnfinishedSound ()
7095 PlaySound(appData.soundIcsUnfinished);
7101 PlaySound(appData.soundIcsAlarm);
7107 PlaySound(appData.soundTell);
7113 system("stty echo");
7120 system("stty -echo");
7125 RunCommand (char *buf)
7131 Colorize (ColorClass cc, int continuation)
7134 int count, outCount, error;
7136 if (textColors[(int)cc].bg > 0) {
7137 if (textColors[(int)cc].fg > 0) {
7138 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7139 textColors[(int)cc].fg, textColors[(int)cc].bg);
7141 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7142 textColors[(int)cc].bg);
7145 if (textColors[(int)cc].fg > 0) {
7146 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7147 textColors[(int)cc].fg);
7149 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7152 count = strlen(buf);
7153 outCount = OutputToProcess(NoProc, buf, count, &error);
7154 if (outCount < count) {
7155 DisplayFatalError(_("Error writing to display"), error, 1);
7158 if (continuation) return;
7161 PlaySound(appData.soundShout);
7164 PlaySound(appData.soundSShout);
7167 PlaySound(appData.soundChannel1);
7170 PlaySound(appData.soundChannel);
7173 PlaySound(appData.soundKibitz);
7176 PlaySound(appData.soundTell);
7178 case ColorChallenge:
7179 PlaySound(appData.soundChallenge);
7182 PlaySound(appData.soundRequest);
7185 PlaySound(appData.soundSeek);
7197 return getpwuid(getuid())->pw_name;
7201 ExpandPathName (char *path)
7203 static char static_buf[4*MSG_SIZ];
7204 char *d, *s, buf[4*MSG_SIZ];
7210 while (*s && isspace(*s))
7219 if (*(s+1) == '/') {
7220 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7224 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7225 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7226 pwd = getpwnam(buf);
7229 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7233 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7234 strcat(d, strchr(s+1, '/'));
7238 safeStrCpy(d, s, 4*MSG_SIZ );
7246 static char host_name[MSG_SIZ];
7248 #if HAVE_GETHOSTNAME
7249 gethostname(host_name, MSG_SIZ);
7251 #else /* not HAVE_GETHOSTNAME */
7252 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7253 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7255 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7257 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7258 #endif /* not HAVE_GETHOSTNAME */
7261 XtIntervalId delayedEventTimerXID = 0;
7262 DelayedEventCallback delayedEventCallback = 0;
7267 delayedEventTimerXID = 0;
7268 delayedEventCallback();
7272 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7274 if(delayedEventTimerXID && delayedEventCallback == cb)
7275 // [HGM] alive: replace, rather than add or flush identical event
7276 XtRemoveTimeOut(delayedEventTimerXID);
7277 delayedEventCallback = cb;
7278 delayedEventTimerXID =
7279 XtAppAddTimeOut(appContext, millisec,
7280 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7283 DelayedEventCallback
7286 if (delayedEventTimerXID) {
7287 return delayedEventCallback;
7294 CancelDelayedEvent ()
7296 if (delayedEventTimerXID) {
7297 XtRemoveTimeOut(delayedEventTimerXID);
7298 delayedEventTimerXID = 0;
7302 XtIntervalId loadGameTimerXID = 0;
7305 LoadGameTimerRunning ()
7307 return loadGameTimerXID != 0;
7311 StopLoadGameTimer ()
7313 if (loadGameTimerXID != 0) {
7314 XtRemoveTimeOut(loadGameTimerXID);
7315 loadGameTimerXID = 0;
7323 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7325 loadGameTimerXID = 0;
7330 StartLoadGameTimer (long millisec)
7333 XtAppAddTimeOut(appContext, millisec,
7334 (XtTimerCallbackProc) LoadGameTimerCallback,
7338 XtIntervalId analysisClockXID = 0;
7341 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7343 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7344 || appData.icsEngineAnalyze) { // [DM]
7345 AnalysisPeriodicEvent(0);
7346 StartAnalysisClock();
7351 StartAnalysisClock ()
7354 XtAppAddTimeOut(appContext, 2000,
7355 (XtTimerCallbackProc) AnalysisClockCallback,
7359 XtIntervalId clockTimerXID = 0;
7362 ClockTimerRunning ()
7364 return clockTimerXID != 0;
7370 if (clockTimerXID != 0) {
7371 XtRemoveTimeOut(clockTimerXID);
7380 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7387 StartClockTimer (long millisec)
7390 XtAppAddTimeOut(appContext, millisec,
7391 (XtTimerCallbackProc) ClockTimerCallback,
7396 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7401 /* check for low time warning */
7402 Pixel foregroundOrWarningColor = timerForegroundPixel;
7405 appData.lowTimeWarning &&
7406 (timer / 1000) < appData.icsAlarmTime)
7407 foregroundOrWarningColor = lowTimeWarningColor;
7409 if (appData.clockMode) {
7410 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7411 XtSetArg(args[0], XtNlabel, buf);
7413 snprintf(buf, MSG_SIZ, "%s ", color);
7414 XtSetArg(args[0], XtNlabel, buf);
7419 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7420 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7422 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7423 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7426 XtSetValues(w, args, 3);
7430 DisplayWhiteClock (long timeRemaining, int highlight)
7434 if(appData.noGUI) return;
7435 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7436 if (highlight && iconPixmap == bIconPixmap) {
7437 iconPixmap = wIconPixmap;
7438 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7439 XtSetValues(shellWidget, args, 1);
7444 DisplayBlackClock (long timeRemaining, int highlight)
7448 if(appData.noGUI) return;
7449 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7450 if (highlight && iconPixmap == wIconPixmap) {
7451 iconPixmap = bIconPixmap;
7452 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7453 XtSetValues(shellWidget, args, 1);
7472 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7476 int to_prog[2], from_prog[2];
7480 if (appData.debugMode) {
7481 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7484 /* We do NOT feed the cmdLine to the shell; we just
7485 parse it into blank-separated arguments in the
7486 most simple-minded way possible.
7489 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7492 while(*p == ' ') p++;
7494 if(*p == '"' || *p == '\'')
7495 p = strchr(++argv[i-1], *p);
7496 else p = strchr(p, ' ');
7497 if (p == NULL) break;
7502 SetUpChildIO(to_prog, from_prog);
7504 if ((pid = fork()) == 0) {
7506 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7507 close(to_prog[1]); // first close the unused pipe ends
7508 close(from_prog[0]);
7509 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7510 dup2(from_prog[1], 1);
7511 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7512 close(from_prog[1]); // and closing again loses one of the pipes!
7513 if(fileno(stderr) >= 2) // better safe than sorry...
7514 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7516 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7521 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7523 execvp(argv[0], argv);
7525 /* If we get here, exec failed */
7530 /* Parent process */
7532 close(from_prog[1]);
7534 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7537 cp->fdFrom = from_prog[0];
7538 cp->fdTo = to_prog[1];
7543 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7545 AlarmCallBack (int n)
7551 DestroyChildProcess (ProcRef pr, int signalType)
7553 ChildProc *cp = (ChildProc *) pr;
7555 if (cp->kind != CPReal) return;
7557 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7558 signal(SIGALRM, AlarmCallBack);
7560 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7561 kill(cp->pid, SIGKILL); // kill it forcefully
7562 wait((int *) 0); // and wait again
7566 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7568 /* Process is exiting either because of the kill or because of
7569 a quit command sent by the backend; either way, wait for it to die.
7578 InterruptChildProcess (ProcRef pr)
7580 ChildProc *cp = (ChildProc *) pr;
7582 if (cp->kind != CPReal) return;
7583 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7587 OpenTelnet (char *host, char *port, ProcRef *pr)
7589 char cmdLine[MSG_SIZ];
7591 if (port[0] == NULLCHAR) {
7592 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7594 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7596 return StartChildProcess(cmdLine, "", pr);
7600 OpenTCP (char *host, char *port, ProcRef *pr)
7603 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7604 #else /* !OMIT_SOCKETS */
7605 struct addrinfo hints;
7606 struct addrinfo *ais, *ai;
7611 memset(&hints, 0, sizeof(hints));
7612 hints.ai_family = AF_UNSPEC;
7613 hints.ai_socktype = SOCK_STREAM;
7615 error = getaddrinfo(host, port, &hints, &ais);
7617 /* a getaddrinfo error is not an errno, so can't return it */
7618 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7619 host, port, gai_strerror(error));
7623 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7624 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7628 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7641 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7647 #endif /* !OMIT_SOCKETS */
7653 OpenCommPort (char *name, ProcRef *pr)
7658 fd = open(name, 2, 0);
7659 if (fd < 0) return errno;
7661 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7672 OpenLoopback (ProcRef *pr)
7677 SetUpChildIO(to, from);
7679 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7682 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7690 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7692 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7696 #define INPUT_SOURCE_BUF_SIZE 8192
7705 char buf[INPUT_SOURCE_BUF_SIZE];
7710 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7712 InputSource *is = (InputSource *) closure;
7717 if (is->lineByLine) {
7718 count = read(is->fd, is->unused,
7719 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7721 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7724 is->unused += count;
7726 while (p < is->unused) {
7727 q = memchr(p, '\n', is->unused - p);
7728 if (q == NULL) break;
7730 (is->func)(is, is->closure, p, q - p, 0);
7734 while (p < is->unused) {
7739 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7744 (is->func)(is, is->closure, is->buf, count, error);
7749 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7752 ChildProc *cp = (ChildProc *) pr;
7754 is = (InputSource *) calloc(1, sizeof(InputSource));
7755 is->lineByLine = lineByLine;
7759 is->fd = fileno(stdin);
7761 is->kind = cp->kind;
7762 is->fd = cp->fdFrom;
7765 is->unused = is->buf;
7768 is->xid = XtAppAddInput(appContext, is->fd,
7769 (XtPointer) (XtInputReadMask),
7770 (XtInputCallbackProc) DoInputCallback,
7772 is->closure = closure;
7773 return (InputSourceRef) is;
7777 RemoveInputSource (InputSourceRef isr)
7779 InputSource *is = (InputSource *) isr;
7781 if (is->xid == 0) return;
7782 XtRemoveInput(is->xid);
7787 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7789 static int line = 0;
7790 ChildProc *cp = (ChildProc *) pr;
7795 if (appData.noJoin || !appData.useInternalWrap)
7796 outCount = fwrite(message, 1, count, stdout);
7799 int width = get_term_width();
7800 int len = wrap(NULL, message, count, width, &line);
7801 char *msg = malloc(len);
7805 outCount = fwrite(message, 1, count, stdout);
7808 dbgchk = wrap(msg, message, count, width, &line);
7809 if (dbgchk != len && appData.debugMode)
7810 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7811 outCount = fwrite(msg, 1, dbgchk, stdout);
7817 outCount = write(cp->fdTo, message, count);
7827 /* Output message to process, with "ms" milliseconds of delay
7828 between each character. This is needed when sending the logon
7829 script to ICC, which for some reason doesn't like the
7830 instantaneous send. */
7832 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7834 ChildProc *cp = (ChildProc *) pr;
7839 r = write(cp->fdTo, message++, 1);
7852 /**** Animation code by Hugh Fisher, DCS, ANU.
7854 Known problem: if a window overlapping the board is
7855 moved away while a piece is being animated underneath,
7856 the newly exposed area won't be updated properly.
7857 I can live with this.
7859 Known problem: if you look carefully at the animation
7860 of pieces in mono mode, they are being drawn as solid
7861 shapes without interior detail while moving. Fixing
7862 this would be a major complication for minimal return.
7865 /* Masks for XPM pieces. Black and white pieces can have
7866 different shapes, but in the interest of retaining my
7867 sanity pieces must have the same outline on both light
7868 and dark squares, and all pieces must use the same
7869 background square colors/images. */
7871 static int xpmDone = 0;
7874 CreateAnimMasks (int pieceDepth)
7880 unsigned long plane;
7883 /* Need a bitmap just to get a GC with right depth */
7884 buf = XCreatePixmap(xDisplay, xBoardWindow,
7886 values.foreground = 1;
7887 values.background = 0;
7888 /* Don't use XtGetGC, not read only */
7889 maskGC = XCreateGC(xDisplay, buf,
7890 GCForeground | GCBackground, &values);
7891 XFreePixmap(xDisplay, buf);
7893 buf = XCreatePixmap(xDisplay, xBoardWindow,
7894 squareSize, squareSize, pieceDepth);
7895 values.foreground = XBlackPixel(xDisplay, xScreen);
7896 values.background = XWhitePixel(xDisplay, xScreen);
7897 bufGC = XCreateGC(xDisplay, buf,
7898 GCForeground | GCBackground, &values);
7900 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7901 /* Begin with empty mask */
7902 if(!xpmDone) // [HGM] pieces: keep using existing
7903 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7904 squareSize, squareSize, 1);
7905 XSetFunction(xDisplay, maskGC, GXclear);
7906 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7907 0, 0, squareSize, squareSize);
7909 /* Take a copy of the piece */
7914 XSetFunction(xDisplay, bufGC, GXcopy);
7915 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7917 0, 0, squareSize, squareSize, 0, 0);
7919 /* XOR the background (light) over the piece */
7920 XSetFunction(xDisplay, bufGC, GXxor);
7922 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7923 0, 0, squareSize, squareSize, 0, 0);
7925 XSetForeground(xDisplay, bufGC, lightSquareColor);
7926 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7929 /* We now have an inverted piece image with the background
7930 erased. Construct mask by just selecting all the non-zero
7931 pixels - no need to reconstruct the original image. */
7932 XSetFunction(xDisplay, maskGC, GXor);
7934 /* Might be quicker to download an XImage and create bitmap
7935 data from it rather than this N copies per piece, but it
7936 only takes a fraction of a second and there is a much
7937 longer delay for loading the pieces. */
7938 for (n = 0; n < pieceDepth; n ++) {
7939 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7940 0, 0, squareSize, squareSize,
7946 XFreePixmap(xDisplay, buf);
7947 XFreeGC(xDisplay, bufGC);
7948 XFreeGC(xDisplay, maskGC);
7952 InitAnimState (AnimState *anim, XWindowAttributes *info)
7957 /* Each buffer is square size, same depth as window */
7958 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7959 squareSize, squareSize, info->depth);
7960 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7961 squareSize, squareSize, info->depth);
7963 /* Create a plain GC for blitting */
7964 mask = GCForeground | GCBackground | GCFunction |
7965 GCPlaneMask | GCGraphicsExposures;
7966 values.foreground = XBlackPixel(xDisplay, xScreen);
7967 values.background = XWhitePixel(xDisplay, xScreen);
7968 values.function = GXcopy;
7969 values.plane_mask = AllPlanes;
7970 values.graphics_exposures = False;
7971 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7973 /* Piece will be copied from an existing context at
7974 the start of each new animation/drag. */
7975 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7977 /* Outline will be a read-only copy of an existing */
7978 anim->outlineGC = None;
7984 XWindowAttributes info;
7986 if (xpmDone && gameInfo.variant == oldVariant) return;
7987 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7988 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7990 InitAnimState(&game, &info);
7991 InitAnimState(&player, &info);
7993 /* For XPM pieces, we need bitmaps to use as masks. */
7995 CreateAnimMasks(info.depth), xpmDone = 1;
8000 static Boolean frameWaiting;
8003 FrameAlarm (int sig)
8005 frameWaiting = False;
8006 /* In case System-V style signals. Needed?? */
8007 signal(SIGALRM, FrameAlarm);
8011 FrameDelay (int time)
8013 struct itimerval delay;
8015 XSync(xDisplay, False);
8018 frameWaiting = True;
8019 signal(SIGALRM, FrameAlarm);
8020 delay.it_interval.tv_sec =
8021 delay.it_value.tv_sec = time / 1000;
8022 delay.it_interval.tv_usec =
8023 delay.it_value.tv_usec = (time % 1000) * 1000;
8024 setitimer(ITIMER_REAL, &delay, NULL);
8025 while (frameWaiting) pause();
8026 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8027 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8028 setitimer(ITIMER_REAL, &delay, NULL);
8035 FrameDelay (int time)
8037 XSync(xDisplay, False);
8039 usleep(time * 1000);
8050 /* Convert board position to corner of screen rect and color */
8053 ScreenSquare (int column, int row, XPoint *pt, int *color)
8056 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8057 pt->y = lineGap + row * (squareSize + lineGap);
8059 pt->x = lineGap + column * (squareSize + lineGap);
8060 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8062 *color = SquareColor(row, column);
8065 /* Convert window coords to square */
8068 BoardSquare (int x, int y, int *column, int *row)
8070 *column = EventToSquare(x, BOARD_WIDTH);
8071 if (flipView && *column >= 0)
8072 *column = BOARD_WIDTH - 1 - *column;
8073 *row = EventToSquare(y, BOARD_HEIGHT);
8074 if (!flipView && *row >= 0)
8075 *row = BOARD_HEIGHT - 1 - *row;
8080 #undef Max /* just in case */
8082 #define Max(a, b) ((a) > (b) ? (a) : (b))
8083 #define Min(a, b) ((a) < (b) ? (a) : (b))
8086 SetRect (XRectangle *rect, int x, int y, int width, int height)
8090 rect->width = width;
8091 rect->height = height;
8094 /* Test if two frames overlap. If they do, return
8095 intersection rect within old and location of
8096 that rect within new. */
8099 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
8101 if (old->x > new->x + size || new->x > old->x + size ||
8102 old->y > new->y + size || new->y > old->y + size) {
8105 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8106 size - abs(old->x - new->x), size - abs(old->y - new->y));
8107 pt->x = Max(old->x - new->x, 0);
8108 pt->y = Max(old->y - new->y, 0);
8113 /* For two overlapping frames, return the rect(s)
8114 in the old that do not intersect with the new. */
8117 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
8121 /* If old = new (shouldn't happen) then nothing to draw */
8122 if (old->x == new->x && old->y == new->y) {
8126 /* Work out what bits overlap. Since we know the rects
8127 are the same size we don't need a full intersect calc. */
8129 /* Top or bottom edge? */
8130 if (new->y > old->y) {
8131 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8133 } else if (old->y > new->y) {
8134 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8135 size, old->y - new->y);
8138 /* Left or right edge - don't overlap any update calculated above. */
8139 if (new->x > old->x) {
8140 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8141 new->x - old->x, size - abs(new->y - old->y));
8143 } else if (old->x > new->x) {
8144 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8145 old->x - new->x, size - abs(new->y - old->y));
8152 /* Generate a series of frame coords from start->mid->finish.
8153 The movement rate doubles until the half way point is
8154 reached, then halves back down to the final destination,
8155 which gives a nice slow in/out effect. The algorithmn
8156 may seem to generate too many intermediates for short
8157 moves, but remember that the purpose is to attract the
8158 viewers attention to the piece about to be moved and
8159 then to where it ends up. Too few frames would be less
8163 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8165 int fraction, n, count;
8169 /* Slow in, stepping 1/16th, then 1/8th, ... */
8171 for (n = 0; n < factor; n++)
8173 for (n = 0; n < factor; n++) {
8174 frames[count].x = start->x + (mid->x - start->x) / fraction;
8175 frames[count].y = start->y + (mid->y - start->y) / fraction;
8177 fraction = fraction / 2;
8181 frames[count] = *mid;
8184 /* Slow out, stepping 1/2, then 1/4, ... */
8186 for (n = 0; n < factor; n++) {
8187 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8188 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8190 fraction = fraction * 2;
8195 /* Draw a piece on the screen without disturbing what's there */
8198 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8202 /* Bitmap for piece being moved. */
8203 if (appData.monoMode) {
8204 *mask = *pieceToSolid(piece);
8205 } else if (useImages) {
8207 *mask = xpmMask[piece];
8209 *mask = ximMaskPm[piece];
8212 *mask = *pieceToSolid(piece);
8215 /* GC for piece being moved. Square color doesn't matter, but
8216 since it gets modified we make a copy of the original. */
8218 if (appData.monoMode)
8223 if (appData.monoMode)
8228 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8230 /* Outline only used in mono mode and is not modified */
8232 *outline = bwPieceGC;
8234 *outline = wbPieceGC;
8238 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8243 /* Draw solid rectangle which will be clipped to shape of piece */
8244 XFillRectangle(xDisplay, dest, clip,
8245 0, 0, squareSize, squareSize);
8246 if (appData.monoMode)
8247 /* Also draw outline in contrasting color for black
8248 on black / white on white cases */
8249 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8250 0, 0, squareSize, squareSize, 0, 0, 1);
8252 /* Copy the piece */
8257 if(appData.upsideDown && flipView) kind ^= 2;
8258 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8260 0, 0, squareSize, squareSize,
8265 /* Animate the movement of a single piece */
8268 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8272 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8273 /* The old buffer is initialised with the start square (empty) */
8274 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8275 anim->prevFrame = *start;
8277 /* The piece will be drawn using its own bitmap as a matte */
8278 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8279 XSetClipMask(xDisplay, anim->pieceGC, mask);
8283 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8285 XRectangle updates[4];
8290 /* Save what we are about to draw into the new buffer */
8291 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8292 frame->x, frame->y, squareSize, squareSize,
8295 /* Erase bits of the previous frame */
8296 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8297 /* Where the new frame overlapped the previous,
8298 the contents in newBuf are wrong. */
8299 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8300 overlap.x, overlap.y,
8301 overlap.width, overlap.height,
8303 /* Repaint the areas in the old that don't overlap new */
8304 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8305 for (i = 0; i < count; i++)
8306 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8307 updates[i].x - anim->prevFrame.x,
8308 updates[i].y - anim->prevFrame.y,
8309 updates[i].width, updates[i].height,
8310 updates[i].x, updates[i].y);
8312 /* Easy when no overlap */
8313 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8314 0, 0, squareSize, squareSize,
8315 anim->prevFrame.x, anim->prevFrame.y);
8318 /* Save this frame for next time round */
8319 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8320 0, 0, squareSize, squareSize,
8322 anim->prevFrame = *frame;
8324 /* Draw piece over original screen contents, not current,
8325 and copy entire rect. Wipes out overlapping piece images. */
8326 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8327 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8328 0, 0, squareSize, squareSize,
8329 frame->x, frame->y);
8333 EndAnimation (AnimState *anim, XPoint *finish)
8335 XRectangle updates[4];
8340 /* The main code will redraw the final square, so we
8341 only need to erase the bits that don't overlap. */
8342 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8343 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8344 for (i = 0; i < count; i++)
8345 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8346 updates[i].x - anim->prevFrame.x,
8347 updates[i].y - anim->prevFrame.y,
8348 updates[i].width, updates[i].height,
8349 updates[i].x, updates[i].y);
8351 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8352 0, 0, squareSize, squareSize,
8353 anim->prevFrame.x, anim->prevFrame.y);
8358 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8362 BeginAnimation(anim, piece, startColor, start);
8363 for (n = 0; n < nFrames; n++) {
8364 AnimationFrame(anim, &(frames[n]), piece);
8365 FrameDelay(appData.animSpeed);
8367 EndAnimation(anim, finish);
8371 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8374 ChessSquare piece = board[fromY][toY];
8375 board[fromY][toY] = EmptySquare;
8376 DrawPosition(FALSE, board);
8378 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8379 y = lineGap + toY * (squareSize + lineGap);
8381 x = lineGap + toX * (squareSize + lineGap);
8382 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8384 for(i=1; i<4*kFactor; i++) {
8385 int r = squareSize * 9 * i/(20*kFactor - 5);
8386 XFillArc(xDisplay, xBoardWindow, highlineGC,
8387 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8388 FrameDelay(appData.animSpeed);
8390 board[fromY][toY] = piece;
8393 /* Main control logic for deciding what to animate and how */
8396 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8400 XPoint start, finish, mid;
8401 XPoint frames[kFactor * 2 + 1];
8402 int nFrames, startColor, endColor;
8404 /* Are we animating? */
8405 if (!appData.animate || appData.blindfold)
8408 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8409 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8410 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8412 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8413 piece = board[fromY][fromX];
8414 if (piece >= EmptySquare) return;
8419 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8422 ScreenSquare(fromX, fromY, &start, &startColor);
8423 ScreenSquare(toX, toY, &finish, &endColor);
8426 /* Knight: make straight movement then diagonal */
8427 if (abs(toY - fromY) < abs(toX - fromX)) {
8428 mid.x = start.x + (finish.x - start.x) / 2;
8432 mid.y = start.y + (finish.y - start.y) / 2;
8435 mid.x = start.x + (finish.x - start.x) / 2;
8436 mid.y = start.y + (finish.y - start.y) / 2;
8439 /* Don't use as many frames for very short moves */
8440 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8441 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8443 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8444 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8445 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8447 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8448 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8451 /* Be sure end square is redrawn */
8452 damage[0][toY][toX] = True;
8456 DragPieceBegin (int x, int y, Boolean instantly)
8458 int boardX, boardY, color;
8461 /* Are we animating? */
8462 if (!appData.animateDragging || appData.blindfold)
8465 /* Figure out which square we start in and the
8466 mouse position relative to top left corner. */
8467 BoardSquare(x, y, &boardX, &boardY);
8468 player.startBoardX = boardX;
8469 player.startBoardY = boardY;
8470 ScreenSquare(boardX, boardY, &corner, &color);
8471 player.startSquare = corner;
8472 player.startColor = color;
8473 /* As soon as we start dragging, the piece will jump slightly to
8474 be centered over the mouse pointer. */
8475 player.mouseDelta.x = squareSize/2;
8476 player.mouseDelta.y = squareSize/2;
8477 /* Initialise animation */
8478 player.dragPiece = PieceForSquare(boardX, boardY);
8480 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8481 player.dragActive = True;
8482 BeginAnimation(&player, player.dragPiece, color, &corner);
8483 /* Mark this square as needing to be redrawn. Note that
8484 we don't remove the piece though, since logically (ie
8485 as seen by opponent) the move hasn't been made yet. */
8486 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8487 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8488 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8489 corner.x, corner.y, squareSize, squareSize,
8490 0, 0); // [HGM] zh: unstack in stead of grab
8491 if(gatingPiece != EmptySquare) {
8492 /* Kludge alert: When gating we want the introduced
8493 piece to appear on the from square. To generate an
8494 image of it, we draw it on the board, copy the image,
8495 and draw the original piece again. */
8496 ChessSquare piece = boards[currentMove][boardY][boardX];
8497 DrawSquare(boardY, boardX, gatingPiece, 0);
8498 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8499 corner.x, corner.y, squareSize, squareSize, 0, 0);
8500 DrawSquare(boardY, boardX, piece, 0);
8502 damage[0][boardY][boardX] = True;
8504 player.dragActive = False;
8509 ChangeDragPiece (ChessSquare piece)
8512 player.dragPiece = piece;
8513 /* The piece will be drawn using its own bitmap as a matte */
8514 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8515 XSetClipMask(xDisplay, player.pieceGC, mask);
8519 DragPieceMove (int x, int y)
8523 /* Are we animating? */
8524 if (!appData.animateDragging || appData.blindfold)
8528 if (! player.dragActive)
8530 /* Move piece, maintaining same relative position
8531 of mouse within square */
8532 corner.x = x - player.mouseDelta.x;
8533 corner.y = y - player.mouseDelta.y;
8534 AnimationFrame(&player, &corner, player.dragPiece);
8536 if (appData.highlightDragging) {
8538 BoardSquare(x, y, &boardX, &boardY);
8539 SetHighlights(fromX, fromY, boardX, boardY);
8545 DragPieceEnd (int x, int y)
8547 int boardX, boardY, color;
8550 /* Are we animating? */
8551 if (!appData.animateDragging || appData.blindfold)
8555 if (! player.dragActive)
8557 /* Last frame in sequence is square piece is
8558 placed on, which may not match mouse exactly. */
8559 BoardSquare(x, y, &boardX, &boardY);
8560 ScreenSquare(boardX, boardY, &corner, &color);
8561 EndAnimation(&player, &corner);
8563 /* Be sure end square is redrawn */
8564 damage[0][boardY][boardX] = True;
8566 /* This prevents weird things happening with fast successive
8567 clicks which on my Sun at least can cause motion events
8568 without corresponding press/release. */
8569 player.dragActive = False;
8572 /* Handle expose event while piece being dragged */
8577 if (!player.dragActive || appData.blindfold)
8580 /* What we're doing: logically, the move hasn't been made yet,
8581 so the piece is still in it's original square. But visually
8582 it's being dragged around the board. So we erase the square
8583 that the piece is on and draw it at the last known drag point. */
8584 BlankSquare(player.startSquare.x, player.startSquare.y,
8585 player.startColor, EmptySquare, xBoardWindow, 1);
8586 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8587 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8590 #include <sys/ioctl.h>
8594 int fd, default_width;
8597 default_width = 79; // this is FICS default anyway...
8599 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8601 if (!ioctl(fd, TIOCGSIZE, &win))
8602 default_width = win.ts_cols;
8603 #elif defined(TIOCGWINSZ)
8605 if (!ioctl(fd, TIOCGWINSZ, &win))
8606 default_width = win.ws_col;
8608 return default_width;
8614 static int old_width = 0;
8615 int new_width = get_term_width();
8617 if (old_width != new_width)
8618 ics_printf("set width %d\n", new_width);
8619 old_width = new_width;
8623 NotifyFrontendLogin ()
8628 /* [AS] Arrow highlighting support */
8630 static double A_WIDTH = 5; /* Width of arrow body */
8632 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8633 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8644 return (int) (x + 0.5);
8648 SquareToPos (int rank, int file, int *x, int *y)
8651 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8652 *y = lineGap + rank * (squareSize + lineGap);
8654 *x = lineGap + file * (squareSize + lineGap);
8655 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8659 /* Draw an arrow between two points using current settings */
8661 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8664 double dx, dy, j, k, x, y;
8667 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8669 arrow[0].x = s_x + A_WIDTH + 0.5;
8672 arrow[1].x = s_x + A_WIDTH + 0.5;
8673 arrow[1].y = d_y - h;
8675 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8676 arrow[2].y = d_y - h;
8681 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8682 arrow[5].y = d_y - h;
8684 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8685 arrow[4].y = d_y - h;
8687 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8690 else if( d_y == s_y ) {
8691 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8694 arrow[0].y = s_y + A_WIDTH + 0.5;
8696 arrow[1].x = d_x - w;
8697 arrow[1].y = s_y + A_WIDTH + 0.5;
8699 arrow[2].x = d_x - w;
8700 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8705 arrow[5].x = d_x - w;
8706 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8708 arrow[4].x = d_x - w;
8709 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8712 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8715 /* [AS] Needed a lot of paper for this! :-) */
8716 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8717 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8719 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8721 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8726 arrow[0].x = Round(x - j);
8727 arrow[0].y = Round(y + j*dx);
8729 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8730 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8733 x = (double) d_x - k;
8734 y = (double) d_y - k*dy;
8737 x = (double) d_x + k;
8738 y = (double) d_y + k*dy;
8741 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8743 arrow[6].x = Round(x - j);
8744 arrow[6].y = Round(y + j*dx);
8746 arrow[2].x = Round(arrow[6].x + 2*j);
8747 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8749 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8750 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8755 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8756 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8759 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8760 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8761 // Polygon( hdc, arrow, 7 );
8765 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8768 hor = 64*s_col + 32; vert = 64*s_row + 32;
8769 for(i=0; i<= 64; i++) {
8770 damage[0][vert+6>>6][hor+6>>6] = True;
8771 damage[0][vert-6>>6][hor+6>>6] = True;
8772 damage[0][vert+6>>6][hor-6>>6] = True;
8773 damage[0][vert-6>>6][hor-6>>6] = True;
8774 hor += d_col - s_col; vert += d_row - s_row;
8778 /* [AS] Draw an arrow between two squares */
8780 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8782 int s_x, s_y, d_x, d_y;
8784 if( s_col == d_col && s_row == d_row ) {
8788 /* Get source and destination points */
8789 SquareToPos( s_row, s_col, &s_x, &s_y);
8790 SquareToPos( d_row, d_col, &d_x, &d_y);
8793 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8795 else if( d_y < s_y ) {
8796 d_y += squareSize / 2 + squareSize / 4;
8799 d_y += squareSize / 2;
8803 d_x += squareSize / 2 - squareSize / 4;
8805 else if( d_x < s_x ) {
8806 d_x += squareSize / 2 + squareSize / 4;
8809 d_x += squareSize / 2;
8812 s_x += squareSize / 2;
8813 s_y += squareSize / 2;
8816 A_WIDTH = squareSize / 14.; //[HGM] make float
8818 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8819 ArrowDamage(s_col, s_row, d_col, d_row);
8823 IsDrawArrowEnabled ()
8825 return appData.highlightMoveWithArrow && squareSize >= 32;
8829 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8831 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8832 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8836 UpdateLogos (int displ)
8838 return; // no logos in XBoard yet