2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
245 int main P((int argc, char **argv));
246 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
247 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
248 RETSIGTYPE CmailSigHandler P((int sig));
249 RETSIGTYPE IntSigHandler P((int sig));
250 RETSIGTYPE TermSizeSigHandler P((int sig));
251 void CreateGCs P((int redo));
252 void CreateAnyPieces P((void));
253 void CreateXIMPieces P((void));
254 void CreateXPMPieces P((void));
255 void CreateXPMBoard P((char *s, int n));
256 void CreatePieces P((void));
257 void CreatePieceMenus P((void));
258 Widget CreateMenuBar P((Menu *mb, int boardWidth));
259 Widget CreateButtonBar P ((MenuItem *mi));
261 char *InsertPxlSize P((char *pattern, int targetPxlSize));
262 XFontSet CreateFontSet P((char *base_fnt_lst));
264 char *FindFont P((char *pattern, int targetPxlSize));
266 void PieceMenuPopup P((Widget w, XEvent *event,
267 String *params, Cardinal *num_params));
268 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
269 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
270 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
271 u_int wreq, u_int hreq));
272 void CreateGrid P((void));
273 int EventToSquare P((int x, int limit));
274 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
275 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
276 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
277 void HandleUserMove P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AnimateUserMove P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void HandlePV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void SelectPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void StopPV P((Widget w, XEvent * event,
286 String * params, Cardinal * nParams));
287 void WhiteClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void BlackClock P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void DrawPositionProc P((Widget w, XEvent *event,
292 String *prms, Cardinal *nprms));
293 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
295 void CommentClick P((Widget w, XEvent * event,
296 String * params, Cardinal * nParams));
297 void CommentPopUp P((char *title, char *label));
298 void CommentPopDown P((void));
299 void ICSInputBoxPopUp P((void));
300 void ICSInputBoxPopDown P((void));
301 void FileNamePopUp P((char *label, char *def, char *filter,
302 FileProc proc, char *openMode));
303 void FileNamePopDown P((void));
304 void FileNameCallback P((Widget w, XtPointer client_data,
305 XtPointer call_data));
306 void FileNameAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionReplyAction P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionProc P((Widget w, XEvent *event,
311 String *prms, Cardinal *nprms));
312 void AskQuestionPopDown P((void));
313 void PromotionPopDown P((void));
314 void PromotionCallback P((Widget w, XtPointer client_data,
315 XtPointer call_data));
316 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
317 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
318 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
321 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
323 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
325 void LoadPositionProc P((Widget w, XEvent *event,
326 String *prms, Cardinal *nprms));
327 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
329 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
331 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
333 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
335 void PastePositionProc P((Widget w, XEvent *event, String *prms,
337 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
340 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
341 void SavePositionProc P((Widget w, XEvent *event,
342 String *prms, Cardinal *nprms));
343 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
344 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
346 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
347 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
350 void MachineWhiteProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeModeProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void AnalyzeFileProc P((Widget w, XEvent *event,
355 String *prms, Cardinal *nprms));
356 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
358 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void IcsClientProc P((Widget w, XEvent *event, String *prms,
362 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void EditPositionProc P((Widget w, XEvent *event,
364 String *prms, Cardinal *nprms));
365 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void EditCommentProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void IcsInputBoxProc P((Widget w, XEvent *event,
369 String *prms, Cardinal *nprms));
370 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void StopObservingProc P((Widget w, XEvent *event, String *prms,
387 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
389 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 Boolean TempBackwardActive = False;
395 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
396 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
401 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
403 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
404 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
406 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
408 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
410 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
412 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
413 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
415 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
416 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
418 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
420 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
422 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
424 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
425 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
427 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
429 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
431 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
433 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
436 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
438 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
440 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
442 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void DisplayMove P((int moveNumber));
453 void DisplayTitle P((char *title));
454 void ICSInitScript P((void));
455 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
456 void ErrorPopUp P((char *title, char *text, int modal));
457 void ErrorPopDown P((void));
458 static char *ExpandPathName P((char *path));
459 static void CreateAnimVars P((void));
460 static void DragPieceMove P((int x, int y));
461 static void DrawDragPiece P((void));
462 char *ModeToWidgetName P((GameMode mode));
463 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
477 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
478 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
479 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
480 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
481 void GameListOptionsPopDown P(());
482 void GenericPopDown P(());
483 void update_ics_width P(());
484 int get_term_width P(());
485 int CopyMemoProc P(());
486 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
487 Boolean IsDrawArrowEnabled P(());
490 * XBoard depends on Xt R4 or higher
492 int xtVersion = XtSpecificationRelease;
497 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
498 jailSquareColor, highlightSquareColor, premoveHighlightColor;
499 Pixel lowTimeWarningColor;
500 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
501 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
502 wjPieceGC, bjPieceGC, prelineGC, countGC;
503 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
504 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
505 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
506 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
507 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
508 ICSInputShell, fileNameShell, askQuestionShell;
509 Widget historyShell, evalGraphShell, gameListShell;
510 int hOffset; // [HGM] dual
511 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
512 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
513 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
515 XFontSet fontSet, clockFontSet;
518 XFontStruct *clockFontStruct;
520 Font coordFontID, countFontID;
521 XFontStruct *coordFontStruct, *countFontStruct;
522 XtAppContext appContext;
524 char *oldICSInteractionTitle;
528 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
530 Position commentX = -1, commentY = -1;
531 Dimension commentW, commentH;
532 typedef unsigned int BoardSize;
534 Boolean chessProgram;
536 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
537 int squareSize, smallLayout = 0, tinyLayout = 0,
538 marginW, marginH, // [HGM] for run-time resizing
539 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
540 ICSInputBoxUp = False, askQuestionUp = False,
541 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
542 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
543 Dimension textHeight;
544 Pixel timerForegroundPixel, timerBackgroundPixel;
545 Pixel buttonForegroundPixel, buttonBackgroundPixel;
546 char *chessDir, *programName, *programVersion,
547 *gameCopyFilename, *gamePasteFilename;
548 Boolean alwaysOnTop = False;
549 Boolean saveSettingsOnExit;
550 char *settingsFileName;
551 char *icsTextMenuString;
553 char *firstChessProgramNames;
554 char *secondChessProgramNames;
556 WindowPlacement wpMain;
557 WindowPlacement wpConsole;
558 WindowPlacement wpComment;
559 WindowPlacement wpMoveHistory;
560 WindowPlacement wpEvalGraph;
561 WindowPlacement wpEngineOutput;
562 WindowPlacement wpGameList;
563 WindowPlacement wpTags;
565 extern Widget shells[];
566 extern Boolean shellUp[];
570 Pixmap pieceBitmap[2][(int)BlackPawn];
571 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
572 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
573 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
574 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
575 Pixmap xpmBoardBitmap[2];
576 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
577 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
578 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
579 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
580 XImage *ximLightSquare, *ximDarkSquare;
583 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
584 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
586 #define White(piece) ((int)(piece) < (int)BlackPawn)
588 /* Variables for doing smooth animation. This whole thing
589 would be much easier if the board was double-buffered,
590 but that would require a fairly major rewrite. */
595 GC blitGC, pieceGC, outlineGC;
596 XPoint startSquare, prevFrame, mouseDelta;
600 int startBoardX, startBoardY;
603 /* There can be two pieces being animated at once: a player
604 can begin dragging a piece before the remote opponent has moved. */
606 static AnimState game, player;
608 /* Bitmaps for use as masks when drawing XPM pieces.
609 Need one for each black and white piece. */
610 static Pixmap xpmMask[BlackKing + 1];
612 /* This magic number is the number of intermediate frames used
613 in each half of the animation. For short moves it's reduced
614 by 1. The total number of frames will be factor * 2 + 1. */
617 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
619 MenuItem fileMenu[] = {
620 {N_("New Game Ctrl+N"), "New Game", ResetProc},
621 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
622 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
623 {"----", NULL, NothingProc},
624 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
625 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
626 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
627 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
628 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
629 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
630 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
631 {"----", NULL, NothingProc},
632 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
633 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
634 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
635 {"----", NULL, NothingProc},
636 {N_("Mail Move"), "Mail Move", MailMoveProc},
637 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
638 {"----", NULL, NothingProc},
639 {N_("Quit Ctr+Q"), "Exit", QuitProc},
643 MenuItem editMenu[] = {
644 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
645 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
646 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
647 {"----", NULL, NothingProc},
648 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
649 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
650 {"----", NULL, NothingProc},
651 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
652 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
653 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
654 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
655 {N_("Edit Book"), "Edit Book", EditBookProc},
656 {"----", NULL, NothingProc},
657 {N_("Revert Home"), "Revert", RevertProc},
658 {N_("Annotate"), "Annotate", AnnotateProc},
659 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
660 {"----", NULL, NothingProc},
661 {N_("Backward Alt+Left"), "Backward", BackwardProc},
662 {N_("Forward Alt+Right"), "Forward", ForwardProc},
663 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
664 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
668 MenuItem viewMenu[] = {
669 {N_("Flip View F2"), "Flip View", FlipViewProc},
670 {"----", NULL, NothingProc},
671 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
672 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
673 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
674 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
675 {N_("ICS text menu"), "ICStex", IcsTextProc},
676 {"----", NULL, NothingProc},
677 {N_("Tags"), "Show Tags", EditTagsProc},
678 {N_("Comments"), "Show Comments", EditCommentProc},
679 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
680 {"----", NULL, NothingProc},
681 {N_("Board..."), "Board Options", BoardOptionsProc},
682 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
686 MenuItem modeMenu[] = {
687 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
688 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
689 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
690 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
691 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
692 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
693 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
694 {N_("Training"), "Training", TrainingProc},
695 {N_("ICS Client"), "ICS Client", IcsClientProc},
696 {"----", NULL, NothingProc},
697 {N_("Machine Match"), "Machine Match", MatchProc},
698 {N_("Pause Pause"), "Pause", PauseProc},
702 MenuItem actionMenu[] = {
703 {N_("Accept F3"), "Accept", AcceptProc},
704 {N_("Decline F4"), "Decline", DeclineProc},
705 {N_("Rematch F12"), "Rematch", RematchProc},
706 {"----", NULL, NothingProc},
707 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
708 {N_("Draw F6"), "Draw", DrawProc},
709 {N_("Adjourn F7"), "Adjourn", AdjournProc},
710 {N_("Abort F8"),"Abort", AbortProc},
711 {N_("Resign F9"), "Resign", ResignProc},
712 {"----", NULL, NothingProc},
713 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
714 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
715 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
716 {"----", NULL, NothingProc},
717 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
718 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
719 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
723 MenuItem engineMenu[] = {
724 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
725 {"----", NULL, NothingProc},
726 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
727 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
728 {"----", NULL, NothingProc},
729 {N_("Hint"), "Hint", HintProc},
730 {N_("Book"), "Book", BookProc},
731 {"----", NULL, NothingProc},
732 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
733 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
737 MenuItem optionsMenu[] = {
738 #define OPTIONSDIALOG
740 {N_("General ..."), "General", OptionsProc},
742 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
743 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
744 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
745 {N_("ICS ..."), "ICS", IcsOptionsProc},
746 {N_("Match ..."), "Match", MatchOptionsProc},
747 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
748 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
749 // {N_(" ..."), "", OptionsProc},
750 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
751 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
752 {"----", NULL, NothingProc},
753 #ifndef OPTIONSDIALOG
754 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
755 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
756 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
757 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
758 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
759 {N_("Blindfold"), "Blindfold", BlindfoldProc},
760 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
762 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
764 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
765 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
766 {N_("Move Sound"), "Move Sound", MoveSoundProc},
767 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
768 {N_("One-Click Moving"), "OneClick", OneClickProc},
769 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
770 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
771 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
772 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
773 // {N_("Premove"), "Premove", PremoveProc},
774 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
775 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
776 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
777 {"----", NULL, NothingProc},
779 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
780 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
784 MenuItem helpMenu[] = {
785 {N_("Info XBoard"), "Info XBoard", InfoProc},
786 {N_("Man XBoard F1"), "Man XBoard", ManProc},
787 {"----", NULL, NothingProc},
788 {N_("About XBoard"), "About XBoard", AboutProc},
793 {N_("File"), "File", fileMenu},
794 {N_("Edit"), "Edit", editMenu},
795 {N_("View"), "View", viewMenu},
796 {N_("Mode"), "Mode", modeMenu},
797 {N_("Action"), "Action", actionMenu},
798 {N_("Engine"), "Engine", engineMenu},
799 {N_("Options"), "Options", optionsMenu},
800 {N_("Help"), "Help", helpMenu},
804 #define PAUSE_BUTTON "P"
805 MenuItem buttonBar[] = {
806 {"<<", "<<", ToStartProc},
807 {"<", "<", BackwardProc},
808 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
809 {">", ">", ForwardProc},
810 {">>", ">>", ToEndProc},
814 #define PIECE_MENU_SIZE 18
815 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
816 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
817 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
818 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
819 N_("Empty square"), N_("Clear board") },
820 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
821 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
822 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
823 N_("Empty square"), N_("Clear board") }
825 /* must be in same order as pieceMenuStrings! */
826 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
827 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
828 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
829 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
830 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
831 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
832 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
833 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
834 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
837 #define DROP_MENU_SIZE 6
838 String dropMenuStrings[DROP_MENU_SIZE] = {
839 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
841 /* must be in same order as dropMenuStrings! */
842 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
843 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
844 WhiteRook, WhiteQueen
852 DropMenuEnables dmEnables[] = {
870 { XtNborderWidth, 0 },
871 { XtNdefaultDistance, 0 },
875 { XtNborderWidth, 0 },
876 { XtNresizable, (XtArgVal) True },
880 { XtNborderWidth, 0 },
886 { XtNjustify, (XtArgVal) XtJustifyRight },
887 { XtNlabel, (XtArgVal) "..." },
888 { XtNresizable, (XtArgVal) True },
889 { XtNresize, (XtArgVal) False }
892 Arg messageArgs[] = {
893 { XtNjustify, (XtArgVal) XtJustifyLeft },
894 { XtNlabel, (XtArgVal) "..." },
895 { XtNresizable, (XtArgVal) True },
896 { XtNresize, (XtArgVal) False }
900 { XtNborderWidth, 0 },
901 { XtNjustify, (XtArgVal) XtJustifyLeft }
904 XtResource clientResources[] = {
905 { "flashCount", "flashCount", XtRInt, sizeof(int),
906 XtOffset(AppDataPtr, flashCount), XtRImmediate,
907 (XtPointer) FLASH_COUNT },
910 XrmOptionDescRec shellOptions[] = {
911 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
912 { "-flash", "flashCount", XrmoptionNoArg, "3" },
913 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
916 XtActionsRec boardActions[] = {
917 { "DrawPosition", DrawPositionProc },
918 { "HandleUserMove", HandleUserMove },
919 { "AnimateUserMove", AnimateUserMove },
920 { "HandlePV", HandlePV },
921 { "SelectPV", SelectPV },
922 { "StopPV", StopPV },
923 { "FileNameAction", FileNameAction },
924 { "AskQuestionProc", AskQuestionProc },
925 { "AskQuestionReplyAction", AskQuestionReplyAction },
926 { "PieceMenuPopup", PieceMenuPopup },
927 { "WhiteClock", WhiteClock },
928 { "BlackClock", BlackClock },
929 { "ResetProc", ResetProc },
930 { "NewVariantProc", NewVariantProc },
931 { "LoadGameProc", LoadGameProc },
932 { "LoadNextGameProc", LoadNextGameProc },
933 { "LoadPrevGameProc", LoadPrevGameProc },
934 { "LoadSelectedProc", LoadSelectedProc },
935 { "SetFilterProc", SetFilterProc },
936 { "ReloadGameProc", ReloadGameProc },
937 { "LoadPositionProc", LoadPositionProc },
938 { "LoadNextPositionProc", LoadNextPositionProc },
939 { "LoadPrevPositionProc", LoadPrevPositionProc },
940 { "ReloadPositionProc", ReloadPositionProc },
941 { "CopyPositionProc", CopyPositionProc },
942 { "PastePositionProc", PastePositionProc },
943 { "CopyGameProc", CopyGameProc },
944 { "CopyGameListProc", CopyGameListProc },
945 { "PasteGameProc", PasteGameProc },
946 { "SaveGameProc", SaveGameProc },
947 { "SavePositionProc", SavePositionProc },
948 { "MailMoveProc", MailMoveProc },
949 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
950 { "QuitProc", QuitProc },
951 { "MachineWhiteProc", MachineWhiteProc },
952 { "MachineBlackProc", MachineBlackProc },
953 { "AnalysisModeProc", AnalyzeModeProc },
954 { "AnalyzeFileProc", AnalyzeFileProc },
955 { "TwoMachinesProc", TwoMachinesProc },
956 { "IcsClientProc", IcsClientProc },
957 { "EditGameProc", EditGameProc },
958 { "EditPositionProc", EditPositionProc },
959 { "TrainingProc", EditPositionProc },
960 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
961 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
962 { "ShowGameListProc", ShowGameListProc },
963 { "ShowMoveListProc", HistoryShowProc},
964 { "EditTagsProc", EditTagsProc },
965 { "EditBookProc", EditBookProc },
966 { "EditCommentProc", EditCommentProc },
967 { "IcsInputBoxProc", IcsInputBoxProc },
968 { "PauseProc", PauseProc },
969 { "AcceptProc", AcceptProc },
970 { "DeclineProc", DeclineProc },
971 { "RematchProc", RematchProc },
972 { "CallFlagProc", CallFlagProc },
973 { "DrawProc", DrawProc },
974 { "AdjournProc", AdjournProc },
975 { "AbortProc", AbortProc },
976 { "ResignProc", ResignProc },
977 { "AdjuWhiteProc", AdjuWhiteProc },
978 { "AdjuBlackProc", AdjuBlackProc },
979 { "AdjuDrawProc", AdjuDrawProc },
980 { "TypeInProc", TypeInProc },
981 { "EnterKeyProc", EnterKeyProc },
982 { "UpKeyProc", UpKeyProc },
983 { "DownKeyProc", DownKeyProc },
984 { "StopObservingProc", StopObservingProc },
985 { "StopExaminingProc", StopExaminingProc },
986 { "UploadProc", UploadProc },
987 { "BackwardProc", BackwardProc },
988 { "ForwardProc", ForwardProc },
989 { "TempBackwardProc", TempBackwardProc },
990 { "TempForwardProc", TempForwardProc },
991 { "ToStartProc", ToStartProc },
992 { "ToEndProc", ToEndProc },
993 { "RevertProc", RevertProc },
994 { "AnnotateProc", AnnotateProc },
995 { "TruncateGameProc", TruncateGameProc },
996 { "MoveNowProc", MoveNowProc },
997 { "RetractMoveProc", RetractMoveProc },
998 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
999 { "UciMenuProc", (XtActionProc) UciMenuProc },
1000 { "TimeControlProc", (XtActionProc) TimeControlProc },
1001 { "FlipViewProc", FlipViewProc },
1002 { "PonderNextMoveProc", PonderNextMoveProc },
1003 #ifndef OPTIONSDIALOG
1004 { "AlwaysQueenProc", AlwaysQueenProc },
1005 { "AnimateDraggingProc", AnimateDraggingProc },
1006 { "AnimateMovingProc", AnimateMovingProc },
1007 { "AutoflagProc", AutoflagProc },
1008 { "AutoflipProc", AutoflipProc },
1009 { "BlindfoldProc", BlindfoldProc },
1010 { "FlashMovesProc", FlashMovesProc },
1012 { "HighlightDraggingProc", HighlightDraggingProc },
1014 { "HighlightLastMoveProc", HighlightLastMoveProc },
1015 // { "IcsAlarmProc", IcsAlarmProc },
1016 { "MoveSoundProc", MoveSoundProc },
1017 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1018 { "PopupExitMessageProc", PopupExitMessageProc },
1019 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1020 // { "PremoveProc", PremoveProc },
1021 { "ShowCoordsProc", ShowCoordsProc },
1022 { "ShowThinkingProc", ShowThinkingProc },
1023 { "HideThinkingProc", HideThinkingProc },
1024 { "TestLegalityProc", TestLegalityProc },
1026 { "SaveSettingsProc", SaveSettingsProc },
1027 { "SaveOnExitProc", SaveOnExitProc },
1028 { "InfoProc", InfoProc },
1029 { "ManProc", ManProc },
1030 { "HintProc", HintProc },
1031 { "BookProc", BookProc },
1032 { "AboutGameProc", AboutGameProc },
1033 { "AboutProc", AboutProc },
1034 { "DebugProc", DebugProc },
1035 { "NothingProc", NothingProc },
1036 { "CommentClick", (XtActionProc) CommentClick },
1037 { "CommentPopDown", (XtActionProc) CommentPopDown },
1038 { "TagsPopDown", (XtActionProc) TagsPopDown },
1039 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1040 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1041 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1042 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1043 { "GameListPopDown", (XtActionProc) GameListPopDown },
1044 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1045 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1046 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1047 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1048 { "GenericPopDown", (XtActionProc) GenericPopDown },
1049 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1050 { "SelectMove", (XtActionProc) SelectMove },
1053 char globalTranslations[] =
1054 ":<Key>F9: ResignProc() \n \
1055 :Ctrl<Key>n: ResetProc() \n \
1056 :Meta<Key>V: NewVariantProc() \n \
1057 :Ctrl<Key>o: LoadGameProc() \n \
1058 :Meta<Key>Next: LoadNextGameProc() \n \
1059 :Meta<Key>Prior: LoadPrevGameProc() \n \
1060 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1061 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1062 :Ctrl<Key>s: SaveGameProc() \n \
1063 :Ctrl<Key>c: CopyGameProc() \n \
1064 :Ctrl<Key>v: PasteGameProc() \n \
1065 :Ctrl<Key>O: LoadPositionProc() \n \
1066 :Shift<Key>Next: LoadNextPositionProc() \n \
1067 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1068 :Ctrl<Key>S: SavePositionProc() \n \
1069 :Ctrl<Key>C: CopyPositionProc() \n \
1070 :Ctrl<Key>V: PastePositionProc() \n \
1071 :Ctrl<Key>q: QuitProc() \n \
1072 :Ctrl<Key>w: MachineWhiteProc() \n \
1073 :Ctrl<Key>b: MachineBlackProc() \n \
1074 :Ctrl<Key>t: TwoMachinesProc() \n \
1075 :Ctrl<Key>a: AnalysisModeProc() \n \
1076 :Ctrl<Key>g: AnalyzeFileProc() \n \
1077 :Ctrl<Key>e: EditGameProc() \n \
1078 :Ctrl<Key>E: EditPositionProc() \n \
1079 :Meta<Key>O: EngineOutputProc() \n \
1080 :Meta<Key>E: EvalGraphProc() \n \
1081 :Meta<Key>G: ShowGameListProc() \n \
1082 :Meta<Key>H: ShowMoveListProc() \n \
1083 :<Key>Pause: PauseProc() \n \
1084 :<Key>F3: AcceptProc() \n \
1085 :<Key>F4: DeclineProc() \n \
1086 :<Key>F12: RematchProc() \n \
1087 :<Key>F5: CallFlagProc() \n \
1088 :<Key>F6: DrawProc() \n \
1089 :<Key>F7: AdjournProc() \n \
1090 :<Key>F8: AbortProc() \n \
1091 :<Key>F10: StopObservingProc() \n \
1092 :<Key>F11: StopExaminingProc() \n \
1093 :Meta Ctrl<Key>F12: DebugProc() \n \
1094 :Meta<Key>End: ToEndProc() \n \
1095 :Meta<Key>Right: ForwardProc() \n \
1096 :Meta<Key>Home: ToStartProc() \n \
1097 :Meta<Key>Left: BackwardProc() \n \
1098 :<Key>Left: BackwardProc() \n \
1099 :<Key>Right: ForwardProc() \n \
1100 :<Key>Home: RevertProc() \n \
1101 :<Key>End: TruncateGameProc() \n \
1102 :Ctrl<Key>m: MoveNowProc() \n \
1103 :Ctrl<Key>x: RetractMoveProc() \n \
1104 :Meta<Key>J: EngineMenuProc() \n \
1105 :Meta<Key>U: UciMenuProc() \n \
1106 :Meta<Key>T: TimeControlProc() \n \
1107 :Ctrl<Key>P: PonderNextMoveProc() \n "
1108 #ifndef OPTIONSDIALOG
1110 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1111 :Ctrl<Key>F: AutoflagProc() \n \
1112 :Ctrl<Key>A: AnimateMovingProc() \n \
1113 :Ctrl<Key>L: TestLegalityProc() \n \
1114 :Ctrl<Key>H: HideThinkingProc() \n "
1117 :<Key>F1: ManProc() \n \
1118 :<Key>F2: FlipViewProc() \n \
1119 :<KeyDown>Return: TempBackwardProc() \n \
1120 :<KeyUp>Return: TempForwardProc() \n";
1122 char boardTranslations[] =
1123 "<Btn1Down>: HandleUserMove(0) \n \
1124 Shift<Btn1Up>: HandleUserMove(1) \n \
1125 <Btn1Up>: HandleUserMove(0) \n \
1126 <Btn1Motion>: AnimateUserMove() \n \
1127 <Btn3Motion>: HandlePV() \n \
1128 <Btn2Motion>: HandlePV() \n \
1129 <Btn3Up>: PieceMenuPopup(menuB) \n \
1130 <Btn2Up>: PieceMenuPopup(menuB) \n \
1131 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1132 PieceMenuPopup(menuB) \n \
1133 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1134 PieceMenuPopup(menuW) \n \
1135 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1136 PieceMenuPopup(menuW) \n \
1137 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1138 PieceMenuPopup(menuB) \n";
1140 char whiteTranslations[] =
1141 "Shift<BtnDown>: WhiteClock(1)\n \
1142 <BtnDown>: WhiteClock(0)\n";
1143 char blackTranslations[] =
1144 "Shift<BtnDown>: BlackClock(1)\n \
1145 <BtnDown>: BlackClock(0)\n";
1147 char ICSInputTranslations[] =
1148 "<Key>Up: UpKeyProc() \n "
1149 "<Key>Down: DownKeyProc() \n "
1150 "<Key>Return: EnterKeyProc() \n";
1152 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1153 // as the widget is destroyed before the up-click can call extend-end
1154 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1156 String xboardResources[] = {
1157 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1158 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1159 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1164 /* Max possible square size */
1165 #define MAXSQSIZE 256
1167 static int xpm_avail[MAXSQSIZE];
1169 #ifdef HAVE_DIR_STRUCT
1171 /* Extract piece size from filename */
1173 xpm_getsize (char *name, int len, char *ext)
1181 if ((p=strchr(name, '.')) == NULL ||
1182 StrCaseCmp(p+1, ext) != 0)
1188 while (*p && isdigit(*p))
1195 /* Setup xpm_avail */
1197 xpm_getavail (char *dirname, char *ext)
1203 for (i=0; i<MAXSQSIZE; ++i)
1206 if (appData.debugMode)
1207 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1209 dir = opendir(dirname);
1212 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1213 programName, dirname);
1217 while ((ent=readdir(dir)) != NULL) {
1218 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1219 if (i > 0 && i < MAXSQSIZE)
1229 xpm_print_avail (FILE *fp, char *ext)
1233 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1234 for (i=1; i<MAXSQSIZE; ++i) {
1240 /* Return XPM piecesize closest to size */
1242 xpm_closest_to (char *dirname, int size, char *ext)
1245 int sm_diff = MAXSQSIZE;
1249 xpm_getavail(dirname, ext);
1251 if (appData.debugMode)
1252 xpm_print_avail(stderr, ext);
1254 for (i=1; i<MAXSQSIZE; ++i) {
1257 diff = (diff<0) ? -diff : diff;
1258 if (diff < sm_diff) {
1266 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1272 #else /* !HAVE_DIR_STRUCT */
1273 /* If we are on a system without a DIR struct, we can't
1274 read the directory, so we can't collect a list of
1275 filenames, etc., so we can't do any size-fitting. */
1277 xpm_closest_to (char *dirname, int size, char *ext)
1279 fprintf(stderr, _("\
1280 Warning: No DIR structure found on this system --\n\
1281 Unable to autosize for XPM/XIM pieces.\n\
1282 Please report this error to %s.\n\
1283 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1286 #endif /* HAVE_DIR_STRUCT */
1288 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1289 "magenta", "cyan", "white" };
1293 TextColors textColors[(int)NColorClasses];
1295 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1297 parse_color (char *str, int which)
1299 char *p, buf[100], *d;
1302 if (strlen(str) > 99) /* watch bounds on buf */
1307 for (i=0; i<which; ++i) {
1314 /* Could be looking at something like:
1316 .. in which case we want to stop on a comma also */
1317 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1321 return -1; /* Use default for empty field */
1324 if (which == 2 || isdigit(*p))
1327 while (*p && isalpha(*p))
1332 for (i=0; i<8; ++i) {
1333 if (!StrCaseCmp(buf, cnames[i]))
1334 return which? (i+40) : (i+30);
1336 if (!StrCaseCmp(buf, "default")) return -1;
1338 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1343 parse_cpair (ColorClass cc, char *str)
1345 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1346 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1351 /* bg and attr are optional */
1352 textColors[(int)cc].bg = parse_color(str, 1);
1353 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1354 textColors[(int)cc].attr = 0;
1360 /* Arrange to catch delete-window events */
1361 Atom wm_delete_window;
1363 CatchDeleteWindow (Widget w, String procname)
1366 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1367 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1368 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1375 XtSetArg(args[0], XtNiconic, False);
1376 XtSetValues(shellWidget, args, 1);
1378 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1381 //---------------------------------------------------------------------------------------------------------
1382 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1385 #define CW_USEDEFAULT (1<<31)
1386 #define ICS_TEXT_MENU_SIZE 90
1387 #define DEBUG_FILE "xboard.debug"
1388 #define SetCurrentDirectory chdir
1389 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1393 // these two must some day move to frontend.h, when they are implemented
1394 Boolean GameListIsUp();
1396 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1399 // front-end part of option handling
1401 // [HGM] This platform-dependent table provides the location for storing the color info
1402 extern char *crWhite, * crBlack;
1406 &appData.whitePieceColor,
1407 &appData.blackPieceColor,
1408 &appData.lightSquareColor,
1409 &appData.darkSquareColor,
1410 &appData.highlightSquareColor,
1411 &appData.premoveHighlightColor,
1412 &appData.lowTimeWarningColor,
1423 // [HGM] font: keep a font for each square size, even non-stndard ones
1424 #define NUM_SIZES 18
1425 #define MAX_SIZE 130
1426 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1427 char *fontTable[NUM_FONTS][MAX_SIZE];
1430 ParseFont (char *name, int number)
1431 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1433 if(sscanf(name, "size%d:", &size)) {
1434 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1435 // defer processing it until we know if it matches our board size
1436 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1437 fontTable[number][size] = strdup(strchr(name, ':')+1);
1438 fontValid[number][size] = True;
1443 case 0: // CLOCK_FONT
1444 appData.clockFont = strdup(name);
1446 case 1: // MESSAGE_FONT
1447 appData.font = strdup(name);
1449 case 2: // COORD_FONT
1450 appData.coordFont = strdup(name);
1455 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1460 { // only 2 fonts currently
1461 appData.clockFont = CLOCK_FONT_NAME;
1462 appData.coordFont = COORD_FONT_NAME;
1463 appData.font = DEFAULT_FONT_NAME;
1468 { // no-op, until we identify the code for this already in XBoard and move it here
1472 ParseColor (int n, char *name)
1473 { // in XBoard, just copy the color-name string
1474 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1478 ParseTextAttribs (ColorClass cc, char *s)
1480 (&appData.colorShout)[cc] = strdup(s);
1484 ParseBoardSize (void *addr, char *name)
1486 appData.boardSize = strdup(name);
1491 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1495 SetCommPortDefaults ()
1496 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1499 // [HGM] args: these three cases taken out to stay in front-end
1501 SaveFontArg (FILE *f, ArgDescriptor *ad)
1504 int i, n = (int)(intptr_t)ad->argLoc;
1506 case 0: // CLOCK_FONT
1507 name = appData.clockFont;
1509 case 1: // MESSAGE_FONT
1510 name = appData.font;
1512 case 2: // COORD_FONT
1513 name = appData.coordFont;
1518 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1519 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1520 fontTable[n][squareSize] = strdup(name);
1521 fontValid[n][squareSize] = True;
1524 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1525 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1530 { // nothing to do, as the sounds are at all times represented by their text-string names already
1534 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1535 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1536 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1540 SaveColor (FILE *f, ArgDescriptor *ad)
1541 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1542 if(colorVariable[(int)(intptr_t)ad->argLoc])
1543 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1547 SaveBoardSize (FILE *f, char *name, void *addr)
1548 { // wrapper to shield back-end from BoardSize & sizeInfo
1549 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1553 ParseCommPortSettings (char *s)
1554 { // no such option in XBoard (yet)
1557 extern Widget engineOutputShell;
1560 GetActualPlacement (Widget wg, WindowPlacement *wp)
1570 XtSetArg(args[i], XtNx, &x); i++;
1571 XtSetArg(args[i], XtNy, &y); i++;
1572 XtSetArg(args[i], XtNwidth, &w); i++;
1573 XtSetArg(args[i], XtNheight, &h); i++;
1574 XtGetValues(wg, args, i);
1583 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1584 // In XBoard this will have to wait until awareness of window parameters is implemented
1585 GetActualPlacement(shellWidget, &wpMain);
1586 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1587 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1588 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1589 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1590 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1591 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1595 PrintCommPortSettings (FILE *f, char *name)
1596 { // This option does not exist in XBoard
1600 MySearchPath (char *installDir, char *name, char *fullname)
1601 { // just append installDir and name. Perhaps ExpandPath should be used here?
1602 name = ExpandPathName(name);
1603 if(name && name[0] == '/')
1604 safeStrCpy(fullname, name, MSG_SIZ );
1606 sprintf(fullname, "%s%c%s", installDir, '/', name);
1612 MyGetFullPathName (char *name, char *fullname)
1613 { // should use ExpandPath?
1614 name = ExpandPathName(name);
1615 safeStrCpy(fullname, name, MSG_SIZ );
1620 EnsureOnScreen (int *x, int *y, int minX, int minY)
1627 { // [HGM] args: allows testing if main window is realized from back-end
1628 return xBoardWindow != 0;
1632 PopUpStartupDialog ()
1633 { // start menu not implemented in XBoard
1637 ConvertToLine (int argc, char **argv)
1639 static char line[128*1024], buf[1024];
1643 for(i=1; i<argc; i++)
1645 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1646 && argv[i][0] != '{' )
1647 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1649 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1650 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1653 line[strlen(line)-1] = NULLCHAR;
1657 //--------------------------------------------------------------------------------------------
1659 extern Boolean twoBoards, partnerUp;
1662 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1664 #define BoardSize int
1666 InitDrawingSizes (BoardSize boardSize, int flags)
1667 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1668 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1670 XtGeometryResult gres;
1672 static Dimension oldWidth, oldHeight;
1673 static VariantClass oldVariant;
1674 static int oldDual = -1, oldMono = -1;
1676 if(!formWidget) return;
1678 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1679 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1680 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1682 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1684 * Enable shell resizing.
1686 shellArgs[0].value = (XtArgVal) &w;
1687 shellArgs[1].value = (XtArgVal) &h;
1688 XtGetValues(shellWidget, shellArgs, 2);
1690 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1691 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1692 XtSetValues(shellWidget, &shellArgs[2], 4);
1694 XtSetArg(args[0], XtNdefaultDistance, &sep);
1695 XtGetValues(formWidget, args, 1);
1697 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1699 hOffset = boardWidth + 10;
1700 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1701 secondSegments[i] = gridSegments[i];
1702 secondSegments[i].x1 += hOffset;
1703 secondSegments[i].x2 += hOffset;
1706 XtSetArg(args[0], XtNwidth, boardWidth);
1707 XtSetArg(args[1], XtNheight, boardHeight);
1708 XtSetValues(boardWidget, args, 2);
1710 timerWidth = (boardWidth - sep) / 2;
1711 XtSetArg(args[0], XtNwidth, timerWidth);
1712 XtSetValues(whiteTimerWidget, args, 1);
1713 XtSetValues(blackTimerWidget, args, 1);
1715 XawFormDoLayout(formWidget, False);
1717 if (appData.titleInWindow) {
1719 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1720 XtSetArg(args[i], XtNheight, &h); i++;
1721 XtGetValues(titleWidget, args, i);
1723 w = boardWidth - 2*bor;
1725 XtSetArg(args[0], XtNwidth, &w);
1726 XtGetValues(menuBarWidget, args, 1);
1727 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1730 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1731 if (gres != XtGeometryYes && appData.debugMode) {
1733 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1734 programName, gres, w, h, wr, hr);
1738 XawFormDoLayout(formWidget, True);
1741 * Inhibit shell resizing.
1743 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1744 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1745 shellArgs[4].value = shellArgs[2].value = w;
1746 shellArgs[5].value = shellArgs[3].value = h;
1747 XtSetValues(shellWidget, &shellArgs[0], 6);
1750 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1753 if(gameInfo.variant != oldVariant) { // and only if variant changed
1756 for(i=0; i<4; i++) {
1758 for(p=0; p<=(int)WhiteKing; p++)
1759 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1760 if(gameInfo.variant == VariantShogi) {
1761 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1762 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1763 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1764 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1765 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1768 if(gameInfo.variant == VariantGothic) {
1769 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1772 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1773 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1774 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1777 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1778 for(p=0; p<=(int)WhiteKing; p++)
1779 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1780 if(gameInfo.variant == VariantShogi) {
1781 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1782 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1783 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1784 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1785 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1788 if(gameInfo.variant == VariantGothic) {
1789 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1792 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1793 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1794 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1799 for(i=0; i<2; i++) {
1801 for(p=0; p<=(int)WhiteKing; p++)
1802 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1803 if(gameInfo.variant == VariantShogi) {
1804 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1805 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1806 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1807 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1808 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1811 if(gameInfo.variant == VariantGothic) {
1812 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1815 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1816 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1817 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1821 oldMono = -10; // kludge to force recreation of animation masks
1824 if(appData.monoMode != oldMono)
1827 oldMono = appData.monoMode;
1832 ParseIcsTextColors ()
1833 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1834 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1835 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1836 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1837 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1838 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1839 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1840 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1841 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1842 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1843 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1845 if (appData.colorize) {
1847 _("%s: can't parse color names; disabling colorization\n"),
1850 appData.colorize = FALSE;
1856 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1857 XrmValue vFrom, vTo;
1858 int forceMono = False;
1860 if (!appData.monoMode) {
1861 vFrom.addr = (caddr_t) appData.lightSquareColor;
1862 vFrom.size = strlen(appData.lightSquareColor);
1863 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1864 if (vTo.addr == NULL) {
1865 appData.monoMode = True;
1868 lightSquareColor = *(Pixel *) vTo.addr;
1871 if (!appData.monoMode) {
1872 vFrom.addr = (caddr_t) appData.darkSquareColor;
1873 vFrom.size = strlen(appData.darkSquareColor);
1874 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1875 if (vTo.addr == NULL) {
1876 appData.monoMode = True;
1879 darkSquareColor = *(Pixel *) vTo.addr;
1882 if (!appData.monoMode) {
1883 vFrom.addr = (caddr_t) appData.whitePieceColor;
1884 vFrom.size = strlen(appData.whitePieceColor);
1885 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1886 if (vTo.addr == NULL) {
1887 appData.monoMode = True;
1890 whitePieceColor = *(Pixel *) vTo.addr;
1893 if (!appData.monoMode) {
1894 vFrom.addr = (caddr_t) appData.blackPieceColor;
1895 vFrom.size = strlen(appData.blackPieceColor);
1896 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1897 if (vTo.addr == NULL) {
1898 appData.monoMode = True;
1901 blackPieceColor = *(Pixel *) vTo.addr;
1905 if (!appData.monoMode) {
1906 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1907 vFrom.size = strlen(appData.highlightSquareColor);
1908 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1909 if (vTo.addr == NULL) {
1910 appData.monoMode = True;
1913 highlightSquareColor = *(Pixel *) vTo.addr;
1917 if (!appData.monoMode) {
1918 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1919 vFrom.size = strlen(appData.premoveHighlightColor);
1920 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1921 if (vTo.addr == NULL) {
1922 appData.monoMode = True;
1925 premoveHighlightColor = *(Pixel *) vTo.addr;
1933 { // [HGM] taken out of main
1935 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1936 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1937 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1939 if (appData.bitmapDirectory[0] != NULLCHAR) {
1943 CreateXPMBoard(appData.liteBackTextureFile, 1);
1944 CreateXPMBoard(appData.darkBackTextureFile, 0);
1948 /* Create regular pieces */
1949 if (!useImages) CreatePieces();
1954 main (int argc, char **argv)
1956 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1957 XSetWindowAttributes window_attributes;
1959 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1960 XrmValue vFrom, vTo;
1961 XtGeometryResult gres;
1964 int forceMono = False;
1966 srandom(time(0)); // [HGM] book: make random truly random
1968 setbuf(stdout, NULL);
1969 setbuf(stderr, NULL);
1972 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1973 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1977 programName = strrchr(argv[0], '/');
1978 if (programName == NULL)
1979 programName = argv[0];
1984 XtSetLanguageProc(NULL, NULL, NULL);
1985 bindtextdomain(PACKAGE, LOCALEDIR);
1986 textdomain(PACKAGE);
1990 XtAppInitialize(&appContext, "XBoard", shellOptions,
1991 XtNumber(shellOptions),
1992 &argc, argv, xboardResources, NULL, 0);
1993 appData.boardSize = "";
1994 InitAppData(ConvertToLine(argc, argv));
1996 if (p == NULL) p = "/tmp";
1997 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1998 gameCopyFilename = (char*) malloc(i);
1999 gamePasteFilename = (char*) malloc(i);
2000 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2001 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2003 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2004 clientResources, XtNumber(clientResources),
2007 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2008 static char buf[MSG_SIZ];
2009 EscapeExpand(buf, appData.firstInitString);
2010 appData.firstInitString = strdup(buf);
2011 EscapeExpand(buf, appData.secondInitString);
2012 appData.secondInitString = strdup(buf);
2013 EscapeExpand(buf, appData.firstComputerString);
2014 appData.firstComputerString = strdup(buf);
2015 EscapeExpand(buf, appData.secondComputerString);
2016 appData.secondComputerString = strdup(buf);
2019 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2022 if (chdir(chessDir) != 0) {
2023 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2029 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2030 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2031 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2032 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2035 setbuf(debugFP, NULL);
2039 if (appData.debugMode) {
2040 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2044 /* [HGM,HR] make sure board size is acceptable */
2045 if(appData.NrFiles > BOARD_FILES ||
2046 appData.NrRanks > BOARD_RANKS )
2047 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2050 /* This feature does not work; animation needs a rewrite */
2051 appData.highlightDragging = FALSE;
2055 xDisplay = XtDisplay(shellWidget);
2056 xScreen = DefaultScreen(xDisplay);
2057 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2059 gameInfo.variant = StringToVariant(appData.variant);
2060 InitPosition(FALSE);
2063 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2065 if (isdigit(appData.boardSize[0])) {
2066 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2067 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2068 &fontPxlSize, &smallLayout, &tinyLayout);
2070 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2071 programName, appData.boardSize);
2075 /* Find some defaults; use the nearest known size */
2076 SizeDefaults *szd, *nearest;
2077 int distance = 99999;
2078 nearest = szd = sizeDefaults;
2079 while (szd->name != NULL) {
2080 if (abs(szd->squareSize - squareSize) < distance) {
2082 distance = abs(szd->squareSize - squareSize);
2083 if (distance == 0) break;
2087 if (i < 2) lineGap = nearest->lineGap;
2088 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2089 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2090 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2091 if (i < 6) smallLayout = nearest->smallLayout;
2092 if (i < 7) tinyLayout = nearest->tinyLayout;
2095 SizeDefaults *szd = sizeDefaults;
2096 if (*appData.boardSize == NULLCHAR) {
2097 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2098 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2101 if (szd->name == NULL) szd--;
2102 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2104 while (szd->name != NULL &&
2105 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2106 if (szd->name == NULL) {
2107 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2108 programName, appData.boardSize);
2112 squareSize = szd->squareSize;
2113 lineGap = szd->lineGap;
2114 clockFontPxlSize = szd->clockFontPxlSize;
2115 coordFontPxlSize = szd->coordFontPxlSize;
2116 fontPxlSize = szd->fontPxlSize;
2117 smallLayout = szd->smallLayout;
2118 tinyLayout = szd->tinyLayout;
2119 // [HGM] font: use defaults from settings file if available and not overruled
2121 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2122 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2123 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2124 appData.font = fontTable[MESSAGE_FONT][squareSize];
2125 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2126 appData.coordFont = fontTable[COORD_FONT][squareSize];
2128 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2129 if (strlen(appData.pixmapDirectory) > 0) {
2130 p = ExpandPathName(appData.pixmapDirectory);
2132 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2133 appData.pixmapDirectory);
2136 if (appData.debugMode) {
2137 fprintf(stderr, _("\
2138 XBoard square size (hint): %d\n\
2139 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2141 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2142 if (appData.debugMode) {
2143 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2146 defaultLineGap = lineGap;
2147 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2149 /* [HR] height treated separately (hacked) */
2150 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2151 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2152 if (appData.showJail == 1) {
2153 /* Jail on top and bottom */
2154 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2155 XtSetArg(boardArgs[2], XtNheight,
2156 boardHeight + 2*(lineGap + squareSize));
2157 } else if (appData.showJail == 2) {
2159 XtSetArg(boardArgs[1], XtNwidth,
2160 boardWidth + 2*(lineGap + squareSize));
2161 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2164 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2165 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2169 * Determine what fonts to use.
2172 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2173 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2174 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2175 fontSet = CreateFontSet(appData.font);
2176 clockFontSet = CreateFontSet(appData.clockFont);
2178 /* For the coordFont, use the 0th font of the fontset. */
2179 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2180 XFontStruct **font_struct_list;
2181 XFontSetExtents *fontSize;
2182 char **font_name_list;
2183 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2184 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2185 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2186 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
2187 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
2190 appData.font = FindFont(appData.font, fontPxlSize);
2191 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2192 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2193 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2194 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2195 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2196 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2198 countFontID = coordFontID; // [HGM] holdings
2199 countFontStruct = coordFontStruct;
2201 xdb = XtDatabase(xDisplay);
2203 XrmPutLineResource(&xdb, "*international: True");
2204 vTo.size = sizeof(XFontSet);
2205 vTo.addr = (XtPointer) &fontSet;
2206 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2208 XrmPutStringResource(&xdb, "*font", appData.font);
2212 * Detect if there are not enough colors available and adapt.
2214 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2215 appData.monoMode = True;
2218 forceMono = MakeColors();
2221 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2223 appData.monoMode = True;
2226 if (appData.lowTimeWarning && !appData.monoMode) {
2227 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2228 vFrom.size = strlen(appData.lowTimeWarningColor);
2229 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2230 if (vTo.addr == NULL)
2231 appData.monoMode = True;
2233 lowTimeWarningColor = *(Pixel *) vTo.addr;
2236 if (appData.monoMode && appData.debugMode) {
2237 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2238 (unsigned long) XWhitePixel(xDisplay, xScreen),
2239 (unsigned long) XBlackPixel(xDisplay, xScreen));
2242 ParseIcsTextColors();
2243 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2244 textColors[ColorNone].attr = 0;
2246 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2252 layoutName = "tinyLayout";
2253 } else if (smallLayout) {
2254 layoutName = "smallLayout";
2256 layoutName = "normalLayout";
2258 /* Outer layoutWidget is there only to provide a name for use in
2259 resources that depend on the layout style */
2261 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2262 layoutArgs, XtNumber(layoutArgs));
2264 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2265 formArgs, XtNumber(formArgs));
2266 XtSetArg(args[0], XtNdefaultDistance, &sep);
2267 XtGetValues(formWidget, args, 1);
2270 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2271 XtSetArg(args[0], XtNtop, XtChainTop);
2272 XtSetArg(args[1], XtNbottom, XtChainTop);
2273 XtSetArg(args[2], XtNright, XtChainLeft);
2274 XtSetValues(menuBarWidget, args, 3);
2276 widgetList[j++] = whiteTimerWidget =
2277 XtCreateWidget("whiteTime", labelWidgetClass,
2278 formWidget, timerArgs, XtNumber(timerArgs));
2280 XtSetArg(args[0], XtNfontSet, clockFontSet);
2282 XtSetArg(args[0], XtNfont, clockFontStruct);
2284 XtSetArg(args[1], XtNtop, XtChainTop);
2285 XtSetArg(args[2], XtNbottom, XtChainTop);
2286 XtSetValues(whiteTimerWidget, args, 3);
2288 widgetList[j++] = blackTimerWidget =
2289 XtCreateWidget("blackTime", labelWidgetClass,
2290 formWidget, timerArgs, XtNumber(timerArgs));
2292 XtSetArg(args[0], XtNfontSet, clockFontSet);
2294 XtSetArg(args[0], XtNfont, clockFontStruct);
2296 XtSetArg(args[1], XtNtop, XtChainTop);
2297 XtSetArg(args[2], XtNbottom, XtChainTop);
2298 XtSetValues(blackTimerWidget, args, 3);
2300 if (appData.titleInWindow) {
2301 widgetList[j++] = titleWidget =
2302 XtCreateWidget("title", labelWidgetClass, formWidget,
2303 titleArgs, XtNumber(titleArgs));
2304 XtSetArg(args[0], XtNtop, XtChainTop);
2305 XtSetArg(args[1], XtNbottom, XtChainTop);
2306 XtSetValues(titleWidget, args, 2);
2309 if (appData.showButtonBar) {
2310 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2311 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2312 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2313 XtSetArg(args[2], XtNtop, XtChainTop);
2314 XtSetArg(args[3], XtNbottom, XtChainTop);
2315 XtSetValues(buttonBarWidget, args, 4);
2318 widgetList[j++] = messageWidget =
2319 XtCreateWidget("message", labelWidgetClass, formWidget,
2320 messageArgs, XtNumber(messageArgs));
2321 XtSetArg(args[0], XtNtop, XtChainTop);
2322 XtSetArg(args[1], XtNbottom, XtChainTop);
2323 XtSetValues(messageWidget, args, 2);
2325 widgetList[j++] = boardWidget =
2326 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2327 XtNumber(boardArgs));
2329 XtManageChildren(widgetList, j);
2331 timerWidth = (boardWidth - sep) / 2;
2332 XtSetArg(args[0], XtNwidth, timerWidth);
2333 XtSetValues(whiteTimerWidget, args, 1);
2334 XtSetValues(blackTimerWidget, args, 1);
2336 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2337 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2338 XtGetValues(whiteTimerWidget, args, 2);
2340 if (appData.showButtonBar) {
2341 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2342 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2343 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2347 * formWidget uses these constraints but they are stored
2351 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2352 XtSetValues(menuBarWidget, args, i);
2353 if (appData.titleInWindow) {
2356 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2357 XtSetValues(whiteTimerWidget, args, i);
2359 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2360 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2361 XtSetValues(blackTimerWidget, args, i);
2363 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2364 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2365 XtSetValues(titleWidget, args, i);
2367 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2368 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2369 XtSetValues(messageWidget, args, i);
2370 if (appData.showButtonBar) {
2372 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2373 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2374 XtSetValues(buttonBarWidget, args, i);
2378 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2379 XtSetValues(whiteTimerWidget, args, i);
2381 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2382 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2383 XtSetValues(blackTimerWidget, args, i);
2385 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2386 XtSetValues(titleWidget, args, i);
2388 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2389 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2390 XtSetValues(messageWidget, args, i);
2391 if (appData.showButtonBar) {
2393 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2394 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2395 XtSetValues(buttonBarWidget, args, i);
2400 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2401 XtSetValues(whiteTimerWidget, args, i);
2403 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2404 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2405 XtSetValues(blackTimerWidget, args, i);
2407 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2408 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2409 XtSetValues(messageWidget, args, i);
2410 if (appData.showButtonBar) {
2412 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2413 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2414 XtSetValues(buttonBarWidget, args, i);
2418 XtSetArg(args[0], XtNfromVert, messageWidget);
2419 XtSetArg(args[1], XtNtop, XtChainTop);
2420 XtSetArg(args[2], XtNbottom, XtChainBottom);
2421 XtSetArg(args[3], XtNleft, XtChainLeft);
2422 XtSetArg(args[4], XtNright, XtChainRight);
2423 XtSetValues(boardWidget, args, 5);
2425 XtRealizeWidget(shellWidget);
2428 XtSetArg(args[0], XtNx, wpMain.x);
2429 XtSetArg(args[1], XtNy, wpMain.y);
2430 XtSetValues(shellWidget, args, 2);
2434 * Correct the width of the message and title widgets.
2435 * It is not known why some systems need the extra fudge term.
2436 * The value "2" is probably larger than needed.
2438 XawFormDoLayout(formWidget, False);
2440 #define WIDTH_FUDGE 2
2442 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2443 XtSetArg(args[i], XtNheight, &h); i++;
2444 XtGetValues(messageWidget, args, i);
2445 if (appData.showButtonBar) {
2447 XtSetArg(args[i], XtNwidth, &w); i++;
2448 XtGetValues(buttonBarWidget, args, i);
2449 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2451 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2454 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2455 if (gres != XtGeometryYes && appData.debugMode) {
2456 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2457 programName, gres, w, h, wr, hr);
2460 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2461 /* The size used for the child widget in layout lags one resize behind
2462 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2464 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2465 if (gres != XtGeometryYes && appData.debugMode) {
2466 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2467 programName, gres, w, h, wr, hr);
2470 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2471 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2472 XtSetArg(args[1], XtNright, XtChainRight);
2473 XtSetValues(messageWidget, args, 2);
2475 if (appData.titleInWindow) {
2477 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2478 XtSetArg(args[i], XtNheight, &h); i++;
2479 XtGetValues(titleWidget, args, i);
2481 w = boardWidth - 2*bor;
2483 XtSetArg(args[0], XtNwidth, &w);
2484 XtGetValues(menuBarWidget, args, 1);
2485 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2488 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2489 if (gres != XtGeometryYes && appData.debugMode) {
2491 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2492 programName, gres, w, h, wr, hr);
2495 XawFormDoLayout(formWidget, True);
2497 xBoardWindow = XtWindow(boardWidget);
2499 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2500 // not need to go into InitDrawingSizes().
2504 * Create X checkmark bitmap and initialize option menu checks.
2506 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2507 checkmark_bits, checkmark_width, checkmark_height);
2508 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2509 #ifndef OPTIONSDIALOG
2510 if (appData.alwaysPromoteToQueen) {
2511 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2514 if (appData.animateDragging) {
2515 XtSetValues(XtNameToWidget(menuBarWidget,
2516 "menuOptions.Animate Dragging"),
2519 if (appData.animate) {
2520 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2523 if (appData.autoCallFlag) {
2524 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2527 if (appData.autoFlipView) {
2528 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2531 if (appData.blindfold) {
2532 XtSetValues(XtNameToWidget(menuBarWidget,
2533 "menuOptions.Blindfold"), args, 1);
2535 if (appData.flashCount > 0) {
2536 XtSetValues(XtNameToWidget(menuBarWidget,
2537 "menuOptions.Flash Moves"),
2541 if (appData.highlightDragging) {
2542 XtSetValues(XtNameToWidget(menuBarWidget,
2543 "menuOptions.Highlight Dragging"),
2547 if (appData.highlightLastMove) {
2548 XtSetValues(XtNameToWidget(menuBarWidget,
2549 "menuOptions.Highlight Last Move"),
2552 if (appData.highlightMoveWithArrow) {
2553 XtSetValues(XtNameToWidget(menuBarWidget,
2554 "menuOptions.Arrow"),
2557 // if (appData.icsAlarm) {
2558 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2561 if (appData.ringBellAfterMoves) {
2562 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2565 if (appData.oneClick) {
2566 XtSetValues(XtNameToWidget(menuBarWidget,
2567 "menuOptions.OneClick"), args, 1);
2569 if (appData.periodicUpdates) {
2570 XtSetValues(XtNameToWidget(menuBarWidget,
2571 "menuOptions.Periodic Updates"), args, 1);
2573 if (appData.ponderNextMove) {
2574 XtSetValues(XtNameToWidget(menuBarWidget,
2575 "menuOptions.Ponder Next Move"), args, 1);
2577 if (appData.popupExitMessage) {
2578 XtSetValues(XtNameToWidget(menuBarWidget,
2579 "menuOptions.Popup Exit Message"), args, 1);
2581 if (appData.popupMoveErrors) {
2582 XtSetValues(XtNameToWidget(menuBarWidget,
2583 "menuOptions.Popup Move Errors"), args, 1);
2585 // if (appData.premove) {
2586 // XtSetValues(XtNameToWidget(menuBarWidget,
2587 // "menuOptions.Premove"), args, 1);
2589 if (appData.showCoords) {
2590 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2593 if (appData.hideThinkingFromHuman) {
2594 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2597 if (appData.testLegality) {
2598 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2602 if (saveSettingsOnExit) {
2603 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2610 ReadBitmap(&wIconPixmap, "icon_white.bm",
2611 icon_white_bits, icon_white_width, icon_white_height);
2612 ReadBitmap(&bIconPixmap, "icon_black.bm",
2613 icon_black_bits, icon_black_width, icon_black_height);
2614 iconPixmap = wIconPixmap;
2616 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2617 XtSetValues(shellWidget, args, i);
2620 * Create a cursor for the board widget.
2622 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2623 XChangeWindowAttributes(xDisplay, xBoardWindow,
2624 CWCursor, &window_attributes);
2627 * Inhibit shell resizing.
2629 shellArgs[0].value = (XtArgVal) &w;
2630 shellArgs[1].value = (XtArgVal) &h;
2631 XtGetValues(shellWidget, shellArgs, 2);
2632 shellArgs[4].value = shellArgs[2].value = w;
2633 shellArgs[5].value = shellArgs[3].value = h;
2634 XtSetValues(shellWidget, &shellArgs[2], 4);
2635 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2636 marginH = h - boardHeight;
2638 CatchDeleteWindow(shellWidget, "QuitProc");
2646 if (appData.animate || appData.animateDragging)
2649 XtAugmentTranslations(formWidget,
2650 XtParseTranslationTable(globalTranslations));
2651 XtAugmentTranslations(boardWidget,
2652 XtParseTranslationTable(boardTranslations));
2653 XtAugmentTranslations(whiteTimerWidget,
2654 XtParseTranslationTable(whiteTranslations));
2655 XtAugmentTranslations(blackTimerWidget,
2656 XtParseTranslationTable(blackTranslations));
2658 /* Why is the following needed on some versions of X instead
2659 * of a translation? */
2660 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2661 (XtEventHandler) EventProc, NULL);
2663 XtAddEventHandler(formWidget, KeyPressMask, False,
2664 (XtEventHandler) MoveTypeInProc, NULL);
2666 /* [AS] Restore layout */
2667 if( wpMoveHistory.visible ) {
2671 if( wpEvalGraph.visible )
2676 if( wpEngineOutput.visible ) {
2677 EngineOutputPopUp();
2682 if (errorExitStatus == -1) {
2683 if (appData.icsActive) {
2684 /* We now wait until we see "login:" from the ICS before
2685 sending the logon script (problems with timestamp otherwise) */
2686 /*ICSInitScript();*/
2687 if (appData.icsInputBox) ICSInputBoxPopUp();
2691 signal(SIGWINCH, TermSizeSigHandler);
2693 signal(SIGINT, IntSigHandler);
2694 signal(SIGTERM, IntSigHandler);
2695 if (*appData.cmailGameName != NULLCHAR) {
2696 signal(SIGUSR1, CmailSigHandler);
2699 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2701 // XtSetKeyboardFocus(shellWidget, formWidget);
2702 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2704 XtAppMainLoop(appContext);
2705 if (appData.debugMode) fclose(debugFP); // [DM] debug
2709 static Boolean noEcho;
2714 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2715 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2717 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2718 unlink(gameCopyFilename);
2719 unlink(gamePasteFilename);
2720 if(noEcho) EchoOn();
2724 TermSizeSigHandler (int sig)
2730 IntSigHandler (int sig)
2736 CmailSigHandler (int sig)
2741 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2743 /* Activate call-back function CmailSigHandlerCallBack() */
2744 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2746 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2750 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2753 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2755 /**** end signal code ****/
2761 /* try to open the icsLogon script, either in the location given
2762 * or in the users HOME directory
2769 f = fopen(appData.icsLogon, "r");
2772 homedir = getenv("HOME");
2773 if (homedir != NULL)
2775 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2776 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2777 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2778 f = fopen(buf, "r");
2783 ProcessICSInitScript(f);
2785 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2804 GreyRevert (Boolean grey)
2807 if (!menuBarWidget) return;
2808 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2810 DisplayError("menuEdit.Revert", 0);
2812 XtSetSensitive(w, !grey);
2814 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2816 DisplayError("menuEdit.Annotate", 0);
2818 XtSetSensitive(w, !grey);
2823 SetMenuEnables (Enables *enab)
2826 if (!menuBarWidget) return;
2827 while (enab->name != NULL) {
2828 w = XtNameToWidget(menuBarWidget, enab->name);
2830 DisplayError(enab->name, 0);
2832 XtSetSensitive(w, enab->value);
2838 Enables icsEnables[] = {
2839 { "menuFile.Mail Move", False },
2840 { "menuFile.Reload CMail Message", False },
2841 { "menuMode.Machine Black", False },
2842 { "menuMode.Machine White", False },
2843 { "menuMode.Analysis Mode", False },
2844 { "menuMode.Analyze File", False },
2845 { "menuMode.Two Machines", False },
2846 { "menuMode.Machine Match", False },
2848 { "menuEngine.Hint", False },
2849 { "menuEngine.Book", False },
2850 { "menuEngine.Move Now", False },
2851 #ifndef OPTIONSDIALOG
2852 { "menuOptions.Periodic Updates", False },
2853 { "menuOptions.Hide Thinking", False },
2854 { "menuOptions.Ponder Next Move", False },
2857 { "menuEngine.Engine #1 Settings", False },
2858 { "menuEngine.Engine #2 Settings", False },
2859 { "menuEngine.Load Engine", False },
2860 { "menuEdit.Annotate", False },
2861 { "menuOptions.Match", False },
2865 Enables ncpEnables[] = {
2866 { "menuFile.Mail Move", False },
2867 { "menuFile.Reload CMail Message", False },
2868 { "menuMode.Machine White", False },
2869 { "menuMode.Machine Black", False },
2870 { "menuMode.Analysis Mode", False },
2871 { "menuMode.Analyze File", False },
2872 { "menuMode.Two Machines", False },
2873 { "menuMode.Machine Match", False },
2874 { "menuMode.ICS Client", False },
2875 { "menuView.ICStex", False },
2876 { "menuView.ICS Input Box", False },
2877 { "Action", False },
2878 { "menuEdit.Revert", False },
2879 { "menuEdit.Annotate", False },
2880 { "menuEngine.Engine #1 Settings", False },
2881 { "menuEngine.Engine #2 Settings", False },
2882 { "menuEngine.Move Now", False },
2883 { "menuEngine.Retract Move", False },
2884 { "menuOptions.ICS", False },
2885 #ifndef OPTIONSDIALOG
2886 { "menuOptions.Auto Flag", False },
2887 { "menuOptions.Auto Flip View", False },
2888 // { "menuOptions.ICS Alarm", False },
2889 { "menuOptions.Move Sound", False },
2890 { "menuOptions.Hide Thinking", False },
2891 { "menuOptions.Periodic Updates", False },
2892 { "menuOptions.Ponder Next Move", False },
2894 { "menuEngine.Hint", False },
2895 { "menuEngine.Book", False },
2899 Enables gnuEnables[] = {
2900 { "menuMode.ICS Client", False },
2901 { "menuView.ICStex", False },
2902 { "menuView.ICS Input Box", False },
2903 { "menuAction.Accept", False },
2904 { "menuAction.Decline", False },
2905 { "menuAction.Rematch", False },
2906 { "menuAction.Adjourn", False },
2907 { "menuAction.Stop Examining", False },
2908 { "menuAction.Stop Observing", False },
2909 { "menuAction.Upload to Examine", False },
2910 { "menuEdit.Revert", False },
2911 { "menuEdit.Annotate", False },
2912 { "menuOptions.ICS", False },
2914 /* The next two options rely on SetCmailMode being called *after* */
2915 /* SetGNUMode so that when GNU is being used to give hints these */
2916 /* menu options are still available */
2918 { "menuFile.Mail Move", False },
2919 { "menuFile.Reload CMail Message", False },
2920 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2921 { "menuMode.Machine White", True },
2922 { "menuMode.Machine Black", True },
2923 { "menuMode.Analysis Mode", True },
2924 { "menuMode.Analyze File", True },
2925 { "menuMode.Two Machines", True },
2926 { "menuMode.Machine Match", True },
2927 { "menuEngine.Engine #1 Settings", True },
2928 { "menuEngine.Engine #2 Settings", True },
2929 { "menuEngine.Hint", True },
2930 { "menuEngine.Book", True },
2931 { "menuEngine.Move Now", True },
2932 { "menuEngine.Retract Move", True },
2937 Enables cmailEnables[] = {
2939 { "menuAction.Call Flag", False },
2940 { "menuAction.Draw", True },
2941 { "menuAction.Adjourn", False },
2942 { "menuAction.Abort", False },
2943 { "menuAction.Stop Observing", False },
2944 { "menuAction.Stop Examining", False },
2945 { "menuFile.Mail Move", True },
2946 { "menuFile.Reload CMail Message", True },
2950 Enables trainingOnEnables[] = {
2951 { "menuMode.Edit Comment", False },
2952 { "menuMode.Pause", False },
2953 { "menuEdit.Forward", False },
2954 { "menuEdit.Backward", False },
2955 { "menuEdit.Forward to End", False },
2956 { "menuEdit.Back to Start", False },
2957 { "menuEngine.Move Now", False },
2958 { "menuEdit.Truncate Game", False },
2962 Enables trainingOffEnables[] = {
2963 { "menuMode.Edit Comment", True },
2964 { "menuMode.Pause", True },
2965 { "menuEdit.Forward", True },
2966 { "menuEdit.Backward", True },
2967 { "menuEdit.Forward to End", True },
2968 { "menuEdit.Back to Start", True },
2969 { "menuEngine.Move Now", True },
2970 { "menuEdit.Truncate Game", True },
2974 Enables machineThinkingEnables[] = {
2975 { "menuFile.Load Game", False },
2976 // { "menuFile.Load Next Game", False },
2977 // { "menuFile.Load Previous Game", False },
2978 // { "menuFile.Reload Same Game", False },
2979 { "menuEdit.Paste Game", False },
2980 { "menuFile.Load Position", False },
2981 // { "menuFile.Load Next Position", False },
2982 // { "menuFile.Load Previous Position", False },
2983 // { "menuFile.Reload Same Position", False },
2984 { "menuEdit.Paste Position", False },
2985 { "menuMode.Machine White", False },
2986 { "menuMode.Machine Black", False },
2987 { "menuMode.Two Machines", False },
2988 // { "menuMode.Machine Match", False },
2989 { "menuEngine.Retract Move", False },
2993 Enables userThinkingEnables[] = {
2994 { "menuFile.Load Game", True },
2995 // { "menuFile.Load Next Game", True },
2996 // { "menuFile.Load Previous Game", True },
2997 // { "menuFile.Reload Same Game", True },
2998 { "menuEdit.Paste Game", True },
2999 { "menuFile.Load Position", True },
3000 // { "menuFile.Load Next Position", True },
3001 // { "menuFile.Load Previous Position", True },
3002 // { "menuFile.Reload Same Position", True },
3003 { "menuEdit.Paste Position", True },
3004 { "menuMode.Machine White", True },
3005 { "menuMode.Machine Black", True },
3006 { "menuMode.Two Machines", True },
3007 // { "menuMode.Machine Match", True },
3008 { "menuEngine.Retract Move", True },
3015 SetMenuEnables(icsEnables);
3018 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3019 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3020 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3028 SetMenuEnables(ncpEnables);
3034 SetMenuEnables(gnuEnables);
3040 SetMenuEnables(cmailEnables);
3044 SetTrainingModeOn ()
3046 SetMenuEnables(trainingOnEnables);
3047 if (appData.showButtonBar) {
3048 XtSetSensitive(buttonBarWidget, False);
3054 SetTrainingModeOff ()
3056 SetMenuEnables(trainingOffEnables);
3057 if (appData.showButtonBar) {
3058 XtSetSensitive(buttonBarWidget, True);
3063 SetUserThinkingEnables ()
3065 if (appData.noChessProgram) return;
3066 SetMenuEnables(userThinkingEnables);
3070 SetMachineThinkingEnables ()
3072 if (appData.noChessProgram) return;
3073 SetMenuEnables(machineThinkingEnables);
3075 case MachinePlaysBlack:
3076 case MachinePlaysWhite:
3077 case TwoMachinesPlay:
3078 XtSetSensitive(XtNameToWidget(menuBarWidget,
3079 ModeToWidgetName(gameMode)), True);
3086 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3087 #define HISTORY_SIZE 64
3088 static char *history[HISTORY_SIZE];
3089 int histIn = 0, histP = 0;
3092 SaveInHistory (char *cmd)
3094 if (history[histIn] != NULL) {
3095 free(history[histIn]);
3096 history[histIn] = NULL;
3098 if (*cmd == NULLCHAR) return;
3099 history[histIn] = StrSave(cmd);
3100 histIn = (histIn + 1) % HISTORY_SIZE;
3101 if (history[histIn] != NULL) {
3102 free(history[histIn]);
3103 history[histIn] = NULL;
3109 PrevInHistory (char *cmd)
3112 if (histP == histIn) {
3113 if (history[histIn] != NULL) free(history[histIn]);
3114 history[histIn] = StrSave(cmd);
3116 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3117 if (newhp == histIn || history[newhp] == NULL) return NULL;
3119 return history[histP];
3125 if (histP == histIn) return NULL;
3126 histP = (histP + 1) % HISTORY_SIZE;
3127 return history[histP];
3129 // end of borrowed code
3131 #define Abs(n) ((n)<0 ? -(n) : (n))
3135 InsertPxlSize (char *pattern, int targetPxlSize)
3137 char *base_fnt_lst, strInt[12], *p, *q;
3138 int alternatives, i, len, strIntLen;
3141 * Replace the "*" (if present) in the pixel-size slot of each
3142 * alternative with the targetPxlSize.
3146 while ((p = strchr(p, ',')) != NULL) {
3150 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3151 strIntLen = strlen(strInt);
3152 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3156 while (alternatives--) {
3157 char *comma = strchr(p, ',');
3158 for (i=0; i<14; i++) {
3159 char *hyphen = strchr(p, '-');
3161 if (comma && hyphen > comma) break;
3162 len = hyphen + 1 - p;
3163 if (i == 7 && *p == '*' && len == 2) {
3165 memcpy(q, strInt, strIntLen);
3175 len = comma + 1 - p;
3182 return base_fnt_lst;
3186 CreateFontSet (char *base_fnt_lst)
3189 char **missing_list;
3193 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3194 &missing_list, &missing_count, &def_string);
3195 if (appData.debugMode) {
3197 XFontStruct **font_struct_list;
3198 char **font_name_list;
3199 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3201 fprintf(debugFP, " got list %s, locale %s\n",
3202 XBaseFontNameListOfFontSet(fntSet),
3203 XLocaleOfFontSet(fntSet));
3204 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3205 for (i = 0; i < count; i++) {
3206 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3209 for (i = 0; i < missing_count; i++) {
3210 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3213 if (fntSet == NULL) {
3214 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3219 #else // not ENABLE_NLS
3221 * Find a font that matches "pattern" that is as close as
3222 * possible to the targetPxlSize. Prefer fonts that are k
3223 * pixels smaller to fonts that are k pixels larger. The
3224 * pattern must be in the X Consortium standard format,
3225 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3226 * The return value should be freed with XtFree when no
3230 FindFont (char *pattern, int targetPxlSize)
3232 char **fonts, *p, *best, *scalable, *scalableTail;
3233 int i, j, nfonts, minerr, err, pxlSize;
3235 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3237 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3238 programName, pattern);
3245 for (i=0; i<nfonts; i++) {
3248 if (*p != '-') continue;
3250 if (*p == NULLCHAR) break;
3251 if (*p++ == '-') j++;
3253 if (j < 7) continue;
3256 scalable = fonts[i];
3259 err = pxlSize - targetPxlSize;
3260 if (Abs(err) < Abs(minerr) ||
3261 (minerr > 0 && err < 0 && -err == minerr)) {
3267 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3268 /* If the error is too big and there is a scalable font,
3269 use the scalable font. */
3270 int headlen = scalableTail - scalable;
3271 p = (char *) XtMalloc(strlen(scalable) + 10);
3272 while (isdigit(*scalableTail)) scalableTail++;
3273 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3275 p = (char *) XtMalloc(strlen(best) + 2);
3276 safeStrCpy(p, best, strlen(best)+1 );
3278 if (appData.debugMode) {
3279 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3280 pattern, targetPxlSize, p);
3282 XFreeFontNames(fonts);
3289 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3290 // must be called before all non-first callse to CreateGCs()
3291 XtReleaseGC(shellWidget, highlineGC);
3292 XtReleaseGC(shellWidget, lightSquareGC);
3293 XtReleaseGC(shellWidget, darkSquareGC);
3294 XtReleaseGC(shellWidget, lineGC);
3295 if (appData.monoMode) {
3296 if (DefaultDepth(xDisplay, xScreen) == 1) {
3297 XtReleaseGC(shellWidget, wbPieceGC);
3299 XtReleaseGC(shellWidget, bwPieceGC);
3302 XtReleaseGC(shellWidget, prelineGC);
3303 XtReleaseGC(shellWidget, jailSquareGC);
3304 XtReleaseGC(shellWidget, wdPieceGC);
3305 XtReleaseGC(shellWidget, wlPieceGC);
3306 XtReleaseGC(shellWidget, wjPieceGC);
3307 XtReleaseGC(shellWidget, bdPieceGC);
3308 XtReleaseGC(shellWidget, blPieceGC);
3309 XtReleaseGC(shellWidget, bjPieceGC);
3314 CreateGCs (int redo)
3316 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3317 | GCBackground | GCFunction | GCPlaneMask;
3318 XGCValues gc_values;
3321 gc_values.plane_mask = AllPlanes;
3322 gc_values.line_width = lineGap;
3323 gc_values.line_style = LineSolid;
3324 gc_values.function = GXcopy;
3327 DeleteGCs(); // called a second time; clean up old GCs first
3328 } else { // [HGM] grid and font GCs created on first call only
3329 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3330 gc_values.background = XWhitePixel(xDisplay, xScreen);
3331 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3332 XSetFont(xDisplay, coordGC, coordFontID);
3334 // [HGM] make font for holdings counts (white on black)
3335 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3336 gc_values.background = XBlackPixel(xDisplay, xScreen);
3337 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3338 XSetFont(xDisplay, countGC, countFontID);
3340 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3341 gc_values.background = XBlackPixel(xDisplay, xScreen);
3342 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3344 if (appData.monoMode) {
3345 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3346 gc_values.background = XWhitePixel(xDisplay, xScreen);
3347 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3349 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3350 gc_values.background = XBlackPixel(xDisplay, xScreen);
3351 lightSquareGC = wbPieceGC
3352 = XtGetGC(shellWidget, value_mask, &gc_values);
3354 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3355 gc_values.background = XWhitePixel(xDisplay, xScreen);
3356 darkSquareGC = bwPieceGC
3357 = XtGetGC(shellWidget, value_mask, &gc_values);
3359 if (DefaultDepth(xDisplay, xScreen) == 1) {
3360 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3361 gc_values.function = GXcopyInverted;
3362 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3363 gc_values.function = GXcopy;
3364 if (XBlackPixel(xDisplay, xScreen) == 1) {
3365 bwPieceGC = darkSquareGC;
3366 wbPieceGC = copyInvertedGC;
3368 bwPieceGC = copyInvertedGC;
3369 wbPieceGC = lightSquareGC;
3373 gc_values.foreground = highlightSquareColor;
3374 gc_values.background = highlightSquareColor;
3375 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3377 gc_values.foreground = premoveHighlightColor;
3378 gc_values.background = premoveHighlightColor;
3379 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3381 gc_values.foreground = lightSquareColor;
3382 gc_values.background = darkSquareColor;
3383 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3385 gc_values.foreground = darkSquareColor;
3386 gc_values.background = lightSquareColor;
3387 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3389 gc_values.foreground = jailSquareColor;
3390 gc_values.background = jailSquareColor;
3391 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3393 gc_values.foreground = whitePieceColor;
3394 gc_values.background = darkSquareColor;
3395 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3397 gc_values.foreground = whitePieceColor;
3398 gc_values.background = lightSquareColor;
3399 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3401 gc_values.foreground = whitePieceColor;
3402 gc_values.background = jailSquareColor;
3403 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3405 gc_values.foreground = blackPieceColor;
3406 gc_values.background = darkSquareColor;
3407 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3409 gc_values.foreground = blackPieceColor;
3410 gc_values.background = lightSquareColor;
3411 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3413 gc_values.foreground = blackPieceColor;
3414 gc_values.background = jailSquareColor;
3415 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3420 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3428 fp = fopen(filename, "rb");
3430 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3437 for (y=0; y<h; ++y) {
3438 for (x=0; x<h; ++x) {
3443 XPutPixel(xim, x, y, blackPieceColor);
3445 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3448 XPutPixel(xim, x, y, darkSquareColor);
3450 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3453 XPutPixel(xim, x, y, whitePieceColor);
3455 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3458 XPutPixel(xim, x, y, lightSquareColor);
3460 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3468 /* create Pixmap of piece */
3469 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3471 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3474 /* create Pixmap of clipmask
3475 Note: We assume the white/black pieces have the same
3476 outline, so we make only 6 masks. This is okay
3477 since the XPM clipmask routines do the same. */
3479 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3481 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3484 /* now create the 1-bit version */
3485 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3488 values.foreground = 1;
3489 values.background = 0;
3491 /* Don't use XtGetGC, not read only */
3492 maskGC = XCreateGC(xDisplay, *mask,
3493 GCForeground | GCBackground, &values);
3494 XCopyPlane(xDisplay, temp, *mask, maskGC,
3495 0, 0, squareSize, squareSize, 0, 0, 1);
3496 XFreePixmap(xDisplay, temp);
3501 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3509 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3514 /* The XSynchronize calls were copied from CreatePieces.
3515 Not sure if needed, but can't hurt */
3516 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3519 /* temp needed by loadXIM() */
3520 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3521 0, 0, ss, ss, AllPlanes, XYPixmap);
3523 if (strlen(appData.pixmapDirectory) == 0) {
3527 if (appData.monoMode) {
3528 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3532 fprintf(stderr, _("\nLoading XIMs...\n"));
3534 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3535 fprintf(stderr, "%d", piece+1);
3536 for (kind=0; kind<4; kind++) {
3537 fprintf(stderr, ".");
3538 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3539 ExpandPathName(appData.pixmapDirectory),
3540 piece <= (int) WhiteKing ? "" : "w",
3541 pieceBitmapNames[piece],
3543 ximPieceBitmap[kind][piece] =
3544 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3545 0, 0, ss, ss, AllPlanes, XYPixmap);
3546 if (appData.debugMode)
3547 fprintf(stderr, _("(File:%s:) "), buf);
3548 loadXIM(ximPieceBitmap[kind][piece],
3550 &(xpmPieceBitmap2[kind][piece]),
3551 &(ximMaskPm2[piece]));
3552 if(piece <= (int)WhiteKing)
3553 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3555 fprintf(stderr," ");
3557 /* Load light and dark squares */
3558 /* If the LSQ and DSQ pieces don't exist, we will
3559 draw them with solid squares. */
3560 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3561 if (access(buf, 0) != 0) {
3565 fprintf(stderr, _("light square "));
3567 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3568 0, 0, ss, ss, AllPlanes, XYPixmap);
3569 if (appData.debugMode)
3570 fprintf(stderr, _("(File:%s:) "), buf);
3572 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3573 fprintf(stderr, _("dark square "));
3574 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3575 ExpandPathName(appData.pixmapDirectory), ss);
3576 if (appData.debugMode)
3577 fprintf(stderr, _("(File:%s:) "), buf);
3579 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3580 0, 0, ss, ss, AllPlanes, XYPixmap);
3581 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3582 xpmJailSquare = xpmLightSquare;
3584 fprintf(stderr, _("Done.\n"));
3586 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3589 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3593 CreateXPMBoard (char *s, int kind)
3597 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3598 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3599 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3605 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3606 // thisroutine has to be called t free the old piece pixmaps
3608 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3609 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3611 XFreePixmap(xDisplay, xpmLightSquare);
3612 XFreePixmap(xDisplay, xpmDarkSquare);
3621 u_int ss = squareSize;
3623 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3624 XpmColorSymbol symbols[4];
3625 static int redo = False;
3627 if(redo) FreeXPMPieces(); else redo = 1;
3629 /* The XSynchronize calls were copied from CreatePieces.
3630 Not sure if needed, but can't hurt */
3631 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3633 /* Setup translations so piece colors match square colors */
3634 symbols[0].name = "light_piece";
3635 symbols[0].value = appData.whitePieceColor;
3636 symbols[1].name = "dark_piece";
3637 symbols[1].value = appData.blackPieceColor;
3638 symbols[2].name = "light_square";
3639 symbols[2].value = appData.lightSquareColor;
3640 symbols[3].name = "dark_square";
3641 symbols[3].value = appData.darkSquareColor;
3643 attr.valuemask = XpmColorSymbols;
3644 attr.colorsymbols = symbols;
3645 attr.numsymbols = 4;
3647 if (appData.monoMode) {
3648 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3652 if (strlen(appData.pixmapDirectory) == 0) {
3653 XpmPieces* pieces = builtInXpms;
3656 while (pieces->size != squareSize && pieces->size) pieces++;
3657 if (!pieces->size) {
3658 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3661 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3662 for (kind=0; kind<4; kind++) {
3664 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3665 pieces->xpm[piece][kind],
3666 &(xpmPieceBitmap2[kind][piece]),
3667 NULL, &attr)) != 0) {
3668 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3672 if(piece <= (int) WhiteKing)
3673 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3677 xpmJailSquare = xpmLightSquare;
3681 fprintf(stderr, _("\nLoading XPMs...\n"));
3684 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3685 fprintf(stderr, "%d ", piece+1);
3686 for (kind=0; kind<4; kind++) {
3687 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3688 ExpandPathName(appData.pixmapDirectory),
3689 piece > (int) WhiteKing ? "w" : "",
3690 pieceBitmapNames[piece],
3692 if (appData.debugMode) {
3693 fprintf(stderr, _("(File:%s:) "), buf);
3695 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3696 &(xpmPieceBitmap2[kind][piece]),
3697 NULL, &attr)) != 0) {
3698 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3699 // [HGM] missing: read of unorthodox piece failed; substitute King.
3700 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3701 ExpandPathName(appData.pixmapDirectory),
3703 if (appData.debugMode) {
3704 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3706 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3707 &(xpmPieceBitmap2[kind][piece]),
3711 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3716 if(piece <= (int) WhiteKing)
3717 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3720 /* Load light and dark squares */
3721 /* If the LSQ and DSQ pieces don't exist, we will
3722 draw them with solid squares. */
3723 fprintf(stderr, _("light square "));
3724 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3725 if (access(buf, 0) != 0) {
3729 if (appData.debugMode)
3730 fprintf(stderr, _("(File:%s:) "), buf);
3732 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3733 &xpmLightSquare, NULL, &attr)) != 0) {
3734 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3737 fprintf(stderr, _("dark square "));
3738 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3739 ExpandPathName(appData.pixmapDirectory), ss);
3740 if (appData.debugMode) {
3741 fprintf(stderr, _("(File:%s:) "), buf);
3743 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3744 &xpmDarkSquare, NULL, &attr)) != 0) {
3745 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3749 xpmJailSquare = xpmLightSquare;
3750 fprintf(stderr, _("Done.\n"));
3752 oldVariant = -1; // kludge to force re-makig of animation masks
3753 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3756 #endif /* HAVE_LIBXPM */
3759 /* No built-in bitmaps */
3764 u_int ss = squareSize;
3766 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3769 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3770 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3771 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3772 pieceBitmapNames[piece],
3773 ss, kind == SOLID ? 's' : 'o');
3774 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3775 if(piece <= (int)WhiteKing)
3776 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3780 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3784 /* With built-in bitmaps */
3788 BuiltInBits* bib = builtInBits;
3791 u_int ss = squareSize;
3793 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3796 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3798 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3799 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3800 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3801 pieceBitmapNames[piece],
3802 ss, kind == SOLID ? 's' : 'o');
3803 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3804 bib->bits[kind][piece], ss, ss);
3805 if(piece <= (int)WhiteKing)
3806 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3810 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3816 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3821 char msg[MSG_SIZ], fullname[MSG_SIZ];
3823 if (*appData.bitmapDirectory != NULLCHAR) {
3824 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3825 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3826 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3827 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3828 &w, &h, pm, &x_hot, &y_hot);
3829 fprintf(stderr, "load %s\n", name);
3830 if (errcode != BitmapSuccess) {
3832 case BitmapOpenFailed:
3833 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3835 case BitmapFileInvalid:
3836 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3838 case BitmapNoMemory:
3839 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3843 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3847 fprintf(stderr, _("%s: %s...using built-in\n"),
3849 } else if (w != wreq || h != hreq) {
3851 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3852 programName, fullname, w, h, wreq, hreq);
3858 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3868 if (lineGap == 0) return;
3870 /* [HR] Split this into 2 loops for non-square boards. */
3872 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3873 gridSegments[i].x1 = 0;
3874 gridSegments[i].x2 =
3875 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3876 gridSegments[i].y1 = gridSegments[i].y2
3877 = lineGap / 2 + (i * (squareSize + lineGap));
3880 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3881 gridSegments[j + i].y1 = 0;
3882 gridSegments[j + i].y2 =
3883 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3884 gridSegments[j + i].x1 = gridSegments[j + i].x2
3885 = lineGap / 2 + (j * (squareSize + lineGap));
3890 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3892 XtActionProc proc = (XtActionProc) addr;
3894 (proc)(NULL, NULL, NULL, NULL);
3898 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3900 RecentEngineEvent((int) addr);
3904 AppendEnginesToMenu (Widget menu, char *list)
3912 if(appData.recentEngines <= 0) return;
3913 recentEngines = strdup(list);
3915 XtSetArg(args[j], XtNleftMargin, 20); j++;
3916 XtSetArg(args[j], XtNrightMargin, 20); j++;
3918 p = strchr(list, '\n'); if(p == NULL) break;
3919 if(i == 0) XtCreateManagedWidget(_("----"), smeLineObjectClass, menu, args, j); // at least one valid item to add
3921 XtSetArg(args[j], XtNlabel, XtNewString(list));
3922 entry = XtCreateManagedWidget("engine", smeBSBObjectClass, menu, args, j+1);
3923 XtAddCallback(entry, XtNcallback,
3924 (XtCallbackProc) MenuEngineSelect,
3926 i++; *p = '\n'; list = p + 1;
3931 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3938 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3941 XtSetArg(args[j], XtNleftMargin, 20); j++;
3942 XtSetArg(args[j], XtNrightMargin, 20); j++;
3944 while (mi->string != NULL) {
3945 if (strcmp(mi->string, "----") == 0) {
3946 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3949 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3950 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3952 XtAddCallback(entry, XtNcallback,
3953 (XtCallbackProc) MenuBarSelect,
3954 (caddr_t) mi->proc);
3958 if(!strcmp(mb->name, "Engine")) AppendEnginesToMenu(menu, appData.recentEngineList);
3962 CreateMenuBar (Menu *mb, int boardWidth)
3964 int i, j, nr = 0, wtot = 0, widths[10];
3967 char menuName[MSG_SIZ];
3972 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3973 XtSetArg(args[j], XtNvSpace, 0); j++;
3974 XtSetArg(args[j], XtNborderWidth, 0); j++;
3975 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3976 formWidget, args, j);
3978 while (mb->name != NULL) {
3979 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3980 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3982 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3983 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3984 XtSetArg(args[j], XtNborderWidth, 0); j++;
3985 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3987 CreateMenuBarPopup(menuBar, menuName, mb);
3989 XtSetArg(args[j], XtNwidth, &w); j++;
3990 XtGetValues(mb->subMenu, args, j);
3991 wtot += mb->textWidth = widths[nr++] = w;
3994 while(wtot > boardWidth - 40) {
3996 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
4000 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
4002 XtSetArg(args[j], XtNwidth, widths[i]); j++;
4003 XtSetValues(ma[i].subMenu, args, j);
4009 CreateButtonBar (MenuItem *mi)
4012 Widget button, buttonBar;
4016 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4018 XtSetArg(args[j], XtNhSpace, 0); j++;
4020 XtSetArg(args[j], XtNborderWidth, 0); j++;
4021 XtSetArg(args[j], XtNvSpace, 0); j++;
4022 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4023 formWidget, args, j);
4025 while (mi->string != NULL) {
4028 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4029 XtSetArg(args[j], XtNborderWidth, 0); j++;
4031 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4032 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4033 buttonBar, args, j);
4034 XtAddCallback(button, XtNcallback,
4035 (XtCallbackProc) MenuBarSelect,
4036 (caddr_t) mi->proc);
4043 CreatePieceMenu (char *name, int color)
4048 ChessSquare selection;
4050 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4051 boardWidget, args, 0);
4053 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4054 String item = pieceMenuStrings[color][i];
4056 if (strcmp(item, "----") == 0) {
4057 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4060 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4061 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4063 selection = pieceMenuTranslation[color][i];
4064 XtAddCallback(entry, XtNcallback,
4065 (XtCallbackProc) PieceMenuSelect,
4066 (caddr_t) selection);
4067 if (selection == WhitePawn || selection == BlackPawn) {
4068 XtSetArg(args[0], XtNpopupOnEntry, entry);
4069 XtSetValues(menu, args, 1);
4082 ChessSquare selection;
4084 whitePieceMenu = CreatePieceMenu("menuW", 0);
4085 blackPieceMenu = CreatePieceMenu("menuB", 1);
4087 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
4088 XtRegisterGrabAction(PieceMenuPopup, True,
4089 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4090 GrabModeAsync, GrabModeAsync);
4092 XtSetArg(args[0], XtNlabel, _("Drop"));
4093 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4094 boardWidget, args, 1);
4095 for (i = 0; i < DROP_MENU_SIZE; i++) {
4096 String item = dropMenuStrings[i];
4098 if (strcmp(item, "----") == 0) {
4099 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4102 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4103 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4105 selection = dropMenuTranslation[i];
4106 XtAddCallback(entry, XtNcallback,
4107 (XtCallbackProc) DropMenuSelect,
4108 (caddr_t) selection);
4122 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4123 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4124 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4125 dmEnables[i].piece);
4126 XtSetSensitive(entry, p != NULL || !appData.testLegality
4127 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4128 && !appData.icsActive));
4130 while (p && *p++ == dmEnables[i].piece) count++;
4131 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4133 XtSetArg(args[j], XtNlabel, label); j++;
4134 XtSetValues(entry, args, j);
4139 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4141 String whichMenu; int menuNr = -2;
4142 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4143 if (event->type == ButtonRelease)
4144 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4145 else if (event->type == ButtonPress)
4146 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4148 case 0: whichMenu = params[0]; break;
4149 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4151 case -1: if (errorUp) ErrorPopDown();
4154 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4158 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4160 if (pmFromX < 0 || pmFromY < 0) return;
4161 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4165 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4167 if (pmFromX < 0 || pmFromY < 0) return;
4168 DropMenuEvent(piece, pmFromX, pmFromY);
4172 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4174 shiftKey = prms[0][0] & 1;
4179 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4181 shiftKey = prms[0][0] & 1;
4187 * If the user selects on a border boundary, return -1; if off the board,
4188 * return -2. Otherwise map the event coordinate to the square.
4191 EventToSquare (int x, int limit)
4198 if ((x % (squareSize + lineGap)) >= squareSize)
4200 x /= (squareSize + lineGap);
4207 do_flash_delay (unsigned long msec)
4213 drawHighlight (int file, int rank, GC gc)
4217 if (lineGap == 0) return;
4220 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4221 (squareSize + lineGap);
4222 y = lineGap/2 + rank * (squareSize + lineGap);
4224 x = lineGap/2 + file * (squareSize + lineGap);
4225 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4226 (squareSize + lineGap);
4229 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4230 squareSize+lineGap, squareSize+lineGap);
4233 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4234 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4237 SetHighlights (int fromX, int fromY, int toX, int toY)
4239 if (hi1X != fromX || hi1Y != fromY) {
4240 if (hi1X >= 0 && hi1Y >= 0) {
4241 drawHighlight(hi1X, hi1Y, lineGC);
4243 } // [HGM] first erase both, then draw new!
4244 if (hi2X != toX || hi2Y != toY) {
4245 if (hi2X >= 0 && hi2Y >= 0) {
4246 drawHighlight(hi2X, hi2Y, lineGC);
4249 if (hi1X != fromX || hi1Y != fromY) {
4250 if (fromX >= 0 && fromY >= 0) {
4251 drawHighlight(fromX, fromY, highlineGC);
4254 if (hi2X != toX || hi2Y != toY) {
4255 if (toX >= 0 && toY >= 0) {
4256 drawHighlight(toX, toY, highlineGC);
4259 if(toX<0) // clearing the highlights must have damaged arrow
4260 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4270 SetHighlights(-1, -1, -1, -1);
4275 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4277 if (pm1X != fromX || pm1Y != fromY) {
4278 if (pm1X >= 0 && pm1Y >= 0) {
4279 drawHighlight(pm1X, pm1Y, lineGC);
4281 if (fromX >= 0 && fromY >= 0) {
4282 drawHighlight(fromX, fromY, prelineGC);
4285 if (pm2X != toX || pm2Y != toY) {
4286 if (pm2X >= 0 && pm2Y >= 0) {
4287 drawHighlight(pm2X, pm2Y, lineGC);
4289 if (toX >= 0 && toY >= 0) {
4290 drawHighlight(toX, toY, prelineGC);
4300 ClearPremoveHighlights ()
4302 SetPremoveHighlights(-1, -1, -1, -1);
4306 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4308 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4309 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4311 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4312 if(textureW[kind] < W*squareSize)
4313 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4315 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4316 if(textureH[kind] < H*squareSize)
4317 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4319 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4324 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4325 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4327 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4328 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4329 squareSize, squareSize, x*fac, y*fac);
4331 if (useImages && useImageSqs) {
4335 pm = xpmLightSquare;
4340 case 2: /* neutral */
4345 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4346 squareSize, squareSize, x*fac, y*fac);
4356 case 2: /* neutral */
4361 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4366 I split out the routines to draw a piece so that I could
4367 make a generic flash routine.
4370 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4372 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4373 switch (square_color) {
4375 case 2: /* neutral */
4377 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4378 ? *pieceToOutline(piece)
4379 : *pieceToSolid(piece),
4380 dest, bwPieceGC, 0, 0,
4381 squareSize, squareSize, x, y);
4384 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4385 ? *pieceToSolid(piece)
4386 : *pieceToOutline(piece),
4387 dest, wbPieceGC, 0, 0,
4388 squareSize, squareSize, x, y);
4394 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4396 switch (square_color) {
4398 case 2: /* neutral */
4400 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4401 ? *pieceToOutline(piece)
4402 : *pieceToSolid(piece),
4403 dest, bwPieceGC, 0, 0,
4404 squareSize, squareSize, x, y, 1);
4407 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4408 ? *pieceToSolid(piece)
4409 : *pieceToOutline(piece),
4410 dest, wbPieceGC, 0, 0,
4411 squareSize, squareSize, x, y, 1);
4417 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4419 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4420 switch (square_color) {
4422 XCopyPlane(xDisplay, *pieceToSolid(piece),
4423 dest, (int) piece < (int) BlackPawn
4424 ? wlPieceGC : blPieceGC, 0, 0,
4425 squareSize, squareSize, x, y, 1);
4428 XCopyPlane(xDisplay, *pieceToSolid(piece),
4429 dest, (int) piece < (int) BlackPawn
4430 ? wdPieceGC : bdPieceGC, 0, 0,
4431 squareSize, squareSize, x, y, 1);
4433 case 2: /* neutral */
4435 XCopyPlane(xDisplay, *pieceToSolid(piece),
4436 dest, (int) piece < (int) BlackPawn
4437 ? wjPieceGC : bjPieceGC, 0, 0,
4438 squareSize, squareSize, x, y, 1);
4444 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4446 int kind, p = piece;
4448 switch (square_color) {
4450 case 2: /* neutral */
4452 if ((int)piece < (int) BlackPawn) {
4460 if ((int)piece < (int) BlackPawn) {
4468 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4469 if(useTexture & square_color+1) {
4470 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4471 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4472 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4473 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4474 XSetClipMask(xDisplay, wlPieceGC, None);
4475 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4477 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4478 dest, wlPieceGC, 0, 0,
4479 squareSize, squareSize, x, y);
4482 typedef void (*DrawFunc)();
4487 if (appData.monoMode) {
4488 if (DefaultDepth(xDisplay, xScreen) == 1) {
4489 return monoDrawPiece_1bit;
4491 return monoDrawPiece;
4495 return colorDrawPieceImage;
4497 return colorDrawPiece;
4501 /* [HR] determine square color depending on chess variant. */
4503 SquareColor (int row, int column)
4507 if (gameInfo.variant == VariantXiangqi) {
4508 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4510 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4512 } else if (row <= 4) {
4518 square_color = ((column + row) % 2) == 1;
4521 /* [hgm] holdings: next line makes all holdings squares light */
4522 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4524 return square_color;
4528 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4530 int square_color, x, y, direction, font_ascent, font_descent;
4533 XCharStruct overall;
4537 /* Calculate delay in milliseconds (2-delays per complete flash) */
4538 flash_delay = 500 / appData.flashRate;
4541 x = lineGap + ((BOARD_WIDTH-1)-column) *
4542 (squareSize + lineGap);
4543 y = lineGap + row * (squareSize + lineGap);
4545 x = lineGap + column * (squareSize + lineGap);
4546 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4547 (squareSize + lineGap);
4550 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4552 square_color = SquareColor(row, column);
4554 if ( // [HGM] holdings: blank out area between board and holdings
4555 column == BOARD_LEFT-1 || column == BOARD_RGHT
4556 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4557 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4558 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4560 // [HGM] print piece counts next to holdings
4561 string[1] = NULLCHAR;
4562 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4563 string[0] = '0' + piece;
4564 XTextExtents(countFontStruct, string, 1, &direction,
4565 &font_ascent, &font_descent, &overall);
4566 if (appData.monoMode) {
4567 XDrawImageString(xDisplay, xBoardWindow, countGC,
4568 x + squareSize - overall.width - 2,
4569 y + font_ascent + 1, string, 1);
4571 XDrawString(xDisplay, xBoardWindow, countGC,
4572 x + squareSize - overall.width - 2,
4573 y + font_ascent + 1, string, 1);
4576 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4577 string[0] = '0' + piece;
4578 XTextExtents(countFontStruct, string, 1, &direction,
4579 &font_ascent, &font_descent, &overall);
4580 if (appData.monoMode) {
4581 XDrawImageString(xDisplay, xBoardWindow, countGC,
4582 x + 2, y + font_ascent + 1, string, 1);
4584 XDrawString(xDisplay, xBoardWindow, countGC,
4585 x + 2, y + font_ascent + 1, string, 1);
4589 if (piece == EmptySquare || appData.blindfold) {
4590 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4592 drawfunc = ChooseDrawFunc();
4594 if (do_flash && appData.flashCount > 0) {
4595 for (i=0; i<appData.flashCount; ++i) {
4596 drawfunc(piece, square_color, x, y, xBoardWindow);
4597 XSync(xDisplay, False);
4598 do_flash_delay(flash_delay);
4600 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4601 XSync(xDisplay, False);
4602 do_flash_delay(flash_delay);
4605 drawfunc(piece, square_color, x, y, xBoardWindow);
4609 string[1] = NULLCHAR;
4610 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4611 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4612 string[0] = 'a' + column - BOARD_LEFT;
4613 XTextExtents(coordFontStruct, string, 1, &direction,
4614 &font_ascent, &font_descent, &overall);
4615 if (appData.monoMode) {
4616 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4617 x + squareSize - overall.width - 2,
4618 y + squareSize - font_descent - 1, string, 1);
4620 XDrawString(xDisplay, xBoardWindow, coordGC,
4621 x + squareSize - overall.width - 2,
4622 y + squareSize - font_descent - 1, string, 1);
4625 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4626 string[0] = ONE + row;
4627 XTextExtents(coordFontStruct, string, 1, &direction,
4628 &font_ascent, &font_descent, &overall);
4629 if (appData.monoMode) {
4630 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4631 x + 2, y + font_ascent + 1, string, 1);
4633 XDrawString(xDisplay, xBoardWindow, coordGC,
4634 x + 2, y + font_ascent + 1, string, 1);
4637 if(!partnerUp && marker[row][column]) {
4638 if(appData.monoMode) {
4639 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4640 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4641 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4642 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4644 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4645 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4650 /* Why is this needed on some versions of X? */
4652 EventProc (Widget widget, caddr_t unused, XEvent *event)
4654 if (!XtIsRealized(widget))
4657 switch (event->type) {
4659 if (event->xexpose.count > 0) return; /* no clipping is done */
4660 XDrawPosition(widget, True, NULL);
4661 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4662 flipView = !flipView; partnerUp = !partnerUp;
4663 XDrawPosition(widget, True, NULL);
4664 flipView = !flipView; partnerUp = !partnerUp;
4668 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4676 DrawPosition (int fullRedraw, Board board)
4678 XDrawPosition(boardWidget, fullRedraw, board);
4681 /* Returns 1 if there are "too many" differences between b1 and b2
4682 (i.e. more than 1 move was made) */
4684 too_many_diffs (Board b1, Board b2)
4689 for (i=0; i<BOARD_HEIGHT; ++i) {
4690 for (j=0; j<BOARD_WIDTH; ++j) {
4691 if (b1[i][j] != b2[i][j]) {
4692 if (++c > 4) /* Castling causes 4 diffs */
4700 /* Matrix describing castling maneuvers */
4701 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4702 static int castling_matrix[4][5] = {
4703 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4704 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4705 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4706 { 7, 7, 4, 5, 6 } /* 0-0, black */
4709 /* Checks whether castling occurred. If it did, *rrow and *rcol
4710 are set to the destination (row,col) of the rook that moved.
4712 Returns 1 if castling occurred, 0 if not.
4714 Note: Only handles a max of 1 castling move, so be sure
4715 to call too_many_diffs() first.
4718 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4723 /* For each type of castling... */
4724 for (i=0; i<4; ++i) {
4725 r = castling_matrix[i];
4727 /* Check the 4 squares involved in the castling move */
4729 for (j=1; j<=4; ++j) {
4730 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4737 /* All 4 changed, so it must be a castling move */
4746 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4748 DrawSeekAxis (int x, int y, int xTo, int yTo)
4750 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4754 DrawSeekBackground (int left, int top, int right, int bottom)
4756 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4760 DrawSeekText (char *buf, int x, int y)
4762 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4766 DrawSeekDot (int x, int y, int colorNr)
4768 int square = colorNr & 0x80;
4771 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4773 XFillRectangle(xDisplay, xBoardWindow, color,
4774 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4776 XFillArc(xDisplay, xBoardWindow, color,
4777 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4780 static int damage[2][BOARD_RANKS][BOARD_FILES];
4783 * event handler for redrawing the board
4786 XDrawPosition (Widget w, int repaint, Board board)
4789 static int lastFlipView = 0;
4790 static int lastBoardValid[2] = {0, 0};
4791 static Board lastBoard[2];
4794 int nr = twoBoards*partnerUp;
4796 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4798 if (board == NULL) {
4799 if (!lastBoardValid[nr]) return;
4800 board = lastBoard[nr];
4802 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4803 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4804 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4809 * It would be simpler to clear the window with XClearWindow()
4810 * but this causes a very distracting flicker.
4813 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4815 if ( lineGap && IsDrawArrowEnabled())
4816 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4817 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4819 /* If too much changes (begin observing new game, etc.), don't
4821 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4823 /* Special check for castling so we don't flash both the king
4824 and the rook (just flash the king). */
4826 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4827 /* Draw rook with NO flashing. King will be drawn flashing later */
4828 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4829 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4833 /* First pass -- Draw (newly) empty squares and repair damage.
4834 This prevents you from having a piece show up twice while it
4835 is flashing on its new square */
4836 for (i = 0; i < BOARD_HEIGHT; i++)
4837 for (j = 0; j < BOARD_WIDTH; j++)
4838 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4839 || damage[nr][i][j]) {
4840 DrawSquare(i, j, board[i][j], 0);
4841 damage[nr][i][j] = False;
4844 /* Second pass -- Draw piece(s) in new position and flash them */
4845 for (i = 0; i < BOARD_HEIGHT; i++)
4846 for (j = 0; j < BOARD_WIDTH; j++)
4847 if (board[i][j] != lastBoard[nr][i][j]) {
4848 DrawSquare(i, j, board[i][j], do_flash);
4852 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4853 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4854 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4856 for (i = 0; i < BOARD_HEIGHT; i++)
4857 for (j = 0; j < BOARD_WIDTH; j++) {
4858 DrawSquare(i, j, board[i][j], 0);
4859 damage[nr][i][j] = False;
4863 CopyBoard(lastBoard[nr], board);
4864 lastBoardValid[nr] = 1;
4865 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4866 lastFlipView = flipView;
4868 /* Draw highlights */
4869 if (pm1X >= 0 && pm1Y >= 0) {
4870 drawHighlight(pm1X, pm1Y, prelineGC);
4872 if (pm2X >= 0 && pm2Y >= 0) {
4873 drawHighlight(pm2X, pm2Y, prelineGC);
4875 if (hi1X >= 0 && hi1Y >= 0) {
4876 drawHighlight(hi1X, hi1Y, highlineGC);
4878 if (hi2X >= 0 && hi2Y >= 0) {
4879 drawHighlight(hi2X, hi2Y, highlineGC);
4881 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4883 /* If piece being dragged around board, must redraw that too */
4886 XSync(xDisplay, False);
4891 * event handler for redrawing the board
4894 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4896 XDrawPosition(w, True, NULL);
4901 * event handler for parsing user moves
4903 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4904 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4905 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4906 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4907 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4908 // and at the end FinishMove() to perform the move after optional promotion popups.
4909 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4911 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4913 if (w != boardWidget || errorExitStatus != -1) return;
4914 if(nprms) shiftKey = !strcmp(prms[0], "1");
4917 if (event->type == ButtonPress) {
4918 XtPopdown(promotionShell);
4919 XtDestroyWidget(promotionShell);
4920 promotionUp = False;
4928 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4929 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4930 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4934 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4936 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4937 DragPieceMove(event->xmotion.x, event->xmotion.y);
4941 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4942 { // [HGM] pv: walk PV
4943 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4946 static int savedIndex; /* gross that this is global */
4949 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4952 XawTextPosition index, dummy;
4955 XawTextGetSelectionPos(w, &index, &dummy);
4956 XtSetArg(arg, XtNstring, &val);
4957 XtGetValues(w, &arg, 1);
4958 ReplaceComment(savedIndex, val);
4959 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4960 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4964 EditCommentPopUp (int index, char *title, char *text)
4967 if (text == NULL) text = "";
4968 NewCommentPopup(title, text, index);
4977 extern Option boxOptions[];
4987 edit = boxOptions[0].handle;
4989 XtSetArg(args[j], XtNstring, &val); j++;
4990 XtGetValues(edit, args, j);
4992 SendMultiLineToICS(val);
4993 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4994 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4998 ICSInputBoxPopDown ()
5004 CommentPopUp (char *title, char *text)
5006 savedIndex = currentMove; // [HGM] vari
5007 NewCommentPopup(title, text, currentMove);
5016 static char *openName;
5022 (void) (*fileProc)(openFP, 0, openName);
5026 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
5028 fileProc = proc; /* I can't see a way not */
5029 fileOpenMode = openMode; /* to use globals here */
5030 { // [HGM] use file-selector dialog stolen from Ghostview
5031 int index; // this is not supported yet
5032 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5033 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5034 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5035 ScheduleDelayedEvent(&DelayedLoad, 50);
5042 if (!filenameUp) return;
5043 XtPopdown(fileNameShell);
5044 XtDestroyWidget(fileNameShell);
5050 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5055 XtSetArg(args[0], XtNlabel, &name);
5056 XtGetValues(w, args, 1);
5058 if (strcmp(name, _("cancel")) == 0) {
5063 FileNameAction(w, NULL, NULL, NULL);
5067 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5075 name = XawDialogGetValueString(w = XtParent(w));
5077 if ((name != NULL) && (*name != NULLCHAR)) {
5078 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5079 XtPopdown(w = XtParent(XtParent(w)));
5083 p = strrchr(buf, ' ');
5090 fullname = ExpandPathName(buf);
5092 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5095 f = fopen(fullname, fileOpenMode);
5097 DisplayError(_("Failed to open file"), errno);
5099 (void) (*fileProc)(f, index, buf);
5106 XtPopdown(w = XtParent(XtParent(w)));
5116 Widget dialog, layout;
5118 Dimension bw_width, pw_width;
5120 char *PromoChars = "wglcqrbnkac+=\0";
5123 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5124 XtGetValues(boardWidget, args, j);
5127 XtSetArg(args[j], XtNresizable, True); j++;
5128 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5130 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5131 shellWidget, args, j);
5133 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5134 layoutArgs, XtNumber(layoutArgs));
5137 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5138 XtSetArg(args[j], XtNborderWidth, 0); j++;
5139 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5142 if(gameInfo.variant != VariantShogi) {
5143 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5144 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5145 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5146 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5147 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5149 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5150 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5151 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5152 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5154 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5155 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5156 gameInfo.variant == VariantGiveaway) {
5157 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5159 if(gameInfo.variant == VariantCapablanca ||
5160 gameInfo.variant == VariantGothic ||
5161 gameInfo.variant == VariantCapaRandom) {
5162 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5163 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5165 } else // [HGM] shogi
5167 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5168 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5170 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5172 XtRealizeWidget(promotionShell);
5173 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5176 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5177 XtGetValues(promotionShell, args, j);
5179 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5180 lineGap + squareSize/3 +
5181 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5182 0 : 6*(squareSize + lineGap)), &x, &y);
5185 XtSetArg(args[j], XtNx, x); j++;
5186 XtSetArg(args[j], XtNy, y); j++;
5187 XtSetValues(promotionShell, args, j);
5189 XtPopup(promotionShell, XtGrabNone);
5197 if (!promotionUp) return;
5198 XtPopdown(promotionShell);
5199 XtDestroyWidget(promotionShell);
5200 promotionUp = False;
5204 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5206 int promoChar = * (const char *) client_data;
5210 if (fromX == -1) return;
5217 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5219 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5220 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5226 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5228 dialogError = errorUp = False;
5229 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5231 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5238 if (!errorUp) return;
5239 dialogError = errorUp = False;
5240 XtPopdown(errorShell);
5241 XtDestroyWidget(errorShell);
5242 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5246 ErrorPopUp (char *title, char *label, int modal)
5249 Widget dialog, layout;
5253 Dimension bw_width, pw_width;
5254 Dimension pw_height;
5258 XtSetArg(args[i], XtNresizable, True); i++;
5259 XtSetArg(args[i], XtNtitle, title); i++;
5261 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5262 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
5264 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5265 layoutArgs, XtNumber(layoutArgs));
5268 XtSetArg(args[i], XtNlabel, label); i++;
5269 XtSetArg(args[i], XtNborderWidth, 0); i++;
5270 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5273 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5275 XtRealizeWidget(errorShell);
5276 CatchDeleteWindow(errorShell, "ErrorPopDown");
5279 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5280 XtGetValues(boardWidget, args, i);
5282 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5283 XtSetArg(args[i], XtNheight, &pw_height); i++;
5284 XtGetValues(errorShell, args, i);
5287 /* This code seems to tickle an X bug if it is executed too soon
5288 after xboard starts up. The coordinates get transformed as if
5289 the main window was positioned at (0, 0).
5291 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5292 0 - pw_height + squareSize / 3, &x, &y);
5294 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5295 RootWindowOfScreen(XtScreen(boardWidget)),
5296 (bw_width - pw_width) / 2,
5297 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5301 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5304 XtSetArg(args[i], XtNx, x); i++;
5305 XtSetArg(args[i], XtNy, y); i++;
5306 XtSetValues(errorShell, args, i);
5309 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5312 /* Disable all user input other than deleting the window */
5313 static int frozen = 0;
5319 /* Grab by a widget that doesn't accept input */
5320 XtAddGrab(messageWidget, TRUE, FALSE);
5324 /* Undo a FreezeUI */
5328 if (!frozen) return;
5329 XtRemoveGrab(messageWidget);
5334 ModeToWidgetName (GameMode mode)
5337 case BeginningOfGame:
5338 if (appData.icsActive)
5339 return "menuMode.ICS Client";
5340 else if (appData.noChessProgram ||
5341 *appData.cmailGameName != NULLCHAR)
5342 return "menuMode.Edit Game";
5344 return "menuMode.Machine Black";
5345 case MachinePlaysBlack:
5346 return "menuMode.Machine Black";
5347 case MachinePlaysWhite:
5348 return "menuMode.Machine White";
5350 return "menuMode.Analysis Mode";
5352 return "menuMode.Analyze File";
5353 case TwoMachinesPlay:
5354 return "menuMode.Two Machines";
5356 return "menuMode.Edit Game";
5357 case PlayFromGameFile:
5358 return "menuFile.Load Game";
5360 return "menuMode.Edit Position";
5362 return "menuMode.Training";
5363 case IcsPlayingWhite:
5364 case IcsPlayingBlack:
5368 return "menuMode.ICS Client";
5379 static int oldPausing = FALSE;
5380 static GameMode oldmode = (GameMode) -1;
5383 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5385 if (pausing != oldPausing) {
5386 oldPausing = pausing;
5388 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5390 XtSetArg(args[0], XtNleftBitmap, None);
5392 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5395 if (appData.showButtonBar) {
5396 /* Always toggle, don't set. Previous code messes up when
5397 invoked while the button is pressed, as releasing it
5398 toggles the state again. */
5401 XtSetArg(args[0], XtNbackground, &oldbg);
5402 XtSetArg(args[1], XtNforeground, &oldfg);
5403 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5405 XtSetArg(args[0], XtNbackground, oldfg);
5406 XtSetArg(args[1], XtNforeground, oldbg);
5408 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5412 wname = ModeToWidgetName(oldmode);
5413 if (wname != NULL) {
5414 XtSetArg(args[0], XtNleftBitmap, None);
5415 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5417 wname = ModeToWidgetName(gameMode);
5418 if (wname != NULL) {
5419 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5420 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5423 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5424 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5426 /* Maybe all the enables should be handled here, not just this one */
5427 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5428 gameMode == Training || gameMode == PlayFromGameFile);
5433 * Button/menu procedures
5436 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5442 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5444 cmailMsgLoaded = FALSE;
5445 if (gameNumber == 0) {
5446 int error = GameListBuild(f);
5448 DisplayError(_("Cannot build game list"), error);
5449 } else if (!ListEmpty(&gameList) &&
5450 ((ListGame *) gameList.tailPred)->number > 1) {
5451 GameListPopUp(f, title);
5457 return LoadGame(f, gameNumber, title, FALSE);
5461 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5463 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5466 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5470 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5476 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5482 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5488 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5494 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5500 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5506 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5508 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5511 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5515 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5517 FileNamePopUp(_("Save game file name?"),
5518 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5519 appData.oldSaveStyle ? ".game" : ".pgn",
5524 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5526 FileNamePopUp(_("Save position file name?"),
5527 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5528 appData.oldSaveStyle ? ".pos" : ".fen",
5533 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5535 ReloadCmailMsgEvent(FALSE);
5539 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5544 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5545 char *selected_fen_position=NULL;
5548 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5549 Atom *type_return, XtPointer *value_return,
5550 unsigned long *length_return, int *format_return)
5552 char *selection_tmp;
5554 if (!selected_fen_position) return False; /* should never happen */
5555 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5556 /* note: since no XtSelectionDoneProc was registered, Xt will
5557 * automatically call XtFree on the value returned. So have to
5558 * make a copy of it allocated with XtMalloc */
5559 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5560 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5562 *value_return=selection_tmp;
5563 *length_return=strlen(selection_tmp);
5564 *type_return=*target;
5565 *format_return = 8; /* bits per byte */
5567 } else if (*target == XA_TARGETS(xDisplay)) {
5568 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5569 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5570 targets_tmp[1] = XA_STRING;
5571 *value_return = targets_tmp;
5572 *type_return = XA_ATOM;
5575 // This code leads to a read of value_return out of bounds on 64-bit systems.
5576 // Other code which I have seen always sets *format_return to 32 independent of
5577 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5578 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5579 *format_return = 8 * sizeof(Atom);
5580 if (*format_return > 32) {
5581 *length_return *= *format_return / 32;
5582 *format_return = 32;
5585 *format_return = 32;
5593 /* note: when called from menu all parameters are NULL, so no clue what the
5594 * Widget which was clicked on was, or what the click event was
5597 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5600 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5601 * have a notion of a position that is selected but not copied.
5602 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5604 if(gameMode == EditPosition) EditPositionDone(TRUE);
5605 if (selected_fen_position) free(selected_fen_position);
5606 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5607 if (!selected_fen_position) return;
5608 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5610 SendPositionSelection,
5611 NULL/* lose_ownership_proc */ ,
5612 NULL/* transfer_done_proc */);
5613 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5615 SendPositionSelection,
5616 NULL/* lose_ownership_proc */ ,
5617 NULL/* transfer_done_proc */);
5620 /* function called when the data to Paste is ready */
5622 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5623 Atom *type, XtPointer value, unsigned long *len, int *format)
5626 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5627 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5628 EditPositionPasteFEN(fenstr);
5632 /* called when Paste Position button is pressed,
5633 * all parameters will be NULL */
5634 void PastePositionProc(w, event, prms, nprms)
5640 XtGetSelectionValue(menuBarWidget,
5641 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5642 /* (XtSelectionCallbackProc) */ PastePositionCB,
5643 NULL, /* client_data passed to PastePositionCB */
5645 /* better to use the time field from the event that triggered the
5646 * call to this function, but that isn't trivial to get
5654 SendGameSelection (Widget w, Atom *selection, Atom *target,
5655 Atom *type_return, XtPointer *value_return,
5656 unsigned long *length_return, int *format_return)
5658 char *selection_tmp;
5660 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5661 FILE* f = fopen(gameCopyFilename, "r");
5664 if (f == NULL) return False;
5668 selection_tmp = XtMalloc(len + 1);
5669 count = fread(selection_tmp, 1, len, f);
5672 XtFree(selection_tmp);
5675 selection_tmp[len] = NULLCHAR;
5676 *value_return = selection_tmp;
5677 *length_return = len;
5678 *type_return = *target;
5679 *format_return = 8; /* bits per byte */
5681 } else if (*target == XA_TARGETS(xDisplay)) {
5682 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5683 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5684 targets_tmp[1] = XA_STRING;
5685 *value_return = targets_tmp;
5686 *type_return = XA_ATOM;
5689 // This code leads to a read of value_return out of bounds on 64-bit systems.
5690 // Other code which I have seen always sets *format_return to 32 independent of
5691 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5692 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5693 *format_return = 8 * sizeof(Atom);
5694 if (*format_return > 32) {
5695 *length_return *= *format_return / 32;
5696 *format_return = 32;
5699 *format_return = 32;
5711 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5712 * have a notion of a game that is selected but not copied.
5713 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5715 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5718 NULL/* lose_ownership_proc */ ,
5719 NULL/* transfer_done_proc */);
5720 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5723 NULL/* lose_ownership_proc */ ,
5724 NULL/* transfer_done_proc */);
5727 /* note: when called from menu all parameters are NULL, so no clue what the
5728 * Widget which was clicked on was, or what the click event was
5731 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5735 ret = SaveGameToFile(gameCopyFilename, FALSE);
5742 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5744 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5748 /* function called when the data to Paste is ready */
5750 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5751 Atom *type, XtPointer value, unsigned long *len, int *format)
5754 if (value == NULL || *len == 0) {
5755 return; /* nothing had been selected to copy */
5757 f = fopen(gamePasteFilename, "w");
5759 DisplayError(_("Can't open temp file"), errno);
5762 fwrite(value, 1, *len, f);
5765 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5768 /* called when Paste Game button is pressed,
5769 * all parameters will be NULL */
5771 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5773 XtGetSelectionValue(menuBarWidget,
5774 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5775 /* (XtSelectionCallbackProc) */ PasteGameCB,
5776 NULL, /* client_data passed to PasteGameCB */
5778 /* better to use the time field from the event that triggered the
5779 * call to this function, but that isn't trivial to get
5790 SaveGameProc(NULL, NULL, NULL, NULL);
5795 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5801 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5807 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5809 MachineBlackEvent();
5813 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5815 MachineWhiteEvent();
5819 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5823 if (!first.analysisSupport) {
5824 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5825 DisplayError(buf, 0);
5828 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5829 if (appData.icsActive) {
5830 if (gameMode != IcsObserving) {
5831 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5832 DisplayError(buf, 0);
5834 if (appData.icsEngineAnalyze) {
5835 if (appData.debugMode)
5836 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5842 /* if enable, use want disable icsEngineAnalyze */
5843 if (appData.icsEngineAnalyze) {
5848 appData.icsEngineAnalyze = TRUE;
5849 if (appData.debugMode)
5850 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5852 #ifndef OPTIONSDIALOG
5853 if (!appData.showThinking)
5854 ShowThinkingProc(w,event,prms,nprms);
5861 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5863 if (!first.analysisSupport) {
5865 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5866 DisplayError(buf, 0);
5869 // Reset(FALSE, TRUE);
5870 #ifndef OPTIONSDIALOG
5871 if (!appData.showThinking)
5872 ShowThinkingProc(w,event,prms,nprms);
5875 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5876 AnalysisPeriodicEvent(1);
5880 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5886 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5892 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5898 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5904 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5906 EditPositionEvent();
5910 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5916 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5920 if (PopDown(1)) { // popdown succesful
5922 XtSetArg(args[j], XtNleftBitmap, None); j++;
5923 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5924 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5925 } else // was not up
5930 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5932 if (!PopDown(4)) ICSInputBoxPopUp();
5936 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5942 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5948 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5954 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5960 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5966 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5972 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5978 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5984 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5986 UserAdjudicationEvent(+1);
5990 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5992 UserAdjudicationEvent(-1);
5996 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5998 UserAdjudicationEvent(0);
6002 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6004 if (shellUp[4] == True)
6009 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6010 { // [HGM] input: let up-arrow recall previous line from history
6017 if (!shellUp[4]) return;
6018 edit = boxOptions[0].handle;
6020 XtSetArg(args[j], XtNstring, &val); j++;
6021 XtGetValues(edit, args, j);
6022 val = PrevInHistory(val);
6023 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6024 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6026 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6027 XawTextReplace(edit, 0, 0, &t);
6028 XawTextSetInsertionPoint(edit, 9999);
6033 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6034 { // [HGM] input: let down-arrow recall next line from history
6039 if (!shellUp[4]) return;
6040 edit = boxOptions[0].handle;
6041 val = NextInHistory();
6042 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6043 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6045 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6046 XawTextReplace(edit, 0, 0, &t);
6047 XawTextSetInsertionPoint(edit, 9999);
6052 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6054 StopObservingEvent();
6058 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6060 StopExaminingEvent();
6064 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6071 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6078 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6084 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6086 if (!TempBackwardActive) {
6087 TempBackwardActive = True;
6093 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6095 /* Check to see if triggered by a key release event for a repeating key.
6096 * If so the next queued event will be a key press of the same key at the same time */
6097 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6099 XPeekEvent(xDisplay, &next);
6100 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6101 next.xkey.keycode == event->xkey.keycode)
6105 TempBackwardActive = False;
6109 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6115 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6121 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6127 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6133 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6135 TruncateGameEvent();
6139 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6145 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6151 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6153 flipView = !flipView;
6154 DrawPosition(True, NULL);
6158 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6162 PonderNextMoveEvent(!appData.ponderNextMove);
6163 #ifndef OPTIONSDIALOG
6164 if (appData.ponderNextMove) {
6165 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6167 XtSetArg(args[0], XtNleftBitmap, None);
6169 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6174 #ifndef OPTIONSDIALOG
6176 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6180 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6182 if (appData.alwaysPromoteToQueen) {
6183 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6185 XtSetArg(args[0], XtNleftBitmap, None);
6187 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6192 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6196 appData.animateDragging = !appData.animateDragging;
6198 if (appData.animateDragging) {
6199 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6202 XtSetArg(args[0], XtNleftBitmap, None);
6204 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6209 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6213 appData.animate = !appData.animate;
6215 if (appData.animate) {
6216 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6219 XtSetArg(args[0], XtNleftBitmap, None);
6221 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6226 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6230 appData.autoCallFlag = !appData.autoCallFlag;
6232 if (appData.autoCallFlag) {
6233 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6235 XtSetArg(args[0], XtNleftBitmap, None);
6237 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6242 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6246 appData.autoFlipView = !appData.autoFlipView;
6248 if (appData.autoFlipView) {
6249 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6251 XtSetArg(args[0], XtNleftBitmap, None);
6253 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6258 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6262 appData.blindfold = !appData.blindfold;
6264 if (appData.blindfold) {
6265 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6267 XtSetArg(args[0], XtNleftBitmap, None);
6269 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6272 DrawPosition(True, NULL);
6276 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6280 appData.testLegality = !appData.testLegality;
6282 if (appData.testLegality) {
6283 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6285 XtSetArg(args[0], XtNleftBitmap, None);
6287 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6293 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6297 if (appData.flashCount == 0) {
6298 appData.flashCount = 3;
6300 appData.flashCount = -appData.flashCount;
6303 if (appData.flashCount > 0) {
6304 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6306 XtSetArg(args[0], XtNleftBitmap, None);
6308 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6314 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6318 appData.highlightDragging = !appData.highlightDragging;
6320 if (appData.highlightDragging) {
6321 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6323 XtSetArg(args[0], XtNleftBitmap, None);
6325 XtSetValues(XtNameToWidget(menuBarWidget,
6326 "menuOptions.Highlight Dragging"), args, 1);
6331 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6335 appData.highlightLastMove = !appData.highlightLastMove;
6337 if (appData.highlightLastMove) {
6338 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6340 XtSetArg(args[0], XtNleftBitmap, None);
6342 XtSetValues(XtNameToWidget(menuBarWidget,
6343 "menuOptions.Highlight Last Move"), args, 1);
6347 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6351 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6353 if (appData.highlightMoveWithArrow) {
6354 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6356 XtSetArg(args[0], XtNleftBitmap, None);
6358 XtSetValues(XtNameToWidget(menuBarWidget,
6359 "menuOptions.Arrow"), args, 1);
6364 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6368 appData.icsAlarm = !appData.icsAlarm;
6370 if (appData.icsAlarm) {
6371 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6373 XtSetArg(args[0], XtNleftBitmap, None);
6375 XtSetValues(XtNameToWidget(menuBarWidget,
6376 "menuOptions.ICS Alarm"), args, 1);
6381 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6385 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6387 if (appData.ringBellAfterMoves) {
6388 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6390 XtSetArg(args[0], XtNleftBitmap, None);
6392 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6397 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6401 appData.oneClick = !appData.oneClick;
6403 if (appData.oneClick) {
6404 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6406 XtSetArg(args[0], XtNleftBitmap, None);
6408 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6413 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6417 PeriodicUpdatesEvent(!appData.periodicUpdates);
6419 if (appData.periodicUpdates) {
6420 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6422 XtSetArg(args[0], XtNleftBitmap, None);
6424 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6429 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6433 appData.popupExitMessage = !appData.popupExitMessage;
6435 if (appData.popupExitMessage) {
6436 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6438 XtSetArg(args[0], XtNleftBitmap, None);
6440 XtSetValues(XtNameToWidget(menuBarWidget,
6441 "menuOptions.Popup Exit Message"), args, 1);
6445 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6449 appData.popupMoveErrors = !appData.popupMoveErrors;
6451 if (appData.popupMoveErrors) {
6452 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6454 XtSetArg(args[0], XtNleftBitmap, None);
6456 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6462 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6466 appData.premove = !appData.premove;
6468 if (appData.premove) {
6469 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6471 XtSetArg(args[0], XtNleftBitmap, None);
6473 XtSetValues(XtNameToWidget(menuBarWidget,
6474 "menuOptions.Premove"), args, 1);
6479 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6483 appData.showCoords = !appData.showCoords;
6485 if (appData.showCoords) {
6486 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6488 XtSetArg(args[0], XtNleftBitmap, None);
6490 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6493 DrawPosition(True, NULL);
6497 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6499 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6500 ShowThinkingEvent();
6504 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6508 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6509 ShowThinkingEvent();
6511 if (appData.hideThinkingFromHuman) {
6512 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6514 XtSetArg(args[0], XtNleftBitmap, None);
6516 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6522 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6526 saveSettingsOnExit = !saveSettingsOnExit;
6528 if (saveSettingsOnExit) {
6529 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6531 XtSetArg(args[0], XtNleftBitmap, None);
6533 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6538 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6540 SaveSettings(settingsFileName);
6544 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6547 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6553 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6557 if (nprms && *nprms > 0)
6561 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6566 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6572 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6578 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6582 char *zippy = _(" (with Zippy code)");
6586 snprintf(buf, sizeof(buf),
6588 "Copyright 1991 Digital Equipment Corporation\n"
6589 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6590 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6591 "%s is free software and carries NO WARRANTY;"
6592 "see the file COPYING for more information."),
6593 programVersion, zippy, PACKAGE);
6594 ErrorPopUp(_("About XBoard"), buf, FALSE);
6598 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6600 appData.debugMode = !appData.debugMode;
6604 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6610 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6616 DisplayMessage (char *message, char *extMessage)
6618 /* display a message in the message widget */
6627 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6632 message = extMessage;
6636 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6638 /* need to test if messageWidget already exists, since this function
6639 can also be called during the startup, if for example a Xresource
6640 is not set up correctly */
6643 XtSetArg(arg, XtNlabel, message);
6644 XtSetValues(messageWidget, &arg, 1);
6651 DisplayTitle (char *text)
6655 char title[MSG_SIZ];
6658 if (text == NULL) text = "";
6660 if (appData.titleInWindow) {
6662 XtSetArg(args[i], XtNlabel, text); i++;
6663 XtSetValues(titleWidget, args, i);
6666 if (*text != NULLCHAR) {
6667 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6668 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6669 } else if (appData.icsActive) {
6670 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6671 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6672 } else if (appData.cmailGameName[0] != NULLCHAR) {
6673 snprintf(icon, sizeof(icon), "%s", "CMail");
6674 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6676 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6677 } else if (gameInfo.variant == VariantGothic) {
6678 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6679 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6682 } else if (gameInfo.variant == VariantFalcon) {
6683 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6684 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6686 } else if (appData.noChessProgram) {
6687 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6688 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6690 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6691 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6694 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6695 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6696 XtSetValues(shellWidget, args, i);
6697 XSync(xDisplay, False);
6702 DisplayError (String message, int error)
6707 if (appData.debugMode || appData.matchMode) {
6708 fprintf(stderr, "%s: %s\n", programName, message);
6711 if (appData.debugMode || appData.matchMode) {
6712 fprintf(stderr, "%s: %s: %s\n",
6713 programName, message, strerror(error));
6715 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6718 ErrorPopUp(_("Error"), message, FALSE);
6723 DisplayMoveError (String message)
6727 DrawPosition(FALSE, NULL);
6728 if (appData.debugMode || appData.matchMode) {
6729 fprintf(stderr, "%s: %s\n", programName, message);
6731 if (appData.popupMoveErrors) {
6732 ErrorPopUp(_("Error"), message, FALSE);
6734 DisplayMessage(message, "");
6740 DisplayFatalError (String message, int error, int status)
6744 errorExitStatus = status;
6746 fprintf(stderr, "%s: %s\n", programName, message);
6748 fprintf(stderr, "%s: %s: %s\n",
6749 programName, message, strerror(error));
6750 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6753 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6754 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6761 DisplayInformation (String message)
6764 ErrorPopUp(_("Information"), message, TRUE);
6768 DisplayNote (String message)
6771 ErrorPopUp(_("Note"), message, FALSE);
6775 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6781 DisplayIcsInteractionTitle (String message)
6783 if (oldICSInteractionTitle == NULL) {
6784 /* Magic to find the old window title, adapted from vim */
6785 char *wina = getenv("WINDOWID");
6787 Window win = (Window) atoi(wina);
6788 Window root, parent, *children;
6789 unsigned int nchildren;
6790 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6792 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6793 if (!XQueryTree(xDisplay, win, &root, &parent,
6794 &children, &nchildren)) break;
6795 if (children) XFree((void *)children);
6796 if (parent == root || parent == 0) break;
6799 XSetErrorHandler(oldHandler);
6801 if (oldICSInteractionTitle == NULL) {
6802 oldICSInteractionTitle = "xterm";
6805 printf("\033]0;%s\007", message);
6809 char pendingReplyPrefix[MSG_SIZ];
6810 ProcRef pendingReplyPR;
6813 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6816 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6820 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6824 AskQuestionPopDown ()
6826 if (!askQuestionUp) return;
6827 XtPopdown(askQuestionShell);
6828 XtDestroyWidget(askQuestionShell);
6829 askQuestionUp = False;
6833 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6839 reply = XawDialogGetValueString(w = XtParent(w));
6840 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6841 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6842 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6843 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6844 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6845 AskQuestionPopDown();
6847 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6851 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6856 XtSetArg(args[0], XtNlabel, &name);
6857 XtGetValues(w, args, 1);
6859 if (strcmp(name, _("cancel")) == 0) {
6860 AskQuestionPopDown();
6862 AskQuestionReplyAction(w, NULL, NULL, NULL);
6867 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6870 Widget popup, layout, dialog, edit;
6876 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6877 pendingReplyPR = pr;
6880 XtSetArg(args[i], XtNresizable, True); i++;
6881 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6882 askQuestionShell = popup =
6883 XtCreatePopupShell(title, transientShellWidgetClass,
6884 shellWidget, args, i);
6887 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6888 layoutArgs, XtNumber(layoutArgs));
6891 XtSetArg(args[i], XtNlabel, question); i++;
6892 XtSetArg(args[i], XtNvalue, ""); i++;
6893 XtSetArg(args[i], XtNborderWidth, 0); i++;
6894 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6897 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6898 (XtPointer) dialog);
6899 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6900 (XtPointer) dialog);
6902 XtRealizeWidget(popup);
6903 CatchDeleteWindow(popup, "AskQuestionPopDown");
6905 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6906 &x, &y, &win_x, &win_y, &mask);
6908 XtSetArg(args[0], XtNx, x - 10);
6909 XtSetArg(args[1], XtNy, y - 30);
6910 XtSetValues(popup, args, 2);
6912 XtPopup(popup, XtGrabExclusive);
6913 askQuestionUp = True;
6915 edit = XtNameToWidget(dialog, "*value");
6916 XtSetKeyboardFocus(popup, edit);
6921 PlaySound (char *name)
6923 if (*name == NULLCHAR) {
6925 } else if (strcmp(name, "$") == 0) {
6926 putc(BELLCHAR, stderr);
6929 char *prefix = "", *sep = "";
6930 if(appData.soundProgram[0] == NULLCHAR) return;
6931 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6932 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6940 PlaySound(appData.soundMove);
6946 PlaySound(appData.soundIcsWin);
6952 PlaySound(appData.soundIcsLoss);
6958 PlaySound(appData.soundIcsDraw);
6962 PlayIcsUnfinishedSound ()
6964 PlaySound(appData.soundIcsUnfinished);
6970 PlaySound(appData.soundIcsAlarm);
6976 PlaySound(appData.soundTell);
6982 system("stty echo");
6989 system("stty -echo");
6994 RunCommand (char *buf)
7000 Colorize (ColorClass cc, int continuation)
7003 int count, outCount, error;
7005 if (textColors[(int)cc].bg > 0) {
7006 if (textColors[(int)cc].fg > 0) {
7007 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7008 textColors[(int)cc].fg, textColors[(int)cc].bg);
7010 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7011 textColors[(int)cc].bg);
7014 if (textColors[(int)cc].fg > 0) {
7015 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7016 textColors[(int)cc].fg);
7018 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7021 count = strlen(buf);
7022 outCount = OutputToProcess(NoProc, buf, count, &error);
7023 if (outCount < count) {
7024 DisplayFatalError(_("Error writing to display"), error, 1);
7027 if (continuation) return;
7030 PlaySound(appData.soundShout);
7033 PlaySound(appData.soundSShout);
7036 PlaySound(appData.soundChannel1);
7039 PlaySound(appData.soundChannel);
7042 PlaySound(appData.soundKibitz);
7045 PlaySound(appData.soundTell);
7047 case ColorChallenge:
7048 PlaySound(appData.soundChallenge);
7051 PlaySound(appData.soundRequest);
7054 PlaySound(appData.soundSeek);
7066 return getpwuid(getuid())->pw_name;
7070 ExpandPathName (char *path)
7072 static char static_buf[4*MSG_SIZ];
7073 char *d, *s, buf[4*MSG_SIZ];
7079 while (*s && isspace(*s))
7088 if (*(s+1) == '/') {
7089 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7093 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7094 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7095 pwd = getpwnam(buf);
7098 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7102 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7103 strcat(d, strchr(s+1, '/'));
7107 safeStrCpy(d, s, 4*MSG_SIZ );
7115 static char host_name[MSG_SIZ];
7117 #if HAVE_GETHOSTNAME
7118 gethostname(host_name, MSG_SIZ);
7120 #else /* not HAVE_GETHOSTNAME */
7121 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7122 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7124 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7126 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7127 #endif /* not HAVE_GETHOSTNAME */
7130 XtIntervalId delayedEventTimerXID = 0;
7131 DelayedEventCallback delayedEventCallback = 0;
7136 delayedEventTimerXID = 0;
7137 delayedEventCallback();
7141 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7143 if(delayedEventTimerXID && delayedEventCallback == cb)
7144 // [HGM] alive: replace, rather than add or flush identical event
7145 XtRemoveTimeOut(delayedEventTimerXID);
7146 delayedEventCallback = cb;
7147 delayedEventTimerXID =
7148 XtAppAddTimeOut(appContext, millisec,
7149 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7152 DelayedEventCallback
7155 if (delayedEventTimerXID) {
7156 return delayedEventCallback;
7163 CancelDelayedEvent ()
7165 if (delayedEventTimerXID) {
7166 XtRemoveTimeOut(delayedEventTimerXID);
7167 delayedEventTimerXID = 0;
7171 XtIntervalId loadGameTimerXID = 0;
7174 LoadGameTimerRunning ()
7176 return loadGameTimerXID != 0;
7180 StopLoadGameTimer ()
7182 if (loadGameTimerXID != 0) {
7183 XtRemoveTimeOut(loadGameTimerXID);
7184 loadGameTimerXID = 0;
7192 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7194 loadGameTimerXID = 0;
7199 StartLoadGameTimer (long millisec)
7202 XtAppAddTimeOut(appContext, millisec,
7203 (XtTimerCallbackProc) LoadGameTimerCallback,
7207 XtIntervalId analysisClockXID = 0;
7210 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7212 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7213 || appData.icsEngineAnalyze) { // [DM]
7214 AnalysisPeriodicEvent(0);
7215 StartAnalysisClock();
7220 StartAnalysisClock ()
7223 XtAppAddTimeOut(appContext, 2000,
7224 (XtTimerCallbackProc) AnalysisClockCallback,
7228 XtIntervalId clockTimerXID = 0;
7231 ClockTimerRunning ()
7233 return clockTimerXID != 0;
7239 if (clockTimerXID != 0) {
7240 XtRemoveTimeOut(clockTimerXID);
7249 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7256 StartClockTimer (long millisec)
7259 XtAppAddTimeOut(appContext, millisec,
7260 (XtTimerCallbackProc) ClockTimerCallback,
7265 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7270 /* check for low time warning */
7271 Pixel foregroundOrWarningColor = timerForegroundPixel;
7274 appData.lowTimeWarning &&
7275 (timer / 1000) < appData.icsAlarmTime)
7276 foregroundOrWarningColor = lowTimeWarningColor;
7278 if (appData.clockMode) {
7279 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7280 XtSetArg(args[0], XtNlabel, buf);
7282 snprintf(buf, MSG_SIZ, "%s ", color);
7283 XtSetArg(args[0], XtNlabel, buf);
7288 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7289 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7291 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7292 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7295 XtSetValues(w, args, 3);
7299 DisplayWhiteClock (long timeRemaining, int highlight)
7303 if(appData.noGUI) return;
7304 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7305 if (highlight && iconPixmap == bIconPixmap) {
7306 iconPixmap = wIconPixmap;
7307 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7308 XtSetValues(shellWidget, args, 1);
7313 DisplayBlackClock (long timeRemaining, int highlight)
7317 if(appData.noGUI) return;
7318 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7319 if (highlight && iconPixmap == wIconPixmap) {
7320 iconPixmap = bIconPixmap;
7321 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7322 XtSetValues(shellWidget, args, 1);
7341 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7345 int to_prog[2], from_prog[2];
7349 if (appData.debugMode) {
7350 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7353 /* We do NOT feed the cmdLine to the shell; we just
7354 parse it into blank-separated arguments in the
7355 most simple-minded way possible.
7358 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7361 while(*p == ' ') p++;
7363 if(*p == '"' || *p == '\'')
7364 p = strchr(++argv[i-1], *p);
7365 else p = strchr(p, ' ');
7366 if (p == NULL) break;
7371 SetUpChildIO(to_prog, from_prog);
7373 if ((pid = fork()) == 0) {
7375 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7376 close(to_prog[1]); // first close the unused pipe ends
7377 close(from_prog[0]);
7378 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7379 dup2(from_prog[1], 1);
7380 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7381 close(from_prog[1]); // and closing again loses one of the pipes!
7382 if(fileno(stderr) >= 2) // better safe than sorry...
7383 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7385 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7390 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7392 execvp(argv[0], argv);
7394 /* If we get here, exec failed */
7399 /* Parent process */
7401 close(from_prog[1]);
7403 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7406 cp->fdFrom = from_prog[0];
7407 cp->fdTo = to_prog[1];
7412 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7414 AlarmCallBack (int n)
7420 DestroyChildProcess (ProcRef pr, int signalType)
7422 ChildProc *cp = (ChildProc *) pr;
7424 if (cp->kind != CPReal) return;
7426 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7427 signal(SIGALRM, AlarmCallBack);
7429 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7430 kill(cp->pid, SIGKILL); // kill it forcefully
7431 wait((int *) 0); // and wait again
7435 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7437 /* Process is exiting either because of the kill or because of
7438 a quit command sent by the backend; either way, wait for it to die.
7447 InterruptChildProcess (ProcRef pr)
7449 ChildProc *cp = (ChildProc *) pr;
7451 if (cp->kind != CPReal) return;
7452 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7456 OpenTelnet (char *host, char *port, ProcRef *pr)
7458 char cmdLine[MSG_SIZ];
7460 if (port[0] == NULLCHAR) {
7461 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7463 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7465 return StartChildProcess(cmdLine, "", pr);
7469 OpenTCP (char *host, char *port, ProcRef *pr)
7472 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7473 #else /* !OMIT_SOCKETS */
7474 struct addrinfo hints;
7475 struct addrinfo *ais, *ai;
7480 memset(&hints, 0, sizeof(hints));
7481 hints.ai_family = AF_UNSPEC;
7482 hints.ai_socktype = SOCK_STREAM;
7484 error = getaddrinfo(host, port, &hints, &ais);
7486 /* a getaddrinfo error is not an errno, so can't return it */
7487 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7488 host, port, gai_strerror(error));
7492 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7493 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7497 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7510 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7516 #endif /* !OMIT_SOCKETS */
7522 OpenCommPort (char *name, ProcRef *pr)
7527 fd = open(name, 2, 0);
7528 if (fd < 0) return errno;
7530 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7541 OpenLoopback (ProcRef *pr)
7546 SetUpChildIO(to, from);
7548 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7551 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7559 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7561 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7565 #define INPUT_SOURCE_BUF_SIZE 8192
7574 char buf[INPUT_SOURCE_BUF_SIZE];
7579 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7581 InputSource *is = (InputSource *) closure;
7586 if (is->lineByLine) {
7587 count = read(is->fd, is->unused,
7588 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7590 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7593 is->unused += count;
7595 while (p < is->unused) {
7596 q = memchr(p, '\n', is->unused - p);
7597 if (q == NULL) break;
7599 (is->func)(is, is->closure, p, q - p, 0);
7603 while (p < is->unused) {
7608 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7613 (is->func)(is, is->closure, is->buf, count, error);
7618 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7621 ChildProc *cp = (ChildProc *) pr;
7623 is = (InputSource *) calloc(1, sizeof(InputSource));
7624 is->lineByLine = lineByLine;
7628 is->fd = fileno(stdin);
7630 is->kind = cp->kind;
7631 is->fd = cp->fdFrom;
7634 is->unused = is->buf;
7637 is->xid = XtAppAddInput(appContext, is->fd,
7638 (XtPointer) (XtInputReadMask),
7639 (XtInputCallbackProc) DoInputCallback,
7641 is->closure = closure;
7642 return (InputSourceRef) is;
7646 RemoveInputSource (InputSourceRef isr)
7648 InputSource *is = (InputSource *) isr;
7650 if (is->xid == 0) return;
7651 XtRemoveInput(is->xid);
7656 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7658 static int line = 0;
7659 ChildProc *cp = (ChildProc *) pr;
7664 if (appData.noJoin || !appData.useInternalWrap)
7665 outCount = fwrite(message, 1, count, stdout);
7668 int width = get_term_width();
7669 int len = wrap(NULL, message, count, width, &line);
7670 char *msg = malloc(len);
7674 outCount = fwrite(message, 1, count, stdout);
7677 dbgchk = wrap(msg, message, count, width, &line);
7678 if (dbgchk != len && appData.debugMode)
7679 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7680 outCount = fwrite(msg, 1, dbgchk, stdout);
7686 outCount = write(cp->fdTo, message, count);
7696 /* Output message to process, with "ms" milliseconds of delay
7697 between each character. This is needed when sending the logon
7698 script to ICC, which for some reason doesn't like the
7699 instantaneous send. */
7701 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7703 ChildProc *cp = (ChildProc *) pr;
7708 r = write(cp->fdTo, message++, 1);
7721 /**** Animation code by Hugh Fisher, DCS, ANU.
7723 Known problem: if a window overlapping the board is
7724 moved away while a piece is being animated underneath,
7725 the newly exposed area won't be updated properly.
7726 I can live with this.
7728 Known problem: if you look carefully at the animation
7729 of pieces in mono mode, they are being drawn as solid
7730 shapes without interior detail while moving. Fixing
7731 this would be a major complication for minimal return.
7734 /* Masks for XPM pieces. Black and white pieces can have
7735 different shapes, but in the interest of retaining my
7736 sanity pieces must have the same outline on both light
7737 and dark squares, and all pieces must use the same
7738 background square colors/images. */
7740 static int xpmDone = 0;
7743 CreateAnimMasks (int pieceDepth)
7749 unsigned long plane;
7752 /* Need a bitmap just to get a GC with right depth */
7753 buf = XCreatePixmap(xDisplay, xBoardWindow,
7755 values.foreground = 1;
7756 values.background = 0;
7757 /* Don't use XtGetGC, not read only */
7758 maskGC = XCreateGC(xDisplay, buf,
7759 GCForeground | GCBackground, &values);
7760 XFreePixmap(xDisplay, buf);
7762 buf = XCreatePixmap(xDisplay, xBoardWindow,
7763 squareSize, squareSize, pieceDepth);
7764 values.foreground = XBlackPixel(xDisplay, xScreen);
7765 values.background = XWhitePixel(xDisplay, xScreen);
7766 bufGC = XCreateGC(xDisplay, buf,
7767 GCForeground | GCBackground, &values);
7769 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7770 /* Begin with empty mask */
7771 if(!xpmDone) // [HGM] pieces: keep using existing
7772 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7773 squareSize, squareSize, 1);
7774 XSetFunction(xDisplay, maskGC, GXclear);
7775 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7776 0, 0, squareSize, squareSize);
7778 /* Take a copy of the piece */
7783 XSetFunction(xDisplay, bufGC, GXcopy);
7784 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7786 0, 0, squareSize, squareSize, 0, 0);
7788 /* XOR the background (light) over the piece */
7789 XSetFunction(xDisplay, bufGC, GXxor);
7791 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7792 0, 0, squareSize, squareSize, 0, 0);
7794 XSetForeground(xDisplay, bufGC, lightSquareColor);
7795 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7798 /* We now have an inverted piece image with the background
7799 erased. Construct mask by just selecting all the non-zero
7800 pixels - no need to reconstruct the original image. */
7801 XSetFunction(xDisplay, maskGC, GXor);
7803 /* Might be quicker to download an XImage and create bitmap
7804 data from it rather than this N copies per piece, but it
7805 only takes a fraction of a second and there is a much
7806 longer delay for loading the pieces. */
7807 for (n = 0; n < pieceDepth; n ++) {
7808 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7809 0, 0, squareSize, squareSize,
7815 XFreePixmap(xDisplay, buf);
7816 XFreeGC(xDisplay, bufGC);
7817 XFreeGC(xDisplay, maskGC);
7821 InitAnimState (AnimState *anim, XWindowAttributes *info)
7826 /* Each buffer is square size, same depth as window */
7827 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7828 squareSize, squareSize, info->depth);
7829 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7830 squareSize, squareSize, info->depth);
7832 /* Create a plain GC for blitting */
7833 mask = GCForeground | GCBackground | GCFunction |
7834 GCPlaneMask | GCGraphicsExposures;
7835 values.foreground = XBlackPixel(xDisplay, xScreen);
7836 values.background = XWhitePixel(xDisplay, xScreen);
7837 values.function = GXcopy;
7838 values.plane_mask = AllPlanes;
7839 values.graphics_exposures = False;
7840 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7842 /* Piece will be copied from an existing context at
7843 the start of each new animation/drag. */
7844 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7846 /* Outline will be a read-only copy of an existing */
7847 anim->outlineGC = None;
7853 XWindowAttributes info;
7855 if (xpmDone && gameInfo.variant == oldVariant) return;
7856 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7857 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7859 InitAnimState(&game, &info);
7860 InitAnimState(&player, &info);
7862 /* For XPM pieces, we need bitmaps to use as masks. */
7864 CreateAnimMasks(info.depth), xpmDone = 1;
7869 static Boolean frameWaiting;
7872 FrameAlarm (int sig)
7874 frameWaiting = False;
7875 /* In case System-V style signals. Needed?? */
7876 signal(SIGALRM, FrameAlarm);
7880 FrameDelay (int time)
7882 struct itimerval delay;
7884 XSync(xDisplay, False);
7887 frameWaiting = True;
7888 signal(SIGALRM, FrameAlarm);
7889 delay.it_interval.tv_sec =
7890 delay.it_value.tv_sec = time / 1000;
7891 delay.it_interval.tv_usec =
7892 delay.it_value.tv_usec = (time % 1000) * 1000;
7893 setitimer(ITIMER_REAL, &delay, NULL);
7894 while (frameWaiting) pause();
7895 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7896 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7897 setitimer(ITIMER_REAL, &delay, NULL);
7904 FrameDelay (int time)
7906 XSync(xDisplay, False);
7908 usleep(time * 1000);
7919 /* Convert board position to corner of screen rect and color */
7922 ScreenSquare (int column, int row, XPoint *pt, int *color)
7925 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7926 pt->y = lineGap + row * (squareSize + lineGap);
7928 pt->x = lineGap + column * (squareSize + lineGap);
7929 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7931 *color = SquareColor(row, column);
7934 /* Convert window coords to square */
7937 BoardSquare (int x, int y, int *column, int *row)
7939 *column = EventToSquare(x, BOARD_WIDTH);
7940 if (flipView && *column >= 0)
7941 *column = BOARD_WIDTH - 1 - *column;
7942 *row = EventToSquare(y, BOARD_HEIGHT);
7943 if (!flipView && *row >= 0)
7944 *row = BOARD_HEIGHT - 1 - *row;
7949 #undef Max /* just in case */
7951 #define Max(a, b) ((a) > (b) ? (a) : (b))
7952 #define Min(a, b) ((a) < (b) ? (a) : (b))
7955 SetRect (XRectangle *rect, int x, int y, int width, int height)
7959 rect->width = width;
7960 rect->height = height;
7963 /* Test if two frames overlap. If they do, return
7964 intersection rect within old and location of
7965 that rect within new. */
7968 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
7970 if (old->x > new->x + size || new->x > old->x + size ||
7971 old->y > new->y + size || new->y > old->y + size) {
7974 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7975 size - abs(old->x - new->x), size - abs(old->y - new->y));
7976 pt->x = Max(old->x - new->x, 0);
7977 pt->y = Max(old->y - new->y, 0);
7982 /* For two overlapping frames, return the rect(s)
7983 in the old that do not intersect with the new. */
7986 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
7990 /* If old = new (shouldn't happen) then nothing to draw */
7991 if (old->x == new->x && old->y == new->y) {
7995 /* Work out what bits overlap. Since we know the rects
7996 are the same size we don't need a full intersect calc. */
7998 /* Top or bottom edge? */
7999 if (new->y > old->y) {
8000 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8002 } else if (old->y > new->y) {
8003 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8004 size, old->y - new->y);
8007 /* Left or right edge - don't overlap any update calculated above. */
8008 if (new->x > old->x) {
8009 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8010 new->x - old->x, size - abs(new->y - old->y));
8012 } else if (old->x > new->x) {
8013 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8014 old->x - new->x, size - abs(new->y - old->y));
8021 /* Generate a series of frame coords from start->mid->finish.
8022 The movement rate doubles until the half way point is
8023 reached, then halves back down to the final destination,
8024 which gives a nice slow in/out effect. The algorithmn
8025 may seem to generate too many intermediates for short
8026 moves, but remember that the purpose is to attract the
8027 viewers attention to the piece about to be moved and
8028 then to where it ends up. Too few frames would be less
8032 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8034 int fraction, n, count;
8038 /* Slow in, stepping 1/16th, then 1/8th, ... */
8040 for (n = 0; n < factor; n++)
8042 for (n = 0; n < factor; n++) {
8043 frames[count].x = start->x + (mid->x - start->x) / fraction;
8044 frames[count].y = start->y + (mid->y - start->y) / fraction;
8046 fraction = fraction / 2;
8050 frames[count] = *mid;
8053 /* Slow out, stepping 1/2, then 1/4, ... */
8055 for (n = 0; n < factor; n++) {
8056 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8057 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8059 fraction = fraction * 2;
8064 /* Draw a piece on the screen without disturbing what's there */
8067 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8071 /* Bitmap for piece being moved. */
8072 if (appData.monoMode) {
8073 *mask = *pieceToSolid(piece);
8074 } else if (useImages) {
8076 *mask = xpmMask[piece];
8078 *mask = ximMaskPm[piece];
8081 *mask = *pieceToSolid(piece);
8084 /* GC for piece being moved. Square color doesn't matter, but
8085 since it gets modified we make a copy of the original. */
8087 if (appData.monoMode)
8092 if (appData.monoMode)
8097 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8099 /* Outline only used in mono mode and is not modified */
8101 *outline = bwPieceGC;
8103 *outline = wbPieceGC;
8107 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8112 /* Draw solid rectangle which will be clipped to shape of piece */
8113 XFillRectangle(xDisplay, dest, clip,
8114 0, 0, squareSize, squareSize);
8115 if (appData.monoMode)
8116 /* Also draw outline in contrasting color for black
8117 on black / white on white cases */
8118 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8119 0, 0, squareSize, squareSize, 0, 0, 1);
8121 /* Copy the piece */
8126 if(appData.upsideDown && flipView) kind ^= 2;
8127 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8129 0, 0, squareSize, squareSize,
8134 /* Animate the movement of a single piece */
8137 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8141 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8142 /* The old buffer is initialised with the start square (empty) */
8143 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8144 anim->prevFrame = *start;
8146 /* The piece will be drawn using its own bitmap as a matte */
8147 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8148 XSetClipMask(xDisplay, anim->pieceGC, mask);
8152 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8154 XRectangle updates[4];
8159 /* Save what we are about to draw into the new buffer */
8160 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8161 frame->x, frame->y, squareSize, squareSize,
8164 /* Erase bits of the previous frame */
8165 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8166 /* Where the new frame overlapped the previous,
8167 the contents in newBuf are wrong. */
8168 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8169 overlap.x, overlap.y,
8170 overlap.width, overlap.height,
8172 /* Repaint the areas in the old that don't overlap new */
8173 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8174 for (i = 0; i < count; i++)
8175 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8176 updates[i].x - anim->prevFrame.x,
8177 updates[i].y - anim->prevFrame.y,
8178 updates[i].width, updates[i].height,
8179 updates[i].x, updates[i].y);
8181 /* Easy when no overlap */
8182 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8183 0, 0, squareSize, squareSize,
8184 anim->prevFrame.x, anim->prevFrame.y);
8187 /* Save this frame for next time round */
8188 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8189 0, 0, squareSize, squareSize,
8191 anim->prevFrame = *frame;
8193 /* Draw piece over original screen contents, not current,
8194 and copy entire rect. Wipes out overlapping piece images. */
8195 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8196 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8197 0, 0, squareSize, squareSize,
8198 frame->x, frame->y);
8202 EndAnimation (AnimState *anim, XPoint *finish)
8204 XRectangle updates[4];
8209 /* The main code will redraw the final square, so we
8210 only need to erase the bits that don't overlap. */
8211 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8212 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8213 for (i = 0; i < count; i++)
8214 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8215 updates[i].x - anim->prevFrame.x,
8216 updates[i].y - anim->prevFrame.y,
8217 updates[i].width, updates[i].height,
8218 updates[i].x, updates[i].y);
8220 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8221 0, 0, squareSize, squareSize,
8222 anim->prevFrame.x, anim->prevFrame.y);
8227 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8231 BeginAnimation(anim, piece, startColor, start);
8232 for (n = 0; n < nFrames; n++) {
8233 AnimationFrame(anim, &(frames[n]), piece);
8234 FrameDelay(appData.animSpeed);
8236 EndAnimation(anim, finish);
8240 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8243 ChessSquare piece = board[fromY][toY];
8244 board[fromY][toY] = EmptySquare;
8245 DrawPosition(FALSE, board);
8247 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8248 y = lineGap + toY * (squareSize + lineGap);
8250 x = lineGap + toX * (squareSize + lineGap);
8251 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8253 for(i=1; i<4*kFactor; i++) {
8254 int r = squareSize * 9 * i/(20*kFactor - 5);
8255 XFillArc(xDisplay, xBoardWindow, highlineGC,
8256 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8257 FrameDelay(appData.animSpeed);
8259 board[fromY][toY] = piece;
8262 /* Main control logic for deciding what to animate and how */
8265 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8269 XPoint start, finish, mid;
8270 XPoint frames[kFactor * 2 + 1];
8271 int nFrames, startColor, endColor;
8273 /* Are we animating? */
8274 if (!appData.animate || appData.blindfold)
8277 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8278 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8279 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8281 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8282 piece = board[fromY][fromX];
8283 if (piece >= EmptySquare) return;
8288 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8291 ScreenSquare(fromX, fromY, &start, &startColor);
8292 ScreenSquare(toX, toY, &finish, &endColor);
8295 /* Knight: make straight movement then diagonal */
8296 if (abs(toY - fromY) < abs(toX - fromX)) {
8297 mid.x = start.x + (finish.x - start.x) / 2;
8301 mid.y = start.y + (finish.y - start.y) / 2;
8304 mid.x = start.x + (finish.x - start.x) / 2;
8305 mid.y = start.y + (finish.y - start.y) / 2;
8308 /* Don't use as many frames for very short moves */
8309 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8310 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8312 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8313 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8314 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8316 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8317 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8320 /* Be sure end square is redrawn */
8321 damage[0][toY][toX] = True;
8325 DragPieceBegin (int x, int y, Boolean instantly)
8327 int boardX, boardY, color;
8330 /* Are we animating? */
8331 if (!appData.animateDragging || appData.blindfold)
8334 /* Figure out which square we start in and the
8335 mouse position relative to top left corner. */
8336 BoardSquare(x, y, &boardX, &boardY);
8337 player.startBoardX = boardX;
8338 player.startBoardY = boardY;
8339 ScreenSquare(boardX, boardY, &corner, &color);
8340 player.startSquare = corner;
8341 player.startColor = color;
8342 /* As soon as we start dragging, the piece will jump slightly to
8343 be centered over the mouse pointer. */
8344 player.mouseDelta.x = squareSize/2;
8345 player.mouseDelta.y = squareSize/2;
8346 /* Initialise animation */
8347 player.dragPiece = PieceForSquare(boardX, boardY);
8349 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8350 player.dragActive = True;
8351 BeginAnimation(&player, player.dragPiece, color, &corner);
8352 /* Mark this square as needing to be redrawn. Note that
8353 we don't remove the piece though, since logically (ie
8354 as seen by opponent) the move hasn't been made yet. */
8355 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8356 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8357 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8358 corner.x, corner.y, squareSize, squareSize,
8359 0, 0); // [HGM] zh: unstack in stead of grab
8360 if(gatingPiece != EmptySquare) {
8361 /* Kludge alert: When gating we want the introduced
8362 piece to appear on the from square. To generate an
8363 image of it, we draw it on the board, copy the image,
8364 and draw the original piece again. */
8365 ChessSquare piece = boards[currentMove][boardY][boardX];
8366 DrawSquare(boardY, boardX, gatingPiece, 0);
8367 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8368 corner.x, corner.y, squareSize, squareSize, 0, 0);
8369 DrawSquare(boardY, boardX, piece, 0);
8371 damage[0][boardY][boardX] = True;
8373 player.dragActive = False;
8378 ChangeDragPiece (ChessSquare piece)
8381 player.dragPiece = piece;
8382 /* The piece will be drawn using its own bitmap as a matte */
8383 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8384 XSetClipMask(xDisplay, player.pieceGC, mask);
8388 DragPieceMove (int x, int y)
8392 /* Are we animating? */
8393 if (!appData.animateDragging || appData.blindfold)
8397 if (! player.dragActive)
8399 /* Move piece, maintaining same relative position
8400 of mouse within square */
8401 corner.x = x - player.mouseDelta.x;
8402 corner.y = y - player.mouseDelta.y;
8403 AnimationFrame(&player, &corner, player.dragPiece);
8405 if (appData.highlightDragging) {
8407 BoardSquare(x, y, &boardX, &boardY);
8408 SetHighlights(fromX, fromY, boardX, boardY);
8414 DragPieceEnd (int x, int y)
8416 int boardX, boardY, color;
8419 /* Are we animating? */
8420 if (!appData.animateDragging || appData.blindfold)
8424 if (! player.dragActive)
8426 /* Last frame in sequence is square piece is
8427 placed on, which may not match mouse exactly. */
8428 BoardSquare(x, y, &boardX, &boardY);
8429 ScreenSquare(boardX, boardY, &corner, &color);
8430 EndAnimation(&player, &corner);
8432 /* Be sure end square is redrawn */
8433 damage[0][boardY][boardX] = True;
8435 /* This prevents weird things happening with fast successive
8436 clicks which on my Sun at least can cause motion events
8437 without corresponding press/release. */
8438 player.dragActive = False;
8441 /* Handle expose event while piece being dragged */
8446 if (!player.dragActive || appData.blindfold)
8449 /* What we're doing: logically, the move hasn't been made yet,
8450 so the piece is still in it's original square. But visually
8451 it's being dragged around the board. So we erase the square
8452 that the piece is on and draw it at the last known drag point. */
8453 BlankSquare(player.startSquare.x, player.startSquare.y,
8454 player.startColor, EmptySquare, xBoardWindow, 1);
8455 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8456 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8459 #include <sys/ioctl.h>
8463 int fd, default_width;
8466 default_width = 79; // this is FICS default anyway...
8468 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8470 if (!ioctl(fd, TIOCGSIZE, &win))
8471 default_width = win.ts_cols;
8472 #elif defined(TIOCGWINSZ)
8474 if (!ioctl(fd, TIOCGWINSZ, &win))
8475 default_width = win.ws_col;
8477 return default_width;
8483 static int old_width = 0;
8484 int new_width = get_term_width();
8486 if (old_width != new_width)
8487 ics_printf("set width %d\n", new_width);
8488 old_width = new_width;
8492 NotifyFrontendLogin ()
8497 /* [AS] Arrow highlighting support */
8499 static double A_WIDTH = 5; /* Width of arrow body */
8501 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8502 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8513 return (int) (x + 0.5);
8517 SquareToPos (int rank, int file, int *x, int *y)
8520 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8521 *y = lineGap + rank * (squareSize + lineGap);
8523 *x = lineGap + file * (squareSize + lineGap);
8524 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8528 /* Draw an arrow between two points using current settings */
8530 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8533 double dx, dy, j, k, x, y;
8536 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8538 arrow[0].x = s_x + A_WIDTH + 0.5;
8541 arrow[1].x = s_x + A_WIDTH + 0.5;
8542 arrow[1].y = d_y - h;
8544 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8545 arrow[2].y = d_y - h;
8550 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8551 arrow[5].y = d_y - h;
8553 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8554 arrow[4].y = d_y - h;
8556 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8559 else if( d_y == s_y ) {
8560 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8563 arrow[0].y = s_y + A_WIDTH + 0.5;
8565 arrow[1].x = d_x - w;
8566 arrow[1].y = s_y + A_WIDTH + 0.5;
8568 arrow[2].x = d_x - w;
8569 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8574 arrow[5].x = d_x - w;
8575 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8577 arrow[4].x = d_x - w;
8578 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8581 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8584 /* [AS] Needed a lot of paper for this! :-) */
8585 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8586 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8588 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8590 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8595 arrow[0].x = Round(x - j);
8596 arrow[0].y = Round(y + j*dx);
8598 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8599 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8602 x = (double) d_x - k;
8603 y = (double) d_y - k*dy;
8606 x = (double) d_x + k;
8607 y = (double) d_y + k*dy;
8610 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8612 arrow[6].x = Round(x - j);
8613 arrow[6].y = Round(y + j*dx);
8615 arrow[2].x = Round(arrow[6].x + 2*j);
8616 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8618 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8619 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8624 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8625 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8628 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8629 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8630 // Polygon( hdc, arrow, 7 );
8634 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8637 hor = 64*s_col + 32; vert = 64*s_row + 32;
8638 for(i=0; i<= 64; i++) {
8639 damage[0][vert+6>>6][hor+6>>6] = True;
8640 damage[0][vert-6>>6][hor+6>>6] = True;
8641 damage[0][vert+6>>6][hor-6>>6] = True;
8642 damage[0][vert-6>>6][hor-6>>6] = True;
8643 hor += d_col - s_col; vert += d_row - s_row;
8647 /* [AS] Draw an arrow between two squares */
8649 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8651 int s_x, s_y, d_x, d_y;
8653 if( s_col == d_col && s_row == d_row ) {
8657 /* Get source and destination points */
8658 SquareToPos( s_row, s_col, &s_x, &s_y);
8659 SquareToPos( d_row, d_col, &d_x, &d_y);
8662 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8664 else if( d_y < s_y ) {
8665 d_y += squareSize / 2 + squareSize / 4;
8668 d_y += squareSize / 2;
8672 d_x += squareSize / 2 - squareSize / 4;
8674 else if( d_x < s_x ) {
8675 d_x += squareSize / 2 + squareSize / 4;
8678 d_x += squareSize / 2;
8681 s_x += squareSize / 2;
8682 s_y += squareSize / 2;
8685 A_WIDTH = squareSize / 14.; //[HGM] make float
8687 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8688 ArrowDamage(s_col, s_row, d_col, d_row);
8692 IsDrawArrowEnabled ()
8694 return appData.highlightMoveWithArrow && squareSize >= 32;
8698 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8700 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8701 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8705 UpdateLogos (int displ)
8707 return; // no logos in XBoard yet