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 <Btn3Up>: PieceMenuPopup(menuB) \n \
1129 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1130 PieceMenuPopup(menuB) \n \
1131 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1132 PieceMenuPopup(menuW) \n \
1133 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1134 PieceMenuPopup(menuW) \n \
1135 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1136 PieceMenuPopup(menuB) \n";
1138 char whiteTranslations[] =
1139 "Shift<BtnDown>: WhiteClock(1)\n \
1140 <BtnDown>: WhiteClock(0)\n";
1141 char blackTranslations[] =
1142 "Shift<BtnDown>: BlackClock(1)\n \
1143 <BtnDown>: BlackClock(0)\n";
1145 char ICSInputTranslations[] =
1146 "<Key>Up: UpKeyProc() \n "
1147 "<Key>Down: DownKeyProc() \n "
1148 "<Key>Return: EnterKeyProc() \n";
1150 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1151 // as the widget is destroyed before the up-click can call extend-end
1152 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1154 String xboardResources[] = {
1155 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1156 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1157 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1162 /* Max possible square size */
1163 #define MAXSQSIZE 256
1165 static int xpm_avail[MAXSQSIZE];
1167 #ifdef HAVE_DIR_STRUCT
1169 /* Extract piece size from filename */
1171 xpm_getsize (char *name, int len, char *ext)
1179 if ((p=strchr(name, '.')) == NULL ||
1180 StrCaseCmp(p+1, ext) != 0)
1186 while (*p && isdigit(*p))
1193 /* Setup xpm_avail */
1195 xpm_getavail (char *dirname, char *ext)
1201 for (i=0; i<MAXSQSIZE; ++i)
1204 if (appData.debugMode)
1205 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1207 dir = opendir(dirname);
1210 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1211 programName, dirname);
1215 while ((ent=readdir(dir)) != NULL) {
1216 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1217 if (i > 0 && i < MAXSQSIZE)
1227 xpm_print_avail (FILE *fp, char *ext)
1231 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1232 for (i=1; i<MAXSQSIZE; ++i) {
1238 /* Return XPM piecesize closest to size */
1240 xpm_closest_to (char *dirname, int size, char *ext)
1243 int sm_diff = MAXSQSIZE;
1247 xpm_getavail(dirname, ext);
1249 if (appData.debugMode)
1250 xpm_print_avail(stderr, ext);
1252 for (i=1; i<MAXSQSIZE; ++i) {
1255 diff = (diff<0) ? -diff : diff;
1256 if (diff < sm_diff) {
1264 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1270 #else /* !HAVE_DIR_STRUCT */
1271 /* If we are on a system without a DIR struct, we can't
1272 read the directory, so we can't collect a list of
1273 filenames, etc., so we can't do any size-fitting. */
1275 xpm_closest_to (char *dirname, int size, char *ext)
1277 fprintf(stderr, _("\
1278 Warning: No DIR structure found on this system --\n\
1279 Unable to autosize for XPM/XIM pieces.\n\
1280 Please report this error to %s.\n\
1281 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1284 #endif /* HAVE_DIR_STRUCT */
1286 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1287 "magenta", "cyan", "white" };
1291 TextColors textColors[(int)NColorClasses];
1293 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1295 parse_color (char *str, int which)
1297 char *p, buf[100], *d;
1300 if (strlen(str) > 99) /* watch bounds on buf */
1305 for (i=0; i<which; ++i) {
1312 /* Could be looking at something like:
1314 .. in which case we want to stop on a comma also */
1315 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1319 return -1; /* Use default for empty field */
1322 if (which == 2 || isdigit(*p))
1325 while (*p && isalpha(*p))
1330 for (i=0; i<8; ++i) {
1331 if (!StrCaseCmp(buf, cnames[i]))
1332 return which? (i+40) : (i+30);
1334 if (!StrCaseCmp(buf, "default")) return -1;
1336 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1341 parse_cpair (ColorClass cc, char *str)
1343 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1344 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1349 /* bg and attr are optional */
1350 textColors[(int)cc].bg = parse_color(str, 1);
1351 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1352 textColors[(int)cc].attr = 0;
1358 /* Arrange to catch delete-window events */
1359 Atom wm_delete_window;
1361 CatchDeleteWindow (Widget w, String procname)
1364 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1365 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1366 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1373 XtSetArg(args[0], XtNiconic, False);
1374 XtSetValues(shellWidget, args, 1);
1376 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1379 //---------------------------------------------------------------------------------------------------------
1380 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1383 #define CW_USEDEFAULT (1<<31)
1384 #define ICS_TEXT_MENU_SIZE 90
1385 #define DEBUG_FILE "xboard.debug"
1386 #define SetCurrentDirectory chdir
1387 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1391 // these two must some day move to frontend.h, when they are implemented
1392 Boolean GameListIsUp();
1394 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1397 // front-end part of option handling
1399 // [HGM] This platform-dependent table provides the location for storing the color info
1400 extern char *crWhite, * crBlack;
1404 &appData.whitePieceColor,
1405 &appData.blackPieceColor,
1406 &appData.lightSquareColor,
1407 &appData.darkSquareColor,
1408 &appData.highlightSquareColor,
1409 &appData.premoveHighlightColor,
1410 &appData.lowTimeWarningColor,
1421 // [HGM] font: keep a font for each square size, even non-stndard ones
1422 #define NUM_SIZES 18
1423 #define MAX_SIZE 130
1424 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1425 char *fontTable[NUM_FONTS][MAX_SIZE];
1428 ParseFont (char *name, int number)
1429 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1431 if(sscanf(name, "size%d:", &size)) {
1432 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1433 // defer processing it until we know if it matches our board size
1434 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1435 fontTable[number][size] = strdup(strchr(name, ':')+1);
1436 fontValid[number][size] = True;
1441 case 0: // CLOCK_FONT
1442 appData.clockFont = strdup(name);
1444 case 1: // MESSAGE_FONT
1445 appData.font = strdup(name);
1447 case 2: // COORD_FONT
1448 appData.coordFont = strdup(name);
1453 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1458 { // only 2 fonts currently
1459 appData.clockFont = CLOCK_FONT_NAME;
1460 appData.coordFont = COORD_FONT_NAME;
1461 appData.font = DEFAULT_FONT_NAME;
1466 { // no-op, until we identify the code for this already in XBoard and move it here
1470 ParseColor (int n, char *name)
1471 { // in XBoard, just copy the color-name string
1472 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1476 ParseTextAttribs (ColorClass cc, char *s)
1478 (&appData.colorShout)[cc] = strdup(s);
1482 ParseBoardSize (void *addr, char *name)
1484 appData.boardSize = strdup(name);
1489 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1493 SetCommPortDefaults ()
1494 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1497 // [HGM] args: these three cases taken out to stay in front-end
1499 SaveFontArg (FILE *f, ArgDescriptor *ad)
1502 int i, n = (int)(intptr_t)ad->argLoc;
1504 case 0: // CLOCK_FONT
1505 name = appData.clockFont;
1507 case 1: // MESSAGE_FONT
1508 name = appData.font;
1510 case 2: // COORD_FONT
1511 name = appData.coordFont;
1516 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1517 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1518 fontTable[n][squareSize] = strdup(name);
1519 fontValid[n][squareSize] = True;
1522 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1523 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1528 { // nothing to do, as the sounds are at all times represented by their text-string names already
1532 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1533 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1534 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1538 SaveColor (FILE *f, ArgDescriptor *ad)
1539 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1540 if(colorVariable[(int)(intptr_t)ad->argLoc])
1541 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1545 SaveBoardSize (FILE *f, char *name, void *addr)
1546 { // wrapper to shield back-end from BoardSize & sizeInfo
1547 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1551 ParseCommPortSettings (char *s)
1552 { // no such option in XBoard (yet)
1555 extern Widget engineOutputShell;
1558 GetActualPlacement (Widget wg, WindowPlacement *wp)
1568 XtSetArg(args[i], XtNx, &x); i++;
1569 XtSetArg(args[i], XtNy, &y); i++;
1570 XtSetArg(args[i], XtNwidth, &w); i++;
1571 XtSetArg(args[i], XtNheight, &h); i++;
1572 XtGetValues(wg, args, i);
1581 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1582 // In XBoard this will have to wait until awareness of window parameters is implemented
1583 GetActualPlacement(shellWidget, &wpMain);
1584 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1585 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1586 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1587 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1588 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1589 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1593 PrintCommPortSettings (FILE *f, char *name)
1594 { // This option does not exist in XBoard
1598 MySearchPath (char *installDir, char *name, char *fullname)
1599 { // just append installDir and name. Perhaps ExpandPath should be used here?
1600 name = ExpandPathName(name);
1601 if(name && name[0] == '/')
1602 safeStrCpy(fullname, name, MSG_SIZ );
1604 sprintf(fullname, "%s%c%s", installDir, '/', name);
1610 MyGetFullPathName (char *name, char *fullname)
1611 { // should use ExpandPath?
1612 name = ExpandPathName(name);
1613 safeStrCpy(fullname, name, MSG_SIZ );
1618 EnsureOnScreen (int *x, int *y, int minX, int minY)
1625 { // [HGM] args: allows testing if main window is realized from back-end
1626 return xBoardWindow != 0;
1630 PopUpStartupDialog ()
1631 { // start menu not implemented in XBoard
1635 ConvertToLine (int argc, char **argv)
1637 static char line[128*1024], buf[1024];
1641 for(i=1; i<argc; i++)
1643 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1644 && argv[i][0] != '{' )
1645 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1647 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1648 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1651 line[strlen(line)-1] = NULLCHAR;
1655 //--------------------------------------------------------------------------------------------
1657 extern Boolean twoBoards, partnerUp;
1660 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1662 #define BoardSize int
1664 InitDrawingSizes (BoardSize boardSize, int flags)
1665 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1666 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1668 XtGeometryResult gres;
1670 static Dimension oldWidth, oldHeight;
1671 static VariantClass oldVariant;
1672 static int oldDual = -1, oldMono = -1;
1674 if(!formWidget) return;
1676 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1677 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1678 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1680 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1682 * Enable shell resizing.
1684 shellArgs[0].value = (XtArgVal) &w;
1685 shellArgs[1].value = (XtArgVal) &h;
1686 XtGetValues(shellWidget, shellArgs, 2);
1688 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1689 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1690 XtSetValues(shellWidget, &shellArgs[2], 4);
1692 XtSetArg(args[0], XtNdefaultDistance, &sep);
1693 XtGetValues(formWidget, args, 1);
1695 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1697 hOffset = boardWidth + 10;
1698 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1699 secondSegments[i] = gridSegments[i];
1700 secondSegments[i].x1 += hOffset;
1701 secondSegments[i].x2 += hOffset;
1704 XtSetArg(args[0], XtNwidth, boardWidth);
1705 XtSetArg(args[1], XtNheight, boardHeight);
1706 XtSetValues(boardWidget, args, 2);
1708 timerWidth = (boardWidth - sep) / 2;
1709 XtSetArg(args[0], XtNwidth, timerWidth);
1710 XtSetValues(whiteTimerWidget, args, 1);
1711 XtSetValues(blackTimerWidget, args, 1);
1713 XawFormDoLayout(formWidget, False);
1715 if (appData.titleInWindow) {
1717 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1718 XtSetArg(args[i], XtNheight, &h); i++;
1719 XtGetValues(titleWidget, args, i);
1721 w = boardWidth - 2*bor;
1723 XtSetArg(args[0], XtNwidth, &w);
1724 XtGetValues(menuBarWidget, args, 1);
1725 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1728 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1729 if (gres != XtGeometryYes && appData.debugMode) {
1731 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1732 programName, gres, w, h, wr, hr);
1736 XawFormDoLayout(formWidget, True);
1739 * Inhibit shell resizing.
1741 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1742 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1743 shellArgs[4].value = shellArgs[2].value = w;
1744 shellArgs[5].value = shellArgs[3].value = h;
1745 XtSetValues(shellWidget, &shellArgs[0], 6);
1748 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1751 if(gameInfo.variant != oldVariant) { // and only if variant changed
1754 for(i=0; i<4; i++) {
1756 for(p=0; p<=(int)WhiteKing; p++)
1757 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1758 if(gameInfo.variant == VariantShogi) {
1759 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1760 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1761 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1762 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1763 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1766 if(gameInfo.variant == VariantGothic) {
1767 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1770 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1771 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1772 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1775 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1776 for(p=0; p<=(int)WhiteKing; p++)
1777 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1778 if(gameInfo.variant == VariantShogi) {
1779 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1780 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1781 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1782 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1783 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1786 if(gameInfo.variant == VariantGothic) {
1787 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1790 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1791 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1792 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1797 for(i=0; i<2; i++) {
1799 for(p=0; p<=(int)WhiteKing; p++)
1800 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1801 if(gameInfo.variant == VariantShogi) {
1802 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1803 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1804 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1805 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1806 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1809 if(gameInfo.variant == VariantGothic) {
1810 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1813 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1814 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1815 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1819 oldMono = -10; // kludge to force recreation of animation masks
1822 if(appData.monoMode != oldMono)
1825 oldMono = appData.monoMode;
1830 ParseIcsTextColors ()
1831 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1832 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1833 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1834 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1835 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1836 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1837 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1838 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1839 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1840 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1841 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1843 if (appData.colorize) {
1845 _("%s: can't parse color names; disabling colorization\n"),
1848 appData.colorize = FALSE;
1854 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1855 XrmValue vFrom, vTo;
1856 int forceMono = False;
1858 if (!appData.monoMode) {
1859 vFrom.addr = (caddr_t) appData.lightSquareColor;
1860 vFrom.size = strlen(appData.lightSquareColor);
1861 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1862 if (vTo.addr == NULL) {
1863 appData.monoMode = True;
1866 lightSquareColor = *(Pixel *) vTo.addr;
1869 if (!appData.monoMode) {
1870 vFrom.addr = (caddr_t) appData.darkSquareColor;
1871 vFrom.size = strlen(appData.darkSquareColor);
1872 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1873 if (vTo.addr == NULL) {
1874 appData.monoMode = True;
1877 darkSquareColor = *(Pixel *) vTo.addr;
1880 if (!appData.monoMode) {
1881 vFrom.addr = (caddr_t) appData.whitePieceColor;
1882 vFrom.size = strlen(appData.whitePieceColor);
1883 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1884 if (vTo.addr == NULL) {
1885 appData.monoMode = True;
1888 whitePieceColor = *(Pixel *) vTo.addr;
1891 if (!appData.monoMode) {
1892 vFrom.addr = (caddr_t) appData.blackPieceColor;
1893 vFrom.size = strlen(appData.blackPieceColor);
1894 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1895 if (vTo.addr == NULL) {
1896 appData.monoMode = True;
1899 blackPieceColor = *(Pixel *) vTo.addr;
1903 if (!appData.monoMode) {
1904 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1905 vFrom.size = strlen(appData.highlightSquareColor);
1906 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1907 if (vTo.addr == NULL) {
1908 appData.monoMode = True;
1911 highlightSquareColor = *(Pixel *) vTo.addr;
1915 if (!appData.monoMode) {
1916 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1917 vFrom.size = strlen(appData.premoveHighlightColor);
1918 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1919 if (vTo.addr == NULL) {
1920 appData.monoMode = True;
1923 premoveHighlightColor = *(Pixel *) vTo.addr;
1931 { // [HGM] taken out of main
1933 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1934 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1935 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1937 if (appData.bitmapDirectory[0] != NULLCHAR) {
1941 CreateXPMBoard(appData.liteBackTextureFile, 1);
1942 CreateXPMBoard(appData.darkBackTextureFile, 0);
1946 /* Create regular pieces */
1947 if (!useImages) CreatePieces();
1952 main (int argc, char **argv)
1954 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1955 XSetWindowAttributes window_attributes;
1957 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1958 XrmValue vFrom, vTo;
1959 XtGeometryResult gres;
1962 int forceMono = False;
1964 srandom(time(0)); // [HGM] book: make random truly random
1966 setbuf(stdout, NULL);
1967 setbuf(stderr, NULL);
1970 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1971 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1975 programName = strrchr(argv[0], '/');
1976 if (programName == NULL)
1977 programName = argv[0];
1982 XtSetLanguageProc(NULL, NULL, NULL);
1983 bindtextdomain(PACKAGE, LOCALEDIR);
1984 textdomain(PACKAGE);
1988 XtAppInitialize(&appContext, "XBoard", shellOptions,
1989 XtNumber(shellOptions),
1990 &argc, argv, xboardResources, NULL, 0);
1991 appData.boardSize = "";
1992 InitAppData(ConvertToLine(argc, argv));
1994 if (p == NULL) p = "/tmp";
1995 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1996 gameCopyFilename = (char*) malloc(i);
1997 gamePasteFilename = (char*) malloc(i);
1998 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1999 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2001 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2002 clientResources, XtNumber(clientResources),
2005 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2006 static char buf[MSG_SIZ];
2007 EscapeExpand(buf, appData.firstInitString);
2008 appData.firstInitString = strdup(buf);
2009 EscapeExpand(buf, appData.secondInitString);
2010 appData.secondInitString = strdup(buf);
2011 EscapeExpand(buf, appData.firstComputerString);
2012 appData.firstComputerString = strdup(buf);
2013 EscapeExpand(buf, appData.secondComputerString);
2014 appData.secondComputerString = strdup(buf);
2017 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2020 if (chdir(chessDir) != 0) {
2021 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2027 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2028 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2029 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2030 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2033 setbuf(debugFP, NULL);
2037 if (appData.debugMode) {
2038 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2042 /* [HGM,HR] make sure board size is acceptable */
2043 if(appData.NrFiles > BOARD_FILES ||
2044 appData.NrRanks > BOARD_RANKS )
2045 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2048 /* This feature does not work; animation needs a rewrite */
2049 appData.highlightDragging = FALSE;
2053 xDisplay = XtDisplay(shellWidget);
2054 xScreen = DefaultScreen(xDisplay);
2055 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2057 gameInfo.variant = StringToVariant(appData.variant);
2058 InitPosition(FALSE);
2061 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2063 if (isdigit(appData.boardSize[0])) {
2064 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2065 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2066 &fontPxlSize, &smallLayout, &tinyLayout);
2068 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2069 programName, appData.boardSize);
2073 /* Find some defaults; use the nearest known size */
2074 SizeDefaults *szd, *nearest;
2075 int distance = 99999;
2076 nearest = szd = sizeDefaults;
2077 while (szd->name != NULL) {
2078 if (abs(szd->squareSize - squareSize) < distance) {
2080 distance = abs(szd->squareSize - squareSize);
2081 if (distance == 0) break;
2085 if (i < 2) lineGap = nearest->lineGap;
2086 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2087 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2088 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2089 if (i < 6) smallLayout = nearest->smallLayout;
2090 if (i < 7) tinyLayout = nearest->tinyLayout;
2093 SizeDefaults *szd = sizeDefaults;
2094 if (*appData.boardSize == NULLCHAR) {
2095 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2096 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2099 if (szd->name == NULL) szd--;
2100 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2102 while (szd->name != NULL &&
2103 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2104 if (szd->name == NULL) {
2105 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2106 programName, appData.boardSize);
2110 squareSize = szd->squareSize;
2111 lineGap = szd->lineGap;
2112 clockFontPxlSize = szd->clockFontPxlSize;
2113 coordFontPxlSize = szd->coordFontPxlSize;
2114 fontPxlSize = szd->fontPxlSize;
2115 smallLayout = szd->smallLayout;
2116 tinyLayout = szd->tinyLayout;
2117 // [HGM] font: use defaults from settings file if available and not overruled
2119 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2120 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2121 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2122 appData.font = fontTable[MESSAGE_FONT][squareSize];
2123 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2124 appData.coordFont = fontTable[COORD_FONT][squareSize];
2126 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2127 if (strlen(appData.pixmapDirectory) > 0) {
2128 p = ExpandPathName(appData.pixmapDirectory);
2130 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2131 appData.pixmapDirectory);
2134 if (appData.debugMode) {
2135 fprintf(stderr, _("\
2136 XBoard square size (hint): %d\n\
2137 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2139 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2140 if (appData.debugMode) {
2141 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2144 defaultLineGap = lineGap;
2145 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2147 /* [HR] height treated separately (hacked) */
2148 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2149 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2150 if (appData.showJail == 1) {
2151 /* Jail on top and bottom */
2152 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2153 XtSetArg(boardArgs[2], XtNheight,
2154 boardHeight + 2*(lineGap + squareSize));
2155 } else if (appData.showJail == 2) {
2157 XtSetArg(boardArgs[1], XtNwidth,
2158 boardWidth + 2*(lineGap + squareSize));
2159 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2162 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2163 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2167 * Determine what fonts to use.
2170 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2171 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2172 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2173 fontSet = CreateFontSet(appData.font);
2174 clockFontSet = CreateFontSet(appData.clockFont);
2176 /* For the coordFont, use the 0th font of the fontset. */
2177 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2178 XFontStruct **font_struct_list;
2179 XFontSetExtents *fontSize;
2180 char **font_name_list;
2181 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2182 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2183 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2184 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
2185 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
2188 appData.font = FindFont(appData.font, fontPxlSize);
2189 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2190 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2191 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2192 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2193 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2194 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2196 countFontID = coordFontID; // [HGM] holdings
2197 countFontStruct = coordFontStruct;
2199 xdb = XtDatabase(xDisplay);
2201 XrmPutLineResource(&xdb, "*international: True");
2202 vTo.size = sizeof(XFontSet);
2203 vTo.addr = (XtPointer) &fontSet;
2204 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2206 XrmPutStringResource(&xdb, "*font", appData.font);
2210 * Detect if there are not enough colors available and adapt.
2212 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2213 appData.monoMode = True;
2216 forceMono = MakeColors();
2219 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2221 appData.monoMode = True;
2224 if (appData.lowTimeWarning && !appData.monoMode) {
2225 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2226 vFrom.size = strlen(appData.lowTimeWarningColor);
2227 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2228 if (vTo.addr == NULL)
2229 appData.monoMode = True;
2231 lowTimeWarningColor = *(Pixel *) vTo.addr;
2234 if (appData.monoMode && appData.debugMode) {
2235 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2236 (unsigned long) XWhitePixel(xDisplay, xScreen),
2237 (unsigned long) XBlackPixel(xDisplay, xScreen));
2240 ParseIcsTextColors();
2241 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2242 textColors[ColorNone].attr = 0;
2244 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2250 layoutName = "tinyLayout";
2251 } else if (smallLayout) {
2252 layoutName = "smallLayout";
2254 layoutName = "normalLayout";
2256 /* Outer layoutWidget is there only to provide a name for use in
2257 resources that depend on the layout style */
2259 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2260 layoutArgs, XtNumber(layoutArgs));
2262 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2263 formArgs, XtNumber(formArgs));
2264 XtSetArg(args[0], XtNdefaultDistance, &sep);
2265 XtGetValues(formWidget, args, 1);
2268 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2269 XtSetArg(args[0], XtNtop, XtChainTop);
2270 XtSetArg(args[1], XtNbottom, XtChainTop);
2271 XtSetArg(args[2], XtNright, XtChainLeft);
2272 XtSetValues(menuBarWidget, args, 3);
2274 widgetList[j++] = whiteTimerWidget =
2275 XtCreateWidget("whiteTime", labelWidgetClass,
2276 formWidget, timerArgs, XtNumber(timerArgs));
2278 XtSetArg(args[0], XtNfontSet, clockFontSet);
2280 XtSetArg(args[0], XtNfont, clockFontStruct);
2282 XtSetArg(args[1], XtNtop, XtChainTop);
2283 XtSetArg(args[2], XtNbottom, XtChainTop);
2284 XtSetValues(whiteTimerWidget, args, 3);
2286 widgetList[j++] = blackTimerWidget =
2287 XtCreateWidget("blackTime", labelWidgetClass,
2288 formWidget, timerArgs, XtNumber(timerArgs));
2290 XtSetArg(args[0], XtNfontSet, clockFontSet);
2292 XtSetArg(args[0], XtNfont, clockFontStruct);
2294 XtSetArg(args[1], XtNtop, XtChainTop);
2295 XtSetArg(args[2], XtNbottom, XtChainTop);
2296 XtSetValues(blackTimerWidget, args, 3);
2298 if (appData.titleInWindow) {
2299 widgetList[j++] = titleWidget =
2300 XtCreateWidget("title", labelWidgetClass, formWidget,
2301 titleArgs, XtNumber(titleArgs));
2302 XtSetArg(args[0], XtNtop, XtChainTop);
2303 XtSetArg(args[1], XtNbottom, XtChainTop);
2304 XtSetValues(titleWidget, args, 2);
2307 if (appData.showButtonBar) {
2308 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2309 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2310 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2311 XtSetArg(args[2], XtNtop, XtChainTop);
2312 XtSetArg(args[3], XtNbottom, XtChainTop);
2313 XtSetValues(buttonBarWidget, args, 4);
2316 widgetList[j++] = messageWidget =
2317 XtCreateWidget("message", labelWidgetClass, formWidget,
2318 messageArgs, XtNumber(messageArgs));
2319 XtSetArg(args[0], XtNtop, XtChainTop);
2320 XtSetArg(args[1], XtNbottom, XtChainTop);
2321 XtSetValues(messageWidget, args, 2);
2323 widgetList[j++] = boardWidget =
2324 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2325 XtNumber(boardArgs));
2327 XtManageChildren(widgetList, j);
2329 timerWidth = (boardWidth - sep) / 2;
2330 XtSetArg(args[0], XtNwidth, timerWidth);
2331 XtSetValues(whiteTimerWidget, args, 1);
2332 XtSetValues(blackTimerWidget, args, 1);
2334 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2335 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2336 XtGetValues(whiteTimerWidget, args, 2);
2338 if (appData.showButtonBar) {
2339 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2340 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2341 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2345 * formWidget uses these constraints but they are stored
2349 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2350 XtSetValues(menuBarWidget, args, i);
2351 if (appData.titleInWindow) {
2354 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2355 XtSetValues(whiteTimerWidget, args, i);
2357 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2358 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2359 XtSetValues(blackTimerWidget, args, i);
2361 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2362 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2363 XtSetValues(titleWidget, args, i);
2365 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2366 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2367 XtSetValues(messageWidget, args, i);
2368 if (appData.showButtonBar) {
2370 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2371 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2372 XtSetValues(buttonBarWidget, args, i);
2376 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2377 XtSetValues(whiteTimerWidget, args, i);
2379 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2380 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2381 XtSetValues(blackTimerWidget, args, i);
2383 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2384 XtSetValues(titleWidget, args, i);
2386 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2387 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2388 XtSetValues(messageWidget, args, i);
2389 if (appData.showButtonBar) {
2391 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2392 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2393 XtSetValues(buttonBarWidget, args, i);
2398 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2399 XtSetValues(whiteTimerWidget, args, i);
2401 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2402 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2403 XtSetValues(blackTimerWidget, args, i);
2405 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2406 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2407 XtSetValues(messageWidget, args, i);
2408 if (appData.showButtonBar) {
2410 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2411 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2412 XtSetValues(buttonBarWidget, args, i);
2416 XtSetArg(args[0], XtNfromVert, messageWidget);
2417 XtSetArg(args[1], XtNtop, XtChainTop);
2418 XtSetArg(args[2], XtNbottom, XtChainBottom);
2419 XtSetArg(args[3], XtNleft, XtChainLeft);
2420 XtSetArg(args[4], XtNright, XtChainRight);
2421 XtSetValues(boardWidget, args, 5);
2423 XtRealizeWidget(shellWidget);
2426 XtSetArg(args[0], XtNx, wpMain.x);
2427 XtSetArg(args[1], XtNy, wpMain.y);
2428 XtSetValues(shellWidget, args, 2);
2432 * Correct the width of the message and title widgets.
2433 * It is not known why some systems need the extra fudge term.
2434 * The value "2" is probably larger than needed.
2436 XawFormDoLayout(formWidget, False);
2438 #define WIDTH_FUDGE 2
2440 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2441 XtSetArg(args[i], XtNheight, &h); i++;
2442 XtGetValues(messageWidget, args, i);
2443 if (appData.showButtonBar) {
2445 XtSetArg(args[i], XtNwidth, &w); i++;
2446 XtGetValues(buttonBarWidget, args, i);
2447 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2449 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2452 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2453 if (gres != XtGeometryYes && appData.debugMode) {
2454 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2455 programName, gres, w, h, wr, hr);
2458 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2459 /* The size used for the child widget in layout lags one resize behind
2460 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2462 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2463 if (gres != XtGeometryYes && appData.debugMode) {
2464 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2465 programName, gres, w, h, wr, hr);
2468 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2469 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2470 XtSetArg(args[1], XtNright, XtChainRight);
2471 XtSetValues(messageWidget, args, 2);
2473 if (appData.titleInWindow) {
2475 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2476 XtSetArg(args[i], XtNheight, &h); i++;
2477 XtGetValues(titleWidget, args, i);
2479 w = boardWidth - 2*bor;
2481 XtSetArg(args[0], XtNwidth, &w);
2482 XtGetValues(menuBarWidget, args, 1);
2483 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2486 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2487 if (gres != XtGeometryYes && appData.debugMode) {
2489 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2490 programName, gres, w, h, wr, hr);
2493 XawFormDoLayout(formWidget, True);
2495 xBoardWindow = XtWindow(boardWidget);
2497 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2498 // not need to go into InitDrawingSizes().
2502 * Create X checkmark bitmap and initialize option menu checks.
2504 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2505 checkmark_bits, checkmark_width, checkmark_height);
2506 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2507 #ifndef OPTIONSDIALOG
2508 if (appData.alwaysPromoteToQueen) {
2509 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2512 if (appData.animateDragging) {
2513 XtSetValues(XtNameToWidget(menuBarWidget,
2514 "menuOptions.Animate Dragging"),
2517 if (appData.animate) {
2518 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2521 if (appData.autoCallFlag) {
2522 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2525 if (appData.autoFlipView) {
2526 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2529 if (appData.blindfold) {
2530 XtSetValues(XtNameToWidget(menuBarWidget,
2531 "menuOptions.Blindfold"), args, 1);
2533 if (appData.flashCount > 0) {
2534 XtSetValues(XtNameToWidget(menuBarWidget,
2535 "menuOptions.Flash Moves"),
2539 if (appData.highlightDragging) {
2540 XtSetValues(XtNameToWidget(menuBarWidget,
2541 "menuOptions.Highlight Dragging"),
2545 if (appData.highlightLastMove) {
2546 XtSetValues(XtNameToWidget(menuBarWidget,
2547 "menuOptions.Highlight Last Move"),
2550 if (appData.highlightMoveWithArrow) {
2551 XtSetValues(XtNameToWidget(menuBarWidget,
2552 "menuOptions.Arrow"),
2555 // if (appData.icsAlarm) {
2556 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2559 if (appData.ringBellAfterMoves) {
2560 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2563 if (appData.oneClick) {
2564 XtSetValues(XtNameToWidget(menuBarWidget,
2565 "menuOptions.OneClick"), args, 1);
2567 if (appData.periodicUpdates) {
2568 XtSetValues(XtNameToWidget(menuBarWidget,
2569 "menuOptions.Periodic Updates"), args, 1);
2571 if (appData.ponderNextMove) {
2572 XtSetValues(XtNameToWidget(menuBarWidget,
2573 "menuOptions.Ponder Next Move"), args, 1);
2575 if (appData.popupExitMessage) {
2576 XtSetValues(XtNameToWidget(menuBarWidget,
2577 "menuOptions.Popup Exit Message"), args, 1);
2579 if (appData.popupMoveErrors) {
2580 XtSetValues(XtNameToWidget(menuBarWidget,
2581 "menuOptions.Popup Move Errors"), args, 1);
2583 // if (appData.premove) {
2584 // XtSetValues(XtNameToWidget(menuBarWidget,
2585 // "menuOptions.Premove"), args, 1);
2587 if (appData.showCoords) {
2588 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2591 if (appData.hideThinkingFromHuman) {
2592 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2595 if (appData.testLegality) {
2596 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2600 if (saveSettingsOnExit) {
2601 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2608 ReadBitmap(&wIconPixmap, "icon_white.bm",
2609 icon_white_bits, icon_white_width, icon_white_height);
2610 ReadBitmap(&bIconPixmap, "icon_black.bm",
2611 icon_black_bits, icon_black_width, icon_black_height);
2612 iconPixmap = wIconPixmap;
2614 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2615 XtSetValues(shellWidget, args, i);
2618 * Create a cursor for the board widget.
2620 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2621 XChangeWindowAttributes(xDisplay, xBoardWindow,
2622 CWCursor, &window_attributes);
2625 * Inhibit shell resizing.
2627 shellArgs[0].value = (XtArgVal) &w;
2628 shellArgs[1].value = (XtArgVal) &h;
2629 XtGetValues(shellWidget, shellArgs, 2);
2630 shellArgs[4].value = shellArgs[2].value = w;
2631 shellArgs[5].value = shellArgs[3].value = h;
2632 XtSetValues(shellWidget, &shellArgs[2], 4);
2633 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2634 marginH = h - boardHeight;
2636 CatchDeleteWindow(shellWidget, "QuitProc");
2644 if (appData.animate || appData.animateDragging)
2647 XtAugmentTranslations(formWidget,
2648 XtParseTranslationTable(globalTranslations));
2649 XtAugmentTranslations(boardWidget,
2650 XtParseTranslationTable(boardTranslations));
2651 XtAugmentTranslations(whiteTimerWidget,
2652 XtParseTranslationTable(whiteTranslations));
2653 XtAugmentTranslations(blackTimerWidget,
2654 XtParseTranslationTable(blackTranslations));
2656 /* Why is the following needed on some versions of X instead
2657 * of a translation? */
2658 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2659 (XtEventHandler) EventProc, NULL);
2661 XtAddEventHandler(formWidget, KeyPressMask, False,
2662 (XtEventHandler) MoveTypeInProc, NULL);
2664 /* [AS] Restore layout */
2665 if( wpMoveHistory.visible ) {
2669 if( wpEvalGraph.visible )
2674 if( wpEngineOutput.visible ) {
2675 EngineOutputPopUp();
2680 if (errorExitStatus == -1) {
2681 if (appData.icsActive) {
2682 /* We now wait until we see "login:" from the ICS before
2683 sending the logon script (problems with timestamp otherwise) */
2684 /*ICSInitScript();*/
2685 if (appData.icsInputBox) ICSInputBoxPopUp();
2689 signal(SIGWINCH, TermSizeSigHandler);
2691 signal(SIGINT, IntSigHandler);
2692 signal(SIGTERM, IntSigHandler);
2693 if (*appData.cmailGameName != NULLCHAR) {
2694 signal(SIGUSR1, CmailSigHandler);
2697 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2699 // XtSetKeyboardFocus(shellWidget, formWidget);
2700 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2702 XtAppMainLoop(appContext);
2703 if (appData.debugMode) fclose(debugFP); // [DM] debug
2707 static Boolean noEcho;
2712 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2713 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2715 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2716 unlink(gameCopyFilename);
2717 unlink(gamePasteFilename);
2718 if(noEcho) EchoOn();
2722 TermSizeSigHandler (int sig)
2728 IntSigHandler (int sig)
2734 CmailSigHandler (int sig)
2739 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2741 /* Activate call-back function CmailSigHandlerCallBack() */
2742 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2744 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2748 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2751 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2753 /**** end signal code ****/
2759 /* try to open the icsLogon script, either in the location given
2760 * or in the users HOME directory
2767 f = fopen(appData.icsLogon, "r");
2770 homedir = getenv("HOME");
2771 if (homedir != NULL)
2773 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2774 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2775 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2776 f = fopen(buf, "r");
2781 ProcessICSInitScript(f);
2783 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2802 GreyRevert (Boolean grey)
2805 if (!menuBarWidget) return;
2806 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2808 DisplayError("menuEdit.Revert", 0);
2810 XtSetSensitive(w, !grey);
2812 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2814 DisplayError("menuEdit.Annotate", 0);
2816 XtSetSensitive(w, !grey);
2821 SetMenuEnables (Enables *enab)
2824 if (!menuBarWidget) return;
2825 while (enab->name != NULL) {
2826 w = XtNameToWidget(menuBarWidget, enab->name);
2828 DisplayError(enab->name, 0);
2830 XtSetSensitive(w, enab->value);
2836 Enables icsEnables[] = {
2837 { "menuFile.Mail Move", False },
2838 { "menuFile.Reload CMail Message", False },
2839 { "menuMode.Machine Black", False },
2840 { "menuMode.Machine White", False },
2841 { "menuMode.Analysis Mode", False },
2842 { "menuMode.Analyze File", False },
2843 { "menuMode.Two Machines", False },
2844 { "menuMode.Machine Match", False },
2846 { "menuEngine.Hint", False },
2847 { "menuEngine.Book", False },
2848 { "menuEngine.Move Now", False },
2849 #ifndef OPTIONSDIALOG
2850 { "menuOptions.Periodic Updates", False },
2851 { "menuOptions.Hide Thinking", False },
2852 { "menuOptions.Ponder Next Move", False },
2855 { "menuEngine.Engine #1 Settings", False },
2856 { "menuEngine.Engine #2 Settings", False },
2857 { "menuEngine.Load Engine", False },
2858 { "menuEdit.Annotate", False },
2859 { "menuOptions.Match", False },
2863 Enables ncpEnables[] = {
2864 { "menuFile.Mail Move", False },
2865 { "menuFile.Reload CMail Message", False },
2866 { "menuMode.Machine White", False },
2867 { "menuMode.Machine Black", False },
2868 { "menuMode.Analysis Mode", False },
2869 { "menuMode.Analyze File", False },
2870 { "menuMode.Two Machines", False },
2871 { "menuMode.Machine Match", False },
2872 { "menuMode.ICS Client", False },
2873 { "menuView.ICStex", False },
2874 { "menuView.ICS Input Box", False },
2875 { "Action", False },
2876 { "menuEdit.Revert", False },
2877 { "menuEdit.Annotate", False },
2878 { "menuEngine.Engine #1 Settings", False },
2879 { "menuEngine.Engine #2 Settings", False },
2880 { "menuEngine.Move Now", False },
2881 { "menuEngine.Retract Move", False },
2882 { "menuOptions.ICS", False },
2883 #ifndef OPTIONSDIALOG
2884 { "menuOptions.Auto Flag", False },
2885 { "menuOptions.Auto Flip View", False },
2886 // { "menuOptions.ICS Alarm", False },
2887 { "menuOptions.Move Sound", False },
2888 { "menuOptions.Hide Thinking", False },
2889 { "menuOptions.Periodic Updates", False },
2890 { "menuOptions.Ponder Next Move", False },
2892 { "menuEngine.Hint", False },
2893 { "menuEngine.Book", False },
2897 Enables gnuEnables[] = {
2898 { "menuMode.ICS Client", False },
2899 { "menuView.ICStex", False },
2900 { "menuView.ICS Input Box", False },
2901 { "menuAction.Accept", False },
2902 { "menuAction.Decline", False },
2903 { "menuAction.Rematch", False },
2904 { "menuAction.Adjourn", False },
2905 { "menuAction.Stop Examining", False },
2906 { "menuAction.Stop Observing", False },
2907 { "menuAction.Upload to Examine", False },
2908 { "menuEdit.Revert", False },
2909 { "menuEdit.Annotate", False },
2910 { "menuOptions.ICS", False },
2912 /* The next two options rely on SetCmailMode being called *after* */
2913 /* SetGNUMode so that when GNU is being used to give hints these */
2914 /* menu options are still available */
2916 { "menuFile.Mail Move", False },
2917 { "menuFile.Reload CMail Message", False },
2918 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2919 { "menuMode.Machine White", True },
2920 { "menuMode.Machine Black", True },
2921 { "menuMode.Analysis Mode", True },
2922 { "menuMode.Analyze File", True },
2923 { "menuMode.Two Machines", True },
2924 { "menuMode.Machine Match", True },
2925 { "menuEngine.Engine #1 Settings", True },
2926 { "menuEngine.Engine #2 Settings", True },
2927 { "menuEngine.Hint", True },
2928 { "menuEngine.Book", True },
2929 { "menuEngine.Move Now", True },
2930 { "menuEngine.Retract Move", True },
2935 Enables cmailEnables[] = {
2937 { "menuAction.Call Flag", False },
2938 { "menuAction.Draw", True },
2939 { "menuAction.Adjourn", False },
2940 { "menuAction.Abort", False },
2941 { "menuAction.Stop Observing", False },
2942 { "menuAction.Stop Examining", False },
2943 { "menuFile.Mail Move", True },
2944 { "menuFile.Reload CMail Message", True },
2948 Enables trainingOnEnables[] = {
2949 { "menuMode.Edit Comment", False },
2950 { "menuMode.Pause", False },
2951 { "menuEdit.Forward", False },
2952 { "menuEdit.Backward", False },
2953 { "menuEdit.Forward to End", False },
2954 { "menuEdit.Back to Start", False },
2955 { "menuEngine.Move Now", False },
2956 { "menuEdit.Truncate Game", False },
2960 Enables trainingOffEnables[] = {
2961 { "menuMode.Edit Comment", True },
2962 { "menuMode.Pause", True },
2963 { "menuEdit.Forward", True },
2964 { "menuEdit.Backward", True },
2965 { "menuEdit.Forward to End", True },
2966 { "menuEdit.Back to Start", True },
2967 { "menuEngine.Move Now", True },
2968 { "menuEdit.Truncate Game", True },
2972 Enables machineThinkingEnables[] = {
2973 { "menuFile.Load Game", False },
2974 // { "menuFile.Load Next Game", False },
2975 // { "menuFile.Load Previous Game", False },
2976 // { "menuFile.Reload Same Game", False },
2977 { "menuEdit.Paste Game", False },
2978 { "menuFile.Load Position", False },
2979 // { "menuFile.Load Next Position", False },
2980 // { "menuFile.Load Previous Position", False },
2981 // { "menuFile.Reload Same Position", False },
2982 { "menuEdit.Paste Position", False },
2983 { "menuMode.Machine White", False },
2984 { "menuMode.Machine Black", False },
2985 { "menuMode.Two Machines", False },
2986 // { "menuMode.Machine Match", False },
2987 { "menuEngine.Retract Move", False },
2991 Enables userThinkingEnables[] = {
2992 { "menuFile.Load Game", True },
2993 // { "menuFile.Load Next Game", True },
2994 // { "menuFile.Load Previous Game", True },
2995 // { "menuFile.Reload Same Game", True },
2996 { "menuEdit.Paste Game", True },
2997 { "menuFile.Load Position", True },
2998 // { "menuFile.Load Next Position", True },
2999 // { "menuFile.Load Previous Position", True },
3000 // { "menuFile.Reload Same Position", True },
3001 { "menuEdit.Paste Position", True },
3002 { "menuMode.Machine White", True },
3003 { "menuMode.Machine Black", True },
3004 { "menuMode.Two Machines", True },
3005 // { "menuMode.Machine Match", True },
3006 { "menuEngine.Retract Move", True },
3013 SetMenuEnables(icsEnables);
3016 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3017 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3018 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3026 SetMenuEnables(ncpEnables);
3032 SetMenuEnables(gnuEnables);
3038 SetMenuEnables(cmailEnables);
3042 SetTrainingModeOn ()
3044 SetMenuEnables(trainingOnEnables);
3045 if (appData.showButtonBar) {
3046 XtSetSensitive(buttonBarWidget, False);
3052 SetTrainingModeOff ()
3054 SetMenuEnables(trainingOffEnables);
3055 if (appData.showButtonBar) {
3056 XtSetSensitive(buttonBarWidget, True);
3061 SetUserThinkingEnables ()
3063 if (appData.noChessProgram) return;
3064 SetMenuEnables(userThinkingEnables);
3068 SetMachineThinkingEnables ()
3070 if (appData.noChessProgram) return;
3071 SetMenuEnables(machineThinkingEnables);
3073 case MachinePlaysBlack:
3074 case MachinePlaysWhite:
3075 case TwoMachinesPlay:
3076 XtSetSensitive(XtNameToWidget(menuBarWidget,
3077 ModeToWidgetName(gameMode)), True);
3084 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3085 #define HISTORY_SIZE 64
3086 static char *history[HISTORY_SIZE];
3087 int histIn = 0, histP = 0;
3090 SaveInHistory (char *cmd)
3092 if (history[histIn] != NULL) {
3093 free(history[histIn]);
3094 history[histIn] = NULL;
3096 if (*cmd == NULLCHAR) return;
3097 history[histIn] = StrSave(cmd);
3098 histIn = (histIn + 1) % HISTORY_SIZE;
3099 if (history[histIn] != NULL) {
3100 free(history[histIn]);
3101 history[histIn] = NULL;
3107 PrevInHistory (char *cmd)
3110 if (histP == histIn) {
3111 if (history[histIn] != NULL) free(history[histIn]);
3112 history[histIn] = StrSave(cmd);
3114 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3115 if (newhp == histIn || history[newhp] == NULL) return NULL;
3117 return history[histP];
3123 if (histP == histIn) return NULL;
3124 histP = (histP + 1) % HISTORY_SIZE;
3125 return history[histP];
3127 // end of borrowed code
3129 #define Abs(n) ((n)<0 ? -(n) : (n))
3133 InsertPxlSize (char *pattern, int targetPxlSize)
3135 char *base_fnt_lst, strInt[12], *p, *q;
3136 int alternatives, i, len, strIntLen;
3139 * Replace the "*" (if present) in the pixel-size slot of each
3140 * alternative with the targetPxlSize.
3144 while ((p = strchr(p, ',')) != NULL) {
3148 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3149 strIntLen = strlen(strInt);
3150 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3154 while (alternatives--) {
3155 char *comma = strchr(p, ',');
3156 for (i=0; i<14; i++) {
3157 char *hyphen = strchr(p, '-');
3159 if (comma && hyphen > comma) break;
3160 len = hyphen + 1 - p;
3161 if (i == 7 && *p == '*' && len == 2) {
3163 memcpy(q, strInt, strIntLen);
3173 len = comma + 1 - p;
3180 return base_fnt_lst;
3184 CreateFontSet (char *base_fnt_lst)
3187 char **missing_list;
3191 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3192 &missing_list, &missing_count, &def_string);
3193 if (appData.debugMode) {
3195 XFontStruct **font_struct_list;
3196 char **font_name_list;
3197 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3199 fprintf(debugFP, " got list %s, locale %s\n",
3200 XBaseFontNameListOfFontSet(fntSet),
3201 XLocaleOfFontSet(fntSet));
3202 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3203 for (i = 0; i < count; i++) {
3204 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3207 for (i = 0; i < missing_count; i++) {
3208 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3211 if (fntSet == NULL) {
3212 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3217 #else // not ENABLE_NLS
3219 * Find a font that matches "pattern" that is as close as
3220 * possible to the targetPxlSize. Prefer fonts that are k
3221 * pixels smaller to fonts that are k pixels larger. The
3222 * pattern must be in the X Consortium standard format,
3223 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3224 * The return value should be freed with XtFree when no
3228 FindFont (char *pattern, int targetPxlSize)
3230 char **fonts, *p, *best, *scalable, *scalableTail;
3231 int i, j, nfonts, minerr, err, pxlSize;
3233 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3235 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3236 programName, pattern);
3243 for (i=0; i<nfonts; i++) {
3246 if (*p != '-') continue;
3248 if (*p == NULLCHAR) break;
3249 if (*p++ == '-') j++;
3251 if (j < 7) continue;
3254 scalable = fonts[i];
3257 err = pxlSize - targetPxlSize;
3258 if (Abs(err) < Abs(minerr) ||
3259 (minerr > 0 && err < 0 && -err == minerr)) {
3265 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3266 /* If the error is too big and there is a scalable font,
3267 use the scalable font. */
3268 int headlen = scalableTail - scalable;
3269 p = (char *) XtMalloc(strlen(scalable) + 10);
3270 while (isdigit(*scalableTail)) scalableTail++;
3271 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3273 p = (char *) XtMalloc(strlen(best) + 2);
3274 safeStrCpy(p, best, strlen(best)+1 );
3276 if (appData.debugMode) {
3277 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3278 pattern, targetPxlSize, p);
3280 XFreeFontNames(fonts);
3287 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3288 // must be called before all non-first callse to CreateGCs()
3289 XtReleaseGC(shellWidget, highlineGC);
3290 XtReleaseGC(shellWidget, lightSquareGC);
3291 XtReleaseGC(shellWidget, darkSquareGC);
3292 XtReleaseGC(shellWidget, lineGC);
3293 if (appData.monoMode) {
3294 if (DefaultDepth(xDisplay, xScreen) == 1) {
3295 XtReleaseGC(shellWidget, wbPieceGC);
3297 XtReleaseGC(shellWidget, bwPieceGC);
3300 XtReleaseGC(shellWidget, prelineGC);
3301 XtReleaseGC(shellWidget, jailSquareGC);
3302 XtReleaseGC(shellWidget, wdPieceGC);
3303 XtReleaseGC(shellWidget, wlPieceGC);
3304 XtReleaseGC(shellWidget, wjPieceGC);
3305 XtReleaseGC(shellWidget, bdPieceGC);
3306 XtReleaseGC(shellWidget, blPieceGC);
3307 XtReleaseGC(shellWidget, bjPieceGC);
3312 CreateGCs (int redo)
3314 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3315 | GCBackground | GCFunction | GCPlaneMask;
3316 XGCValues gc_values;
3319 gc_values.plane_mask = AllPlanes;
3320 gc_values.line_width = lineGap;
3321 gc_values.line_style = LineSolid;
3322 gc_values.function = GXcopy;
3325 DeleteGCs(); // called a second time; clean up old GCs first
3326 } else { // [HGM] grid and font GCs created on first call only
3327 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3328 gc_values.background = XWhitePixel(xDisplay, xScreen);
3329 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3330 XSetFont(xDisplay, coordGC, coordFontID);
3332 // [HGM] make font for holdings counts (white on black)
3333 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3334 gc_values.background = XBlackPixel(xDisplay, xScreen);
3335 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3336 XSetFont(xDisplay, countGC, countFontID);
3338 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3339 gc_values.background = XBlackPixel(xDisplay, xScreen);
3340 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3342 if (appData.monoMode) {
3343 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3344 gc_values.background = XWhitePixel(xDisplay, xScreen);
3345 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3347 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3348 gc_values.background = XBlackPixel(xDisplay, xScreen);
3349 lightSquareGC = wbPieceGC
3350 = XtGetGC(shellWidget, value_mask, &gc_values);
3352 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3353 gc_values.background = XWhitePixel(xDisplay, xScreen);
3354 darkSquareGC = bwPieceGC
3355 = XtGetGC(shellWidget, value_mask, &gc_values);
3357 if (DefaultDepth(xDisplay, xScreen) == 1) {
3358 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3359 gc_values.function = GXcopyInverted;
3360 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3361 gc_values.function = GXcopy;
3362 if (XBlackPixel(xDisplay, xScreen) == 1) {
3363 bwPieceGC = darkSquareGC;
3364 wbPieceGC = copyInvertedGC;
3366 bwPieceGC = copyInvertedGC;
3367 wbPieceGC = lightSquareGC;
3371 gc_values.foreground = highlightSquareColor;
3372 gc_values.background = highlightSquareColor;
3373 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3375 gc_values.foreground = premoveHighlightColor;
3376 gc_values.background = premoveHighlightColor;
3377 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3379 gc_values.foreground = lightSquareColor;
3380 gc_values.background = darkSquareColor;
3381 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3383 gc_values.foreground = darkSquareColor;
3384 gc_values.background = lightSquareColor;
3385 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3387 gc_values.foreground = jailSquareColor;
3388 gc_values.background = jailSquareColor;
3389 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3391 gc_values.foreground = whitePieceColor;
3392 gc_values.background = darkSquareColor;
3393 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3395 gc_values.foreground = whitePieceColor;
3396 gc_values.background = lightSquareColor;
3397 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3399 gc_values.foreground = whitePieceColor;
3400 gc_values.background = jailSquareColor;
3401 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3403 gc_values.foreground = blackPieceColor;
3404 gc_values.background = darkSquareColor;
3405 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3407 gc_values.foreground = blackPieceColor;
3408 gc_values.background = lightSquareColor;
3409 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3411 gc_values.foreground = blackPieceColor;
3412 gc_values.background = jailSquareColor;
3413 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3418 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3426 fp = fopen(filename, "rb");
3428 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3435 for (y=0; y<h; ++y) {
3436 for (x=0; x<h; ++x) {
3441 XPutPixel(xim, x, y, blackPieceColor);
3443 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3446 XPutPixel(xim, x, y, darkSquareColor);
3448 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3451 XPutPixel(xim, x, y, whitePieceColor);
3453 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3456 XPutPixel(xim, x, y, lightSquareColor);
3458 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3466 /* create Pixmap of piece */
3467 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3469 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3472 /* create Pixmap of clipmask
3473 Note: We assume the white/black pieces have the same
3474 outline, so we make only 6 masks. This is okay
3475 since the XPM clipmask routines do the same. */
3477 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3479 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3482 /* now create the 1-bit version */
3483 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3486 values.foreground = 1;
3487 values.background = 0;
3489 /* Don't use XtGetGC, not read only */
3490 maskGC = XCreateGC(xDisplay, *mask,
3491 GCForeground | GCBackground, &values);
3492 XCopyPlane(xDisplay, temp, *mask, maskGC,
3493 0, 0, squareSize, squareSize, 0, 0, 1);
3494 XFreePixmap(xDisplay, temp);
3499 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3507 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3512 /* The XSynchronize calls were copied from CreatePieces.
3513 Not sure if needed, but can't hurt */
3514 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3517 /* temp needed by loadXIM() */
3518 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3519 0, 0, ss, ss, AllPlanes, XYPixmap);
3521 if (strlen(appData.pixmapDirectory) == 0) {
3525 if (appData.monoMode) {
3526 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3530 fprintf(stderr, _("\nLoading XIMs...\n"));
3532 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3533 fprintf(stderr, "%d", piece+1);
3534 for (kind=0; kind<4; kind++) {
3535 fprintf(stderr, ".");
3536 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3537 ExpandPathName(appData.pixmapDirectory),
3538 piece <= (int) WhiteKing ? "" : "w",
3539 pieceBitmapNames[piece],
3541 ximPieceBitmap[kind][piece] =
3542 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3543 0, 0, ss, ss, AllPlanes, XYPixmap);
3544 if (appData.debugMode)
3545 fprintf(stderr, _("(File:%s:) "), buf);
3546 loadXIM(ximPieceBitmap[kind][piece],
3548 &(xpmPieceBitmap2[kind][piece]),
3549 &(ximMaskPm2[piece]));
3550 if(piece <= (int)WhiteKing)
3551 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3553 fprintf(stderr," ");
3555 /* Load light and dark squares */
3556 /* If the LSQ and DSQ pieces don't exist, we will
3557 draw them with solid squares. */
3558 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3559 if (access(buf, 0) != 0) {
3563 fprintf(stderr, _("light square "));
3565 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3566 0, 0, ss, ss, AllPlanes, XYPixmap);
3567 if (appData.debugMode)
3568 fprintf(stderr, _("(File:%s:) "), buf);
3570 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3571 fprintf(stderr, _("dark square "));
3572 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3573 ExpandPathName(appData.pixmapDirectory), ss);
3574 if (appData.debugMode)
3575 fprintf(stderr, _("(File:%s:) "), buf);
3577 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3578 0, 0, ss, ss, AllPlanes, XYPixmap);
3579 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3580 xpmJailSquare = xpmLightSquare;
3582 fprintf(stderr, _("Done.\n"));
3584 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3587 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3591 CreateXPMBoard (char *s, int kind)
3595 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3596 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3597 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3603 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3604 // thisroutine has to be called t free the old piece pixmaps
3606 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3607 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3609 XFreePixmap(xDisplay, xpmLightSquare);
3610 XFreePixmap(xDisplay, xpmDarkSquare);
3619 u_int ss = squareSize;
3621 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3622 XpmColorSymbol symbols[4];
3623 static int redo = False;
3625 if(redo) FreeXPMPieces(); else redo = 1;
3627 /* The XSynchronize calls were copied from CreatePieces.
3628 Not sure if needed, but can't hurt */
3629 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3631 /* Setup translations so piece colors match square colors */
3632 symbols[0].name = "light_piece";
3633 symbols[0].value = appData.whitePieceColor;
3634 symbols[1].name = "dark_piece";
3635 symbols[1].value = appData.blackPieceColor;
3636 symbols[2].name = "light_square";
3637 symbols[2].value = appData.lightSquareColor;
3638 symbols[3].name = "dark_square";
3639 symbols[3].value = appData.darkSquareColor;
3641 attr.valuemask = XpmColorSymbols;
3642 attr.colorsymbols = symbols;
3643 attr.numsymbols = 4;
3645 if (appData.monoMode) {
3646 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3650 if (strlen(appData.pixmapDirectory) == 0) {
3651 XpmPieces* pieces = builtInXpms;
3654 while (pieces->size != squareSize && pieces->size) pieces++;
3655 if (!pieces->size) {
3656 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3659 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3660 for (kind=0; kind<4; kind++) {
3662 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3663 pieces->xpm[piece][kind],
3664 &(xpmPieceBitmap2[kind][piece]),
3665 NULL, &attr)) != 0) {
3666 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3670 if(piece <= (int) WhiteKing)
3671 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3675 xpmJailSquare = xpmLightSquare;
3679 fprintf(stderr, _("\nLoading XPMs...\n"));
3682 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3683 fprintf(stderr, "%d ", piece+1);
3684 for (kind=0; kind<4; kind++) {
3685 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3686 ExpandPathName(appData.pixmapDirectory),
3687 piece > (int) WhiteKing ? "w" : "",
3688 pieceBitmapNames[piece],
3690 if (appData.debugMode) {
3691 fprintf(stderr, _("(File:%s:) "), buf);
3693 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3694 &(xpmPieceBitmap2[kind][piece]),
3695 NULL, &attr)) != 0) {
3696 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3697 // [HGM] missing: read of unorthodox piece failed; substitute King.
3698 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3699 ExpandPathName(appData.pixmapDirectory),
3701 if (appData.debugMode) {
3702 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3704 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3705 &(xpmPieceBitmap2[kind][piece]),
3709 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3714 if(piece <= (int) WhiteKing)
3715 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3718 /* Load light and dark squares */
3719 /* If the LSQ and DSQ pieces don't exist, we will
3720 draw them with solid squares. */
3721 fprintf(stderr, _("light square "));
3722 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3723 if (access(buf, 0) != 0) {
3727 if (appData.debugMode)
3728 fprintf(stderr, _("(File:%s:) "), buf);
3730 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3731 &xpmLightSquare, NULL, &attr)) != 0) {
3732 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3735 fprintf(stderr, _("dark square "));
3736 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3737 ExpandPathName(appData.pixmapDirectory), ss);
3738 if (appData.debugMode) {
3739 fprintf(stderr, _("(File:%s:) "), buf);
3741 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3742 &xpmDarkSquare, NULL, &attr)) != 0) {
3743 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3747 xpmJailSquare = xpmLightSquare;
3748 fprintf(stderr, _("Done.\n"));
3750 oldVariant = -1; // kludge to force re-makig of animation masks
3751 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3754 #endif /* HAVE_LIBXPM */
3757 /* No built-in bitmaps */
3762 u_int ss = squareSize;
3764 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3767 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3768 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3769 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3770 pieceBitmapNames[piece],
3771 ss, kind == SOLID ? 's' : 'o');
3772 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3773 if(piece <= (int)WhiteKing)
3774 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3778 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3782 /* With built-in bitmaps */
3786 BuiltInBits* bib = builtInBits;
3789 u_int ss = squareSize;
3791 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3794 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3796 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3797 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3798 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3799 pieceBitmapNames[piece],
3800 ss, kind == SOLID ? 's' : 'o');
3801 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3802 bib->bits[kind][piece], ss, ss);
3803 if(piece <= (int)WhiteKing)
3804 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3808 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3814 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3819 char msg[MSG_SIZ], fullname[MSG_SIZ];
3821 if (*appData.bitmapDirectory != NULLCHAR) {
3822 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3823 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3824 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3825 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3826 &w, &h, pm, &x_hot, &y_hot);
3827 fprintf(stderr, "load %s\n", name);
3828 if (errcode != BitmapSuccess) {
3830 case BitmapOpenFailed:
3831 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3833 case BitmapFileInvalid:
3834 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3836 case BitmapNoMemory:
3837 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3841 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3845 fprintf(stderr, _("%s: %s...using built-in\n"),
3847 } else if (w != wreq || h != hreq) {
3849 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3850 programName, fullname, w, h, wreq, hreq);
3856 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3866 if (lineGap == 0) return;
3868 /* [HR] Split this into 2 loops for non-square boards. */
3870 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3871 gridSegments[i].x1 = 0;
3872 gridSegments[i].x2 =
3873 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3874 gridSegments[i].y1 = gridSegments[i].y2
3875 = lineGap / 2 + (i * (squareSize + lineGap));
3878 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3879 gridSegments[j + i].y1 = 0;
3880 gridSegments[j + i].y2 =
3881 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3882 gridSegments[j + i].x1 = gridSegments[j + i].x2
3883 = lineGap / 2 + (j * (squareSize + lineGap));
3888 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3890 XtActionProc proc = (XtActionProc) addr;
3892 (proc)(NULL, NULL, NULL, NULL);
3896 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3898 RecentEngineEvent((int) addr);
3902 AppendEnginesToMenu (Widget menu, char *list)
3910 if(appData.recentEngines <= 0) return;
3911 recentEngines = strdup(list);
3913 XtSetArg(args[j], XtNleftMargin, 20); j++;
3914 XtSetArg(args[j], XtNrightMargin, 20); j++;
3916 p = strchr(list, '\n'); if(p == NULL) break;
3917 if(i == 0) XtCreateManagedWidget(_("----"), smeLineObjectClass, menu, args, j); // at least one valid item to add
3919 XtSetArg(args[j], XtNlabel, XtNewString(list));
3920 entry = XtCreateManagedWidget("engine", smeBSBObjectClass, menu, args, j+1);
3921 XtAddCallback(entry, XtNcallback,
3922 (XtCallbackProc) MenuEngineSelect,
3924 i++; *p = '\n'; list = p + 1;
3929 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3936 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3939 XtSetArg(args[j], XtNleftMargin, 20); j++;
3940 XtSetArg(args[j], XtNrightMargin, 20); j++;
3942 while (mi->string != NULL) {
3943 if (strcmp(mi->string, "----") == 0) {
3944 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3947 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3948 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3950 XtAddCallback(entry, XtNcallback,
3951 (XtCallbackProc) MenuBarSelect,
3952 (caddr_t) mi->proc);
3956 if(!strcmp(mb->name, "Engine")) AppendEnginesToMenu(menu, appData.recentEngineList);
3960 CreateMenuBar (Menu *mb, int boardWidth)
3962 int i, j, nr = 0, wtot = 0, widths[10];
3965 char menuName[MSG_SIZ];
3970 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3971 XtSetArg(args[j], XtNvSpace, 0); j++;
3972 XtSetArg(args[j], XtNborderWidth, 0); j++;
3973 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3974 formWidget, args, j);
3976 while (mb->name != NULL) {
3977 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3978 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3980 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3981 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3982 XtSetArg(args[j], XtNborderWidth, 0); j++;
3983 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3985 CreateMenuBarPopup(menuBar, menuName, mb);
3987 XtSetArg(args[j], XtNwidth, &w); j++;
3988 XtGetValues(mb->subMenu, args, j);
3989 wtot += mb->textWidth = widths[nr++] = w;
3992 while(wtot > boardWidth - 40) {
3994 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3998 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
4000 XtSetArg(args[j], XtNwidth, widths[i]); j++;
4001 XtSetValues(ma[i].subMenu, args, j);
4007 CreateButtonBar (MenuItem *mi)
4010 Widget button, buttonBar;
4014 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4016 XtSetArg(args[j], XtNhSpace, 0); j++;
4018 XtSetArg(args[j], XtNborderWidth, 0); j++;
4019 XtSetArg(args[j], XtNvSpace, 0); j++;
4020 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4021 formWidget, args, j);
4023 while (mi->string != NULL) {
4026 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4027 XtSetArg(args[j], XtNborderWidth, 0); j++;
4029 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4030 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4031 buttonBar, args, j);
4032 XtAddCallback(button, XtNcallback,
4033 (XtCallbackProc) MenuBarSelect,
4034 (caddr_t) mi->proc);
4041 CreatePieceMenu (char *name, int color)
4046 ChessSquare selection;
4048 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4049 boardWidget, args, 0);
4051 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4052 String item = pieceMenuStrings[color][i];
4054 if (strcmp(item, "----") == 0) {
4055 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4058 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4059 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4061 selection = pieceMenuTranslation[color][i];
4062 XtAddCallback(entry, XtNcallback,
4063 (XtCallbackProc) PieceMenuSelect,
4064 (caddr_t) selection);
4065 if (selection == WhitePawn || selection == BlackPawn) {
4066 XtSetArg(args[0], XtNpopupOnEntry, entry);
4067 XtSetValues(menu, args, 1);
4080 ChessSquare selection;
4082 whitePieceMenu = CreatePieceMenu("menuW", 0);
4083 blackPieceMenu = CreatePieceMenu("menuB", 1);
4085 XtRegisterGrabAction(PieceMenuPopup, True,
4086 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4087 GrabModeAsync, GrabModeAsync);
4089 XtSetArg(args[0], XtNlabel, _("Drop"));
4090 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4091 boardWidget, args, 1);
4092 for (i = 0; i < DROP_MENU_SIZE; i++) {
4093 String item = dropMenuStrings[i];
4095 if (strcmp(item, "----") == 0) {
4096 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4099 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4100 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4102 selection = dropMenuTranslation[i];
4103 XtAddCallback(entry, XtNcallback,
4104 (XtCallbackProc) DropMenuSelect,
4105 (caddr_t) selection);
4119 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4120 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4121 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4122 dmEnables[i].piece);
4123 XtSetSensitive(entry, p != NULL || !appData.testLegality
4124 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4125 && !appData.icsActive));
4127 while (p && *p++ == dmEnables[i].piece) count++;
4128 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4130 XtSetArg(args[j], XtNlabel, label); j++;
4131 XtSetValues(entry, args, j);
4136 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4138 String whichMenu; int menuNr = -2;
4139 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4140 if (event->type == ButtonRelease)
4141 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4142 else if (event->type == ButtonPress)
4143 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4145 case 0: whichMenu = params[0]; break;
4146 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4148 case -1: if (errorUp) ErrorPopDown();
4151 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4155 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4157 if (pmFromX < 0 || pmFromY < 0) return;
4158 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4162 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4164 if (pmFromX < 0 || pmFromY < 0) return;
4165 DropMenuEvent(piece, pmFromX, pmFromY);
4169 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4171 shiftKey = prms[0][0] & 1;
4176 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4178 shiftKey = prms[0][0] & 1;
4184 * If the user selects on a border boundary, return -1; if off the board,
4185 * return -2. Otherwise map the event coordinate to the square.
4188 EventToSquare (int x, int limit)
4195 if ((x % (squareSize + lineGap)) >= squareSize)
4197 x /= (squareSize + lineGap);
4204 do_flash_delay (unsigned long msec)
4210 drawHighlight (int file, int rank, GC gc)
4214 if (lineGap == 0) return;
4217 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4218 (squareSize + lineGap);
4219 y = lineGap/2 + rank * (squareSize + lineGap);
4221 x = lineGap/2 + file * (squareSize + lineGap);
4222 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4223 (squareSize + lineGap);
4226 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4227 squareSize+lineGap, squareSize+lineGap);
4230 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4231 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4234 SetHighlights (int fromX, int fromY, int toX, int toY)
4236 if (hi1X != fromX || hi1Y != fromY) {
4237 if (hi1X >= 0 && hi1Y >= 0) {
4238 drawHighlight(hi1X, hi1Y, lineGC);
4240 } // [HGM] first erase both, then draw new!
4241 if (hi2X != toX || hi2Y != toY) {
4242 if (hi2X >= 0 && hi2Y >= 0) {
4243 drawHighlight(hi2X, hi2Y, lineGC);
4246 if (hi1X != fromX || hi1Y != fromY) {
4247 if (fromX >= 0 && fromY >= 0) {
4248 drawHighlight(fromX, fromY, highlineGC);
4251 if (hi2X != toX || hi2Y != toY) {
4252 if (toX >= 0 && toY >= 0) {
4253 drawHighlight(toX, toY, highlineGC);
4256 if(toX<0) // clearing the highlights must have damaged arrow
4257 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4267 SetHighlights(-1, -1, -1, -1);
4272 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4274 if (pm1X != fromX || pm1Y != fromY) {
4275 if (pm1X >= 0 && pm1Y >= 0) {
4276 drawHighlight(pm1X, pm1Y, lineGC);
4278 if (fromX >= 0 && fromY >= 0) {
4279 drawHighlight(fromX, fromY, prelineGC);
4282 if (pm2X != toX || pm2Y != toY) {
4283 if (pm2X >= 0 && pm2Y >= 0) {
4284 drawHighlight(pm2X, pm2Y, lineGC);
4286 if (toX >= 0 && toY >= 0) {
4287 drawHighlight(toX, toY, prelineGC);
4297 ClearPremoveHighlights ()
4299 SetPremoveHighlights(-1, -1, -1, -1);
4303 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4305 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4306 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4308 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4309 if(textureW[kind] < W*squareSize)
4310 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4312 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4313 if(textureH[kind] < H*squareSize)
4314 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4316 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4321 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4322 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4324 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4325 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4326 squareSize, squareSize, x*fac, y*fac);
4328 if (useImages && useImageSqs) {
4332 pm = xpmLightSquare;
4337 case 2: /* neutral */
4342 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4343 squareSize, squareSize, x*fac, y*fac);
4353 case 2: /* neutral */
4358 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4363 I split out the routines to draw a piece so that I could
4364 make a generic flash routine.
4367 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4369 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4370 switch (square_color) {
4372 case 2: /* neutral */
4374 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4375 ? *pieceToOutline(piece)
4376 : *pieceToSolid(piece),
4377 dest, bwPieceGC, 0, 0,
4378 squareSize, squareSize, x, y);
4381 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4382 ? *pieceToSolid(piece)
4383 : *pieceToOutline(piece),
4384 dest, wbPieceGC, 0, 0,
4385 squareSize, squareSize, x, y);
4391 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4393 switch (square_color) {
4395 case 2: /* neutral */
4397 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4398 ? *pieceToOutline(piece)
4399 : *pieceToSolid(piece),
4400 dest, bwPieceGC, 0, 0,
4401 squareSize, squareSize, x, y, 1);
4404 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4405 ? *pieceToSolid(piece)
4406 : *pieceToOutline(piece),
4407 dest, wbPieceGC, 0, 0,
4408 squareSize, squareSize, x, y, 1);
4414 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4416 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4417 switch (square_color) {
4419 XCopyPlane(xDisplay, *pieceToSolid(piece),
4420 dest, (int) piece < (int) BlackPawn
4421 ? wlPieceGC : blPieceGC, 0, 0,
4422 squareSize, squareSize, x, y, 1);
4425 XCopyPlane(xDisplay, *pieceToSolid(piece),
4426 dest, (int) piece < (int) BlackPawn
4427 ? wdPieceGC : bdPieceGC, 0, 0,
4428 squareSize, squareSize, x, y, 1);
4430 case 2: /* neutral */
4432 XCopyPlane(xDisplay, *pieceToSolid(piece),
4433 dest, (int) piece < (int) BlackPawn
4434 ? wjPieceGC : bjPieceGC, 0, 0,
4435 squareSize, squareSize, x, y, 1);
4441 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4443 int kind, p = piece;
4445 switch (square_color) {
4447 case 2: /* neutral */
4449 if ((int)piece < (int) BlackPawn) {
4457 if ((int)piece < (int) BlackPawn) {
4465 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4466 if(useTexture & square_color+1) {
4467 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4468 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4469 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4470 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4471 XSetClipMask(xDisplay, wlPieceGC, None);
4472 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4474 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4475 dest, wlPieceGC, 0, 0,
4476 squareSize, squareSize, x, y);
4479 typedef void (*DrawFunc)();
4484 if (appData.monoMode) {
4485 if (DefaultDepth(xDisplay, xScreen) == 1) {
4486 return monoDrawPiece_1bit;
4488 return monoDrawPiece;
4492 return colorDrawPieceImage;
4494 return colorDrawPiece;
4498 /* [HR] determine square color depending on chess variant. */
4500 SquareColor (int row, int column)
4504 if (gameInfo.variant == VariantXiangqi) {
4505 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4507 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4509 } else if (row <= 4) {
4515 square_color = ((column + row) % 2) == 1;
4518 /* [hgm] holdings: next line makes all holdings squares light */
4519 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4521 return square_color;
4525 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4527 int square_color, x, y, direction, font_ascent, font_descent;
4530 XCharStruct overall;
4534 /* Calculate delay in milliseconds (2-delays per complete flash) */
4535 flash_delay = 500 / appData.flashRate;
4538 x = lineGap + ((BOARD_WIDTH-1)-column) *
4539 (squareSize + lineGap);
4540 y = lineGap + row * (squareSize + lineGap);
4542 x = lineGap + column * (squareSize + lineGap);
4543 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4544 (squareSize + lineGap);
4547 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4549 square_color = SquareColor(row, column);
4551 if ( // [HGM] holdings: blank out area between board and holdings
4552 column == BOARD_LEFT-1 || column == BOARD_RGHT
4553 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4554 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4555 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4557 // [HGM] print piece counts next to holdings
4558 string[1] = NULLCHAR;
4559 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4560 string[0] = '0' + piece;
4561 XTextExtents(countFontStruct, string, 1, &direction,
4562 &font_ascent, &font_descent, &overall);
4563 if (appData.monoMode) {
4564 XDrawImageString(xDisplay, xBoardWindow, countGC,
4565 x + squareSize - overall.width - 2,
4566 y + font_ascent + 1, string, 1);
4568 XDrawString(xDisplay, xBoardWindow, countGC,
4569 x + squareSize - overall.width - 2,
4570 y + font_ascent + 1, string, 1);
4573 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4574 string[0] = '0' + piece;
4575 XTextExtents(countFontStruct, string, 1, &direction,
4576 &font_ascent, &font_descent, &overall);
4577 if (appData.monoMode) {
4578 XDrawImageString(xDisplay, xBoardWindow, countGC,
4579 x + 2, y + font_ascent + 1, string, 1);
4581 XDrawString(xDisplay, xBoardWindow, countGC,
4582 x + 2, y + font_ascent + 1, string, 1);
4586 if (piece == EmptySquare || appData.blindfold) {
4587 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4589 drawfunc = ChooseDrawFunc();
4591 if (do_flash && appData.flashCount > 0) {
4592 for (i=0; i<appData.flashCount; ++i) {
4593 drawfunc(piece, square_color, x, y, xBoardWindow);
4594 XSync(xDisplay, False);
4595 do_flash_delay(flash_delay);
4597 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4598 XSync(xDisplay, False);
4599 do_flash_delay(flash_delay);
4602 drawfunc(piece, square_color, x, y, xBoardWindow);
4606 string[1] = NULLCHAR;
4607 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4608 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4609 string[0] = 'a' + column - BOARD_LEFT;
4610 XTextExtents(coordFontStruct, string, 1, &direction,
4611 &font_ascent, &font_descent, &overall);
4612 if (appData.monoMode) {
4613 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4614 x + squareSize - overall.width - 2,
4615 y + squareSize - font_descent - 1, string, 1);
4617 XDrawString(xDisplay, xBoardWindow, coordGC,
4618 x + squareSize - overall.width - 2,
4619 y + squareSize - font_descent - 1, string, 1);
4622 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4623 string[0] = ONE + row;
4624 XTextExtents(coordFontStruct, string, 1, &direction,
4625 &font_ascent, &font_descent, &overall);
4626 if (appData.monoMode) {
4627 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4628 x + 2, y + font_ascent + 1, string, 1);
4630 XDrawString(xDisplay, xBoardWindow, coordGC,
4631 x + 2, y + font_ascent + 1, string, 1);
4634 if(!partnerUp && marker[row][column]) {
4635 if(appData.monoMode) {
4636 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4637 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4638 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4639 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4641 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4642 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4647 /* Why is this needed on some versions of X? */
4649 EventProc (Widget widget, caddr_t unused, XEvent *event)
4651 if (!XtIsRealized(widget))
4654 switch (event->type) {
4656 if (event->xexpose.count > 0) return; /* no clipping is done */
4657 XDrawPosition(widget, True, NULL);
4658 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4659 flipView = !flipView; partnerUp = !partnerUp;
4660 XDrawPosition(widget, True, NULL);
4661 flipView = !flipView; partnerUp = !partnerUp;
4665 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4673 DrawPosition (int fullRedraw, Board board)
4675 XDrawPosition(boardWidget, fullRedraw, board);
4678 /* Returns 1 if there are "too many" differences between b1 and b2
4679 (i.e. more than 1 move was made) */
4681 too_many_diffs (Board b1, Board b2)
4686 for (i=0; i<BOARD_HEIGHT; ++i) {
4687 for (j=0; j<BOARD_WIDTH; ++j) {
4688 if (b1[i][j] != b2[i][j]) {
4689 if (++c > 4) /* Castling causes 4 diffs */
4697 /* Matrix describing castling maneuvers */
4698 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4699 static int castling_matrix[4][5] = {
4700 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4701 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4702 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4703 { 7, 7, 4, 5, 6 } /* 0-0, black */
4706 /* Checks whether castling occurred. If it did, *rrow and *rcol
4707 are set to the destination (row,col) of the rook that moved.
4709 Returns 1 if castling occurred, 0 if not.
4711 Note: Only handles a max of 1 castling move, so be sure
4712 to call too_many_diffs() first.
4715 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4720 /* For each type of castling... */
4721 for (i=0; i<4; ++i) {
4722 r = castling_matrix[i];
4724 /* Check the 4 squares involved in the castling move */
4726 for (j=1; j<=4; ++j) {
4727 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4734 /* All 4 changed, so it must be a castling move */
4743 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4745 DrawSeekAxis (int x, int y, int xTo, int yTo)
4747 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4751 DrawSeekBackground (int left, int top, int right, int bottom)
4753 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4757 DrawSeekText (char *buf, int x, int y)
4759 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4763 DrawSeekDot (int x, int y, int colorNr)
4765 int square = colorNr & 0x80;
4768 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4770 XFillRectangle(xDisplay, xBoardWindow, color,
4771 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4773 XFillArc(xDisplay, xBoardWindow, color,
4774 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4777 static int damage[2][BOARD_RANKS][BOARD_FILES];
4780 * event handler for redrawing the board
4783 XDrawPosition (Widget w, int repaint, Board board)
4786 static int lastFlipView = 0;
4787 static int lastBoardValid[2] = {0, 0};
4788 static Board lastBoard[2];
4791 int nr = twoBoards*partnerUp;
4793 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4795 if (board == NULL) {
4796 if (!lastBoardValid[nr]) return;
4797 board = lastBoard[nr];
4799 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4800 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4801 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4806 * It would be simpler to clear the window with XClearWindow()
4807 * but this causes a very distracting flicker.
4810 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4812 if ( lineGap && IsDrawArrowEnabled())
4813 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4814 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4816 /* If too much changes (begin observing new game, etc.), don't
4818 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4820 /* Special check for castling so we don't flash both the king
4821 and the rook (just flash the king). */
4823 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4824 /* Draw rook with NO flashing. King will be drawn flashing later */
4825 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4826 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4830 /* First pass -- Draw (newly) empty squares and repair damage.
4831 This prevents you from having a piece show up twice while it
4832 is flashing on its new square */
4833 for (i = 0; i < BOARD_HEIGHT; i++)
4834 for (j = 0; j < BOARD_WIDTH; j++)
4835 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4836 || damage[nr][i][j]) {
4837 DrawSquare(i, j, board[i][j], 0);
4838 damage[nr][i][j] = False;
4841 /* Second pass -- Draw piece(s) in new position and flash them */
4842 for (i = 0; i < BOARD_HEIGHT; i++)
4843 for (j = 0; j < BOARD_WIDTH; j++)
4844 if (board[i][j] != lastBoard[nr][i][j]) {
4845 DrawSquare(i, j, board[i][j], do_flash);
4849 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4850 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4851 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4853 for (i = 0; i < BOARD_HEIGHT; i++)
4854 for (j = 0; j < BOARD_WIDTH; j++) {
4855 DrawSquare(i, j, board[i][j], 0);
4856 damage[nr][i][j] = False;
4860 CopyBoard(lastBoard[nr], board);
4861 lastBoardValid[nr] = 1;
4862 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4863 lastFlipView = flipView;
4865 /* Draw highlights */
4866 if (pm1X >= 0 && pm1Y >= 0) {
4867 drawHighlight(pm1X, pm1Y, prelineGC);
4869 if (pm2X >= 0 && pm2Y >= 0) {
4870 drawHighlight(pm2X, pm2Y, prelineGC);
4872 if (hi1X >= 0 && hi1Y >= 0) {
4873 drawHighlight(hi1X, hi1Y, highlineGC);
4875 if (hi2X >= 0 && hi2Y >= 0) {
4876 drawHighlight(hi2X, hi2Y, highlineGC);
4878 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4880 /* If piece being dragged around board, must redraw that too */
4883 XSync(xDisplay, False);
4888 * event handler for redrawing the board
4891 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4893 XDrawPosition(w, True, NULL);
4898 * event handler for parsing user moves
4900 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4901 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4902 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4903 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4904 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4905 // and at the end FinishMove() to perform the move after optional promotion popups.
4906 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4908 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4910 if (w != boardWidget || errorExitStatus != -1) return;
4911 if(nprms) shiftKey = !strcmp(prms[0], "1");
4914 if (event->type == ButtonPress) {
4915 XtPopdown(promotionShell);
4916 XtDestroyWidget(promotionShell);
4917 promotionUp = False;
4925 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4926 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4927 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4931 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4933 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4934 DragPieceMove(event->xmotion.x, event->xmotion.y);
4938 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4939 { // [HGM] pv: walk PV
4940 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4943 static int savedIndex; /* gross that this is global */
4946 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4949 XawTextPosition index, dummy;
4952 XawTextGetSelectionPos(w, &index, &dummy);
4953 XtSetArg(arg, XtNstring, &val);
4954 XtGetValues(w, &arg, 1);
4955 ReplaceComment(savedIndex, val);
4956 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4957 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4961 EditCommentPopUp (int index, char *title, char *text)
4964 if (text == NULL) text = "";
4965 NewCommentPopup(title, text, index);
4974 extern Option boxOptions[];
4984 edit = boxOptions[0].handle;
4986 XtSetArg(args[j], XtNstring, &val); j++;
4987 XtGetValues(edit, args, j);
4989 SendMultiLineToICS(val);
4990 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4991 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4995 ICSInputBoxPopDown ()
5001 CommentPopUp (char *title, char *text)
5003 savedIndex = currentMove; // [HGM] vari
5004 NewCommentPopup(title, text, currentMove);
5013 static char *openName;
5019 (void) (*fileProc)(openFP, 0, openName);
5023 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
5025 fileProc = proc; /* I can't see a way not */
5026 fileOpenMode = openMode; /* to use globals here */
5027 { // [HGM] use file-selector dialog stolen from Ghostview
5028 int index; // this is not supported yet
5029 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5030 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5031 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5032 ScheduleDelayedEvent(&DelayedLoad, 50);
5039 if (!filenameUp) return;
5040 XtPopdown(fileNameShell);
5041 XtDestroyWidget(fileNameShell);
5047 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5052 XtSetArg(args[0], XtNlabel, &name);
5053 XtGetValues(w, args, 1);
5055 if (strcmp(name, _("cancel")) == 0) {
5060 FileNameAction(w, NULL, NULL, NULL);
5064 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5072 name = XawDialogGetValueString(w = XtParent(w));
5074 if ((name != NULL) && (*name != NULLCHAR)) {
5075 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5076 XtPopdown(w = XtParent(XtParent(w)));
5080 p = strrchr(buf, ' ');
5087 fullname = ExpandPathName(buf);
5089 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5092 f = fopen(fullname, fileOpenMode);
5094 DisplayError(_("Failed to open file"), errno);
5096 (void) (*fileProc)(f, index, buf);
5103 XtPopdown(w = XtParent(XtParent(w)));
5113 Widget dialog, layout;
5115 Dimension bw_width, pw_width;
5117 char *PromoChars = "wglcqrbnkac+=\0";
5120 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5121 XtGetValues(boardWidget, args, j);
5124 XtSetArg(args[j], XtNresizable, True); j++;
5125 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5127 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5128 shellWidget, args, j);
5130 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5131 layoutArgs, XtNumber(layoutArgs));
5134 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5135 XtSetArg(args[j], XtNborderWidth, 0); j++;
5136 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5139 if(gameInfo.variant != VariantShogi) {
5140 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5141 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5142 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5143 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5144 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5146 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5147 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5148 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5149 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5151 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5152 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5153 gameInfo.variant == VariantGiveaway) {
5154 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5156 if(gameInfo.variant == VariantCapablanca ||
5157 gameInfo.variant == VariantGothic ||
5158 gameInfo.variant == VariantCapaRandom) {
5159 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5160 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5162 } else // [HGM] shogi
5164 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5165 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5167 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5169 XtRealizeWidget(promotionShell);
5170 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5173 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5174 XtGetValues(promotionShell, args, j);
5176 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5177 lineGap + squareSize/3 +
5178 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5179 0 : 6*(squareSize + lineGap)), &x, &y);
5182 XtSetArg(args[j], XtNx, x); j++;
5183 XtSetArg(args[j], XtNy, y); j++;
5184 XtSetValues(promotionShell, args, j);
5186 XtPopup(promotionShell, XtGrabNone);
5194 if (!promotionUp) return;
5195 XtPopdown(promotionShell);
5196 XtDestroyWidget(promotionShell);
5197 promotionUp = False;
5201 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5203 int promoChar = * (const char *) client_data;
5207 if (fromX == -1) return;
5214 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5216 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5217 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5223 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5225 dialogError = errorUp = False;
5226 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5228 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5235 if (!errorUp) return;
5236 dialogError = errorUp = False;
5237 XtPopdown(errorShell);
5238 XtDestroyWidget(errorShell);
5239 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5243 ErrorPopUp (char *title, char *label, int modal)
5246 Widget dialog, layout;
5250 Dimension bw_width, pw_width;
5251 Dimension pw_height;
5255 XtSetArg(args[i], XtNresizable, True); i++;
5256 XtSetArg(args[i], XtNtitle, title); i++;
5258 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5259 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
5261 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5262 layoutArgs, XtNumber(layoutArgs));
5265 XtSetArg(args[i], XtNlabel, label); i++;
5266 XtSetArg(args[i], XtNborderWidth, 0); i++;
5267 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5270 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5272 XtRealizeWidget(errorShell);
5273 CatchDeleteWindow(errorShell, "ErrorPopDown");
5276 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5277 XtGetValues(boardWidget, args, i);
5279 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5280 XtSetArg(args[i], XtNheight, &pw_height); i++;
5281 XtGetValues(errorShell, args, i);
5284 /* This code seems to tickle an X bug if it is executed too soon
5285 after xboard starts up. The coordinates get transformed as if
5286 the main window was positioned at (0, 0).
5288 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5289 0 - pw_height + squareSize / 3, &x, &y);
5291 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5292 RootWindowOfScreen(XtScreen(boardWidget)),
5293 (bw_width - pw_width) / 2,
5294 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5298 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5301 XtSetArg(args[i], XtNx, x); i++;
5302 XtSetArg(args[i], XtNy, y); i++;
5303 XtSetValues(errorShell, args, i);
5306 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5309 /* Disable all user input other than deleting the window */
5310 static int frozen = 0;
5316 /* Grab by a widget that doesn't accept input */
5317 XtAddGrab(messageWidget, TRUE, FALSE);
5321 /* Undo a FreezeUI */
5325 if (!frozen) return;
5326 XtRemoveGrab(messageWidget);
5331 ModeToWidgetName (GameMode mode)
5334 case BeginningOfGame:
5335 if (appData.icsActive)
5336 return "menuMode.ICS Client";
5337 else if (appData.noChessProgram ||
5338 *appData.cmailGameName != NULLCHAR)
5339 return "menuMode.Edit Game";
5341 return "menuMode.Machine Black";
5342 case MachinePlaysBlack:
5343 return "menuMode.Machine Black";
5344 case MachinePlaysWhite:
5345 return "menuMode.Machine White";
5347 return "menuMode.Analysis Mode";
5349 return "menuMode.Analyze File";
5350 case TwoMachinesPlay:
5351 return "menuMode.Two Machines";
5353 return "menuMode.Edit Game";
5354 case PlayFromGameFile:
5355 return "menuFile.Load Game";
5357 return "menuMode.Edit Position";
5359 return "menuMode.Training";
5360 case IcsPlayingWhite:
5361 case IcsPlayingBlack:
5365 return "menuMode.ICS Client";
5376 static int oldPausing = FALSE;
5377 static GameMode oldmode = (GameMode) -1;
5380 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5382 if (pausing != oldPausing) {
5383 oldPausing = pausing;
5385 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5387 XtSetArg(args[0], XtNleftBitmap, None);
5389 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5392 if (appData.showButtonBar) {
5393 /* Always toggle, don't set. Previous code messes up when
5394 invoked while the button is pressed, as releasing it
5395 toggles the state again. */
5398 XtSetArg(args[0], XtNbackground, &oldbg);
5399 XtSetArg(args[1], XtNforeground, &oldfg);
5400 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5402 XtSetArg(args[0], XtNbackground, oldfg);
5403 XtSetArg(args[1], XtNforeground, oldbg);
5405 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5409 wname = ModeToWidgetName(oldmode);
5410 if (wname != NULL) {
5411 XtSetArg(args[0], XtNleftBitmap, None);
5412 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5414 wname = ModeToWidgetName(gameMode);
5415 if (wname != NULL) {
5416 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5417 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5420 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5421 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5423 /* Maybe all the enables should be handled here, not just this one */
5424 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5425 gameMode == Training || gameMode == PlayFromGameFile);
5430 * Button/menu procedures
5433 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5439 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5441 cmailMsgLoaded = FALSE;
5442 if (gameNumber == 0) {
5443 int error = GameListBuild(f);
5445 DisplayError(_("Cannot build game list"), error);
5446 } else if (!ListEmpty(&gameList) &&
5447 ((ListGame *) gameList.tailPred)->number > 1) {
5448 GameListPopUp(f, title);
5454 return LoadGame(f, gameNumber, title, FALSE);
5458 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5460 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5463 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5467 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5473 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5479 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5485 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5491 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5497 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5503 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5505 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5508 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5512 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5514 FileNamePopUp(_("Save game file name?"),
5515 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5516 appData.oldSaveStyle ? ".game" : ".pgn",
5521 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5523 FileNamePopUp(_("Save position file name?"),
5524 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5525 appData.oldSaveStyle ? ".pos" : ".fen",
5530 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5532 ReloadCmailMsgEvent(FALSE);
5536 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5541 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5542 char *selected_fen_position=NULL;
5545 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5546 Atom *type_return, XtPointer *value_return,
5547 unsigned long *length_return, int *format_return)
5549 char *selection_tmp;
5551 if (!selected_fen_position) return False; /* should never happen */
5552 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5553 /* note: since no XtSelectionDoneProc was registered, Xt will
5554 * automatically call XtFree on the value returned. So have to
5555 * make a copy of it allocated with XtMalloc */
5556 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5557 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5559 *value_return=selection_tmp;
5560 *length_return=strlen(selection_tmp);
5561 *type_return=*target;
5562 *format_return = 8; /* bits per byte */
5564 } else if (*target == XA_TARGETS(xDisplay)) {
5565 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5566 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5567 targets_tmp[1] = XA_STRING;
5568 *value_return = targets_tmp;
5569 *type_return = XA_ATOM;
5572 // This code leads to a read of value_return out of bounds on 64-bit systems.
5573 // Other code which I have seen always sets *format_return to 32 independent of
5574 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5575 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5576 *format_return = 8 * sizeof(Atom);
5577 if (*format_return > 32) {
5578 *length_return *= *format_return / 32;
5579 *format_return = 32;
5582 *format_return = 32;
5590 /* note: when called from menu all parameters are NULL, so no clue what the
5591 * Widget which was clicked on was, or what the click event was
5594 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5597 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5598 * have a notion of a position that is selected but not copied.
5599 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5601 if(gameMode == EditPosition) EditPositionDone(TRUE);
5602 if (selected_fen_position) free(selected_fen_position);
5603 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5604 if (!selected_fen_position) return;
5605 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5607 SendPositionSelection,
5608 NULL/* lose_ownership_proc */ ,
5609 NULL/* transfer_done_proc */);
5610 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5612 SendPositionSelection,
5613 NULL/* lose_ownership_proc */ ,
5614 NULL/* transfer_done_proc */);
5617 /* function called when the data to Paste is ready */
5619 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5620 Atom *type, XtPointer value, unsigned long *len, int *format)
5623 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5624 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5625 EditPositionPasteFEN(fenstr);
5629 /* called when Paste Position button is pressed,
5630 * all parameters will be NULL */
5631 void PastePositionProc(w, event, prms, nprms)
5637 XtGetSelectionValue(menuBarWidget,
5638 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5639 /* (XtSelectionCallbackProc) */ PastePositionCB,
5640 NULL, /* client_data passed to PastePositionCB */
5642 /* better to use the time field from the event that triggered the
5643 * call to this function, but that isn't trivial to get
5651 SendGameSelection (Widget w, Atom *selection, Atom *target,
5652 Atom *type_return, XtPointer *value_return,
5653 unsigned long *length_return, int *format_return)
5655 char *selection_tmp;
5657 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5658 FILE* f = fopen(gameCopyFilename, "r");
5661 if (f == NULL) return False;
5665 selection_tmp = XtMalloc(len + 1);
5666 count = fread(selection_tmp, 1, len, f);
5669 XtFree(selection_tmp);
5672 selection_tmp[len] = NULLCHAR;
5673 *value_return = selection_tmp;
5674 *length_return = len;
5675 *type_return = *target;
5676 *format_return = 8; /* bits per byte */
5678 } else if (*target == XA_TARGETS(xDisplay)) {
5679 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5680 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5681 targets_tmp[1] = XA_STRING;
5682 *value_return = targets_tmp;
5683 *type_return = XA_ATOM;
5686 // This code leads to a read of value_return out of bounds on 64-bit systems.
5687 // Other code which I have seen always sets *format_return to 32 independent of
5688 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5689 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5690 *format_return = 8 * sizeof(Atom);
5691 if (*format_return > 32) {
5692 *length_return *= *format_return / 32;
5693 *format_return = 32;
5696 *format_return = 32;
5708 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5709 * have a notion of a game that is selected but not copied.
5710 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5712 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5715 NULL/* lose_ownership_proc */ ,
5716 NULL/* transfer_done_proc */);
5717 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5720 NULL/* lose_ownership_proc */ ,
5721 NULL/* transfer_done_proc */);
5724 /* note: when called from menu all parameters are NULL, so no clue what the
5725 * Widget which was clicked on was, or what the click event was
5728 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5732 ret = SaveGameToFile(gameCopyFilename, FALSE);
5739 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5741 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5745 /* function called when the data to Paste is ready */
5747 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5748 Atom *type, XtPointer value, unsigned long *len, int *format)
5751 if (value == NULL || *len == 0) {
5752 return; /* nothing had been selected to copy */
5754 f = fopen(gamePasteFilename, "w");
5756 DisplayError(_("Can't open temp file"), errno);
5759 fwrite(value, 1, *len, f);
5762 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5765 /* called when Paste Game button is pressed,
5766 * all parameters will be NULL */
5768 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5770 XtGetSelectionValue(menuBarWidget,
5771 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5772 /* (XtSelectionCallbackProc) */ PasteGameCB,
5773 NULL, /* client_data passed to PasteGameCB */
5775 /* better to use the time field from the event that triggered the
5776 * call to this function, but that isn't trivial to get
5787 SaveGameProc(NULL, NULL, NULL, NULL);
5792 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5798 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5804 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5806 MachineBlackEvent();
5810 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5812 MachineWhiteEvent();
5816 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5820 if (!first.analysisSupport) {
5821 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5822 DisplayError(buf, 0);
5825 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5826 if (appData.icsActive) {
5827 if (gameMode != IcsObserving) {
5828 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5829 DisplayError(buf, 0);
5831 if (appData.icsEngineAnalyze) {
5832 if (appData.debugMode)
5833 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5839 /* if enable, use want disable icsEngineAnalyze */
5840 if (appData.icsEngineAnalyze) {
5845 appData.icsEngineAnalyze = TRUE;
5846 if (appData.debugMode)
5847 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5849 #ifndef OPTIONSDIALOG
5850 if (!appData.showThinking)
5851 ShowThinkingProc(w,event,prms,nprms);
5858 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5860 if (!first.analysisSupport) {
5862 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5863 DisplayError(buf, 0);
5866 // Reset(FALSE, TRUE);
5867 #ifndef OPTIONSDIALOG
5868 if (!appData.showThinking)
5869 ShowThinkingProc(w,event,prms,nprms);
5872 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5873 AnalysisPeriodicEvent(1);
5877 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5883 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5889 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5895 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5901 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5903 EditPositionEvent();
5907 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5913 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5917 if (PopDown(1)) { // popdown succesful
5919 XtSetArg(args[j], XtNleftBitmap, None); j++;
5920 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5921 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5922 } else // was not up
5927 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5929 if (!PopDown(4)) ICSInputBoxPopUp();
5933 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5939 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5945 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5951 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5957 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5963 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5969 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5975 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5981 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5983 UserAdjudicationEvent(+1);
5987 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5989 UserAdjudicationEvent(-1);
5993 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5995 UserAdjudicationEvent(0);
5999 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6001 if (shellUp[4] == True)
6006 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6007 { // [HGM] input: let up-arrow recall previous line from history
6014 if (!shellUp[4]) return;
6015 edit = boxOptions[0].handle;
6017 XtSetArg(args[j], XtNstring, &val); j++;
6018 XtGetValues(edit, args, j);
6019 val = PrevInHistory(val);
6020 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6021 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6023 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6024 XawTextReplace(edit, 0, 0, &t);
6025 XawTextSetInsertionPoint(edit, 9999);
6030 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6031 { // [HGM] input: let down-arrow recall next line from history
6036 if (!shellUp[4]) return;
6037 edit = boxOptions[0].handle;
6038 val = NextInHistory();
6039 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6040 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6042 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6043 XawTextReplace(edit, 0, 0, &t);
6044 XawTextSetInsertionPoint(edit, 9999);
6049 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6051 StopObservingEvent();
6055 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6057 StopExaminingEvent();
6061 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6068 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6075 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6081 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6083 if (!TempBackwardActive) {
6084 TempBackwardActive = True;
6090 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6092 /* Check to see if triggered by a key release event for a repeating key.
6093 * If so the next queued event will be a key press of the same key at the same time */
6094 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6096 XPeekEvent(xDisplay, &next);
6097 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6098 next.xkey.keycode == event->xkey.keycode)
6102 TempBackwardActive = False;
6106 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6112 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6118 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6124 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6130 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6132 TruncateGameEvent();
6136 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6142 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6148 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6150 flipView = !flipView;
6151 DrawPosition(True, NULL);
6155 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6159 PonderNextMoveEvent(!appData.ponderNextMove);
6160 #ifndef OPTIONSDIALOG
6161 if (appData.ponderNextMove) {
6162 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6164 XtSetArg(args[0], XtNleftBitmap, None);
6166 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6171 #ifndef OPTIONSDIALOG
6173 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6177 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6179 if (appData.alwaysPromoteToQueen) {
6180 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6182 XtSetArg(args[0], XtNleftBitmap, None);
6184 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6189 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6193 appData.animateDragging = !appData.animateDragging;
6195 if (appData.animateDragging) {
6196 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6199 XtSetArg(args[0], XtNleftBitmap, None);
6201 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6206 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6210 appData.animate = !appData.animate;
6212 if (appData.animate) {
6213 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6216 XtSetArg(args[0], XtNleftBitmap, None);
6218 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6223 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6227 appData.autoCallFlag = !appData.autoCallFlag;
6229 if (appData.autoCallFlag) {
6230 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6232 XtSetArg(args[0], XtNleftBitmap, None);
6234 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6239 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6243 appData.autoFlipView = !appData.autoFlipView;
6245 if (appData.autoFlipView) {
6246 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6248 XtSetArg(args[0], XtNleftBitmap, None);
6250 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6255 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6259 appData.blindfold = !appData.blindfold;
6261 if (appData.blindfold) {
6262 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6264 XtSetArg(args[0], XtNleftBitmap, None);
6266 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6269 DrawPosition(True, NULL);
6273 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6277 appData.testLegality = !appData.testLegality;
6279 if (appData.testLegality) {
6280 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6282 XtSetArg(args[0], XtNleftBitmap, None);
6284 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6290 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6294 if (appData.flashCount == 0) {
6295 appData.flashCount = 3;
6297 appData.flashCount = -appData.flashCount;
6300 if (appData.flashCount > 0) {
6301 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6303 XtSetArg(args[0], XtNleftBitmap, None);
6305 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6311 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6315 appData.highlightDragging = !appData.highlightDragging;
6317 if (appData.highlightDragging) {
6318 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6320 XtSetArg(args[0], XtNleftBitmap, None);
6322 XtSetValues(XtNameToWidget(menuBarWidget,
6323 "menuOptions.Highlight Dragging"), args, 1);
6328 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6332 appData.highlightLastMove = !appData.highlightLastMove;
6334 if (appData.highlightLastMove) {
6335 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6337 XtSetArg(args[0], XtNleftBitmap, None);
6339 XtSetValues(XtNameToWidget(menuBarWidget,
6340 "menuOptions.Highlight Last Move"), args, 1);
6344 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6348 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6350 if (appData.highlightMoveWithArrow) {
6351 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6353 XtSetArg(args[0], XtNleftBitmap, None);
6355 XtSetValues(XtNameToWidget(menuBarWidget,
6356 "menuOptions.Arrow"), args, 1);
6361 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6365 appData.icsAlarm = !appData.icsAlarm;
6367 if (appData.icsAlarm) {
6368 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6370 XtSetArg(args[0], XtNleftBitmap, None);
6372 XtSetValues(XtNameToWidget(menuBarWidget,
6373 "menuOptions.ICS Alarm"), args, 1);
6378 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6382 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6384 if (appData.ringBellAfterMoves) {
6385 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6387 XtSetArg(args[0], XtNleftBitmap, None);
6389 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6394 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6398 appData.oneClick = !appData.oneClick;
6400 if (appData.oneClick) {
6401 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6403 XtSetArg(args[0], XtNleftBitmap, None);
6405 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6410 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6414 PeriodicUpdatesEvent(!appData.periodicUpdates);
6416 if (appData.periodicUpdates) {
6417 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6419 XtSetArg(args[0], XtNleftBitmap, None);
6421 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6426 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6430 appData.popupExitMessage = !appData.popupExitMessage;
6432 if (appData.popupExitMessage) {
6433 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6435 XtSetArg(args[0], XtNleftBitmap, None);
6437 XtSetValues(XtNameToWidget(menuBarWidget,
6438 "menuOptions.Popup Exit Message"), args, 1);
6442 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6446 appData.popupMoveErrors = !appData.popupMoveErrors;
6448 if (appData.popupMoveErrors) {
6449 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6451 XtSetArg(args[0], XtNleftBitmap, None);
6453 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6459 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6463 appData.premove = !appData.premove;
6465 if (appData.premove) {
6466 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6468 XtSetArg(args[0], XtNleftBitmap, None);
6470 XtSetValues(XtNameToWidget(menuBarWidget,
6471 "menuOptions.Premove"), args, 1);
6476 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6480 appData.showCoords = !appData.showCoords;
6482 if (appData.showCoords) {
6483 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6485 XtSetArg(args[0], XtNleftBitmap, None);
6487 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6490 DrawPosition(True, NULL);
6494 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6496 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6497 ShowThinkingEvent();
6501 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6505 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6506 ShowThinkingEvent();
6508 if (appData.hideThinkingFromHuman) {
6509 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6511 XtSetArg(args[0], XtNleftBitmap, None);
6513 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6519 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6523 saveSettingsOnExit = !saveSettingsOnExit;
6525 if (saveSettingsOnExit) {
6526 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6528 XtSetArg(args[0], XtNleftBitmap, None);
6530 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6535 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6537 SaveSettings(settingsFileName);
6541 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6544 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6550 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6554 if (nprms && *nprms > 0)
6558 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6563 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6569 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6575 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6579 char *zippy = _(" (with Zippy code)");
6583 snprintf(buf, sizeof(buf),
6585 "Copyright 1991 Digital Equipment Corporation\n"
6586 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6587 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6588 "%s is free software and carries NO WARRANTY;"
6589 "see the file COPYING for more information."),
6590 programVersion, zippy, PACKAGE);
6591 ErrorPopUp(_("About XBoard"), buf, FALSE);
6595 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6597 appData.debugMode = !appData.debugMode;
6601 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6607 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6613 DisplayMessage (char *message, char *extMessage)
6615 /* display a message in the message widget */
6624 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6629 message = extMessage;
6633 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6635 /* need to test if messageWidget already exists, since this function
6636 can also be called during the startup, if for example a Xresource
6637 is not set up correctly */
6640 XtSetArg(arg, XtNlabel, message);
6641 XtSetValues(messageWidget, &arg, 1);
6648 DisplayTitle (char *text)
6652 char title[MSG_SIZ];
6655 if (text == NULL) text = "";
6657 if (appData.titleInWindow) {
6659 XtSetArg(args[i], XtNlabel, text); i++;
6660 XtSetValues(titleWidget, args, i);
6663 if (*text != NULLCHAR) {
6664 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6665 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6666 } else if (appData.icsActive) {
6667 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6668 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6669 } else if (appData.cmailGameName[0] != NULLCHAR) {
6670 snprintf(icon, sizeof(icon), "%s", "CMail");
6671 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6673 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6674 } else if (gameInfo.variant == VariantGothic) {
6675 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6676 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6679 } else if (gameInfo.variant == VariantFalcon) {
6680 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6681 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6683 } else if (appData.noChessProgram) {
6684 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6685 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6687 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6688 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6691 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6692 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6693 XtSetValues(shellWidget, args, i);
6694 XSync(xDisplay, False);
6699 DisplayError (String message, int error)
6704 if (appData.debugMode || appData.matchMode) {
6705 fprintf(stderr, "%s: %s\n", programName, message);
6708 if (appData.debugMode || appData.matchMode) {
6709 fprintf(stderr, "%s: %s: %s\n",
6710 programName, message, strerror(error));
6712 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6715 ErrorPopUp(_("Error"), message, FALSE);
6720 DisplayMoveError (String message)
6724 DrawPosition(FALSE, NULL);
6725 if (appData.debugMode || appData.matchMode) {
6726 fprintf(stderr, "%s: %s\n", programName, message);
6728 if (appData.popupMoveErrors) {
6729 ErrorPopUp(_("Error"), message, FALSE);
6731 DisplayMessage(message, "");
6737 DisplayFatalError (String message, int error, int status)
6741 errorExitStatus = status;
6743 fprintf(stderr, "%s: %s\n", programName, message);
6745 fprintf(stderr, "%s: %s: %s\n",
6746 programName, message, strerror(error));
6747 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6750 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6751 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6758 DisplayInformation (String message)
6761 ErrorPopUp(_("Information"), message, TRUE);
6765 DisplayNote (String message)
6768 ErrorPopUp(_("Note"), message, FALSE);
6772 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6778 DisplayIcsInteractionTitle (String message)
6780 if (oldICSInteractionTitle == NULL) {
6781 /* Magic to find the old window title, adapted from vim */
6782 char *wina = getenv("WINDOWID");
6784 Window win = (Window) atoi(wina);
6785 Window root, parent, *children;
6786 unsigned int nchildren;
6787 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6789 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6790 if (!XQueryTree(xDisplay, win, &root, &parent,
6791 &children, &nchildren)) break;
6792 if (children) XFree((void *)children);
6793 if (parent == root || parent == 0) break;
6796 XSetErrorHandler(oldHandler);
6798 if (oldICSInteractionTitle == NULL) {
6799 oldICSInteractionTitle = "xterm";
6802 printf("\033]0;%s\007", message);
6806 char pendingReplyPrefix[MSG_SIZ];
6807 ProcRef pendingReplyPR;
6810 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6813 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6817 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6821 AskQuestionPopDown ()
6823 if (!askQuestionUp) return;
6824 XtPopdown(askQuestionShell);
6825 XtDestroyWidget(askQuestionShell);
6826 askQuestionUp = False;
6830 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6836 reply = XawDialogGetValueString(w = XtParent(w));
6837 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6838 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6839 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6840 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6841 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6842 AskQuestionPopDown();
6844 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6848 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6853 XtSetArg(args[0], XtNlabel, &name);
6854 XtGetValues(w, args, 1);
6856 if (strcmp(name, _("cancel")) == 0) {
6857 AskQuestionPopDown();
6859 AskQuestionReplyAction(w, NULL, NULL, NULL);
6864 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6867 Widget popup, layout, dialog, edit;
6873 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6874 pendingReplyPR = pr;
6877 XtSetArg(args[i], XtNresizable, True); i++;
6878 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6879 askQuestionShell = popup =
6880 XtCreatePopupShell(title, transientShellWidgetClass,
6881 shellWidget, args, i);
6884 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6885 layoutArgs, XtNumber(layoutArgs));
6888 XtSetArg(args[i], XtNlabel, question); i++;
6889 XtSetArg(args[i], XtNvalue, ""); i++;
6890 XtSetArg(args[i], XtNborderWidth, 0); i++;
6891 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6894 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6895 (XtPointer) dialog);
6896 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6897 (XtPointer) dialog);
6899 XtRealizeWidget(popup);
6900 CatchDeleteWindow(popup, "AskQuestionPopDown");
6902 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6903 &x, &y, &win_x, &win_y, &mask);
6905 XtSetArg(args[0], XtNx, x - 10);
6906 XtSetArg(args[1], XtNy, y - 30);
6907 XtSetValues(popup, args, 2);
6909 XtPopup(popup, XtGrabExclusive);
6910 askQuestionUp = True;
6912 edit = XtNameToWidget(dialog, "*value");
6913 XtSetKeyboardFocus(popup, edit);
6918 PlaySound (char *name)
6920 if (*name == NULLCHAR) {
6922 } else if (strcmp(name, "$") == 0) {
6923 putc(BELLCHAR, stderr);
6926 char *prefix = "", *sep = "";
6927 if(appData.soundProgram[0] == NULLCHAR) return;
6928 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6929 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6937 PlaySound(appData.soundMove);
6943 PlaySound(appData.soundIcsWin);
6949 PlaySound(appData.soundIcsLoss);
6955 PlaySound(appData.soundIcsDraw);
6959 PlayIcsUnfinishedSound ()
6961 PlaySound(appData.soundIcsUnfinished);
6967 PlaySound(appData.soundIcsAlarm);
6973 PlaySound(appData.soundTell);
6979 system("stty echo");
6986 system("stty -echo");
6991 RunCommand (char *buf)
6997 Colorize (ColorClass cc, int continuation)
7000 int count, outCount, error;
7002 if (textColors[(int)cc].bg > 0) {
7003 if (textColors[(int)cc].fg > 0) {
7004 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7005 textColors[(int)cc].fg, textColors[(int)cc].bg);
7007 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7008 textColors[(int)cc].bg);
7011 if (textColors[(int)cc].fg > 0) {
7012 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7013 textColors[(int)cc].fg);
7015 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7018 count = strlen(buf);
7019 outCount = OutputToProcess(NoProc, buf, count, &error);
7020 if (outCount < count) {
7021 DisplayFatalError(_("Error writing to display"), error, 1);
7024 if (continuation) return;
7027 PlaySound(appData.soundShout);
7030 PlaySound(appData.soundSShout);
7033 PlaySound(appData.soundChannel1);
7036 PlaySound(appData.soundChannel);
7039 PlaySound(appData.soundKibitz);
7042 PlaySound(appData.soundTell);
7044 case ColorChallenge:
7045 PlaySound(appData.soundChallenge);
7048 PlaySound(appData.soundRequest);
7051 PlaySound(appData.soundSeek);
7063 return getpwuid(getuid())->pw_name;
7067 ExpandPathName (char *path)
7069 static char static_buf[4*MSG_SIZ];
7070 char *d, *s, buf[4*MSG_SIZ];
7076 while (*s && isspace(*s))
7085 if (*(s+1) == '/') {
7086 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7090 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7091 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7092 pwd = getpwnam(buf);
7095 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7099 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7100 strcat(d, strchr(s+1, '/'));
7104 safeStrCpy(d, s, 4*MSG_SIZ );
7112 static char host_name[MSG_SIZ];
7114 #if HAVE_GETHOSTNAME
7115 gethostname(host_name, MSG_SIZ);
7117 #else /* not HAVE_GETHOSTNAME */
7118 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7119 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7121 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7123 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7124 #endif /* not HAVE_GETHOSTNAME */
7127 XtIntervalId delayedEventTimerXID = 0;
7128 DelayedEventCallback delayedEventCallback = 0;
7133 delayedEventTimerXID = 0;
7134 delayedEventCallback();
7138 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7140 if(delayedEventTimerXID && delayedEventCallback == cb)
7141 // [HGM] alive: replace, rather than add or flush identical event
7142 XtRemoveTimeOut(delayedEventTimerXID);
7143 delayedEventCallback = cb;
7144 delayedEventTimerXID =
7145 XtAppAddTimeOut(appContext, millisec,
7146 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7149 DelayedEventCallback
7152 if (delayedEventTimerXID) {
7153 return delayedEventCallback;
7160 CancelDelayedEvent ()
7162 if (delayedEventTimerXID) {
7163 XtRemoveTimeOut(delayedEventTimerXID);
7164 delayedEventTimerXID = 0;
7168 XtIntervalId loadGameTimerXID = 0;
7171 LoadGameTimerRunning ()
7173 return loadGameTimerXID != 0;
7177 StopLoadGameTimer ()
7179 if (loadGameTimerXID != 0) {
7180 XtRemoveTimeOut(loadGameTimerXID);
7181 loadGameTimerXID = 0;
7189 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7191 loadGameTimerXID = 0;
7196 StartLoadGameTimer (long millisec)
7199 XtAppAddTimeOut(appContext, millisec,
7200 (XtTimerCallbackProc) LoadGameTimerCallback,
7204 XtIntervalId analysisClockXID = 0;
7207 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7209 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7210 || appData.icsEngineAnalyze) { // [DM]
7211 AnalysisPeriodicEvent(0);
7212 StartAnalysisClock();
7217 StartAnalysisClock ()
7220 XtAppAddTimeOut(appContext, 2000,
7221 (XtTimerCallbackProc) AnalysisClockCallback,
7225 XtIntervalId clockTimerXID = 0;
7228 ClockTimerRunning ()
7230 return clockTimerXID != 0;
7236 if (clockTimerXID != 0) {
7237 XtRemoveTimeOut(clockTimerXID);
7246 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7253 StartClockTimer (long millisec)
7256 XtAppAddTimeOut(appContext, millisec,
7257 (XtTimerCallbackProc) ClockTimerCallback,
7262 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7267 /* check for low time warning */
7268 Pixel foregroundOrWarningColor = timerForegroundPixel;
7271 appData.lowTimeWarning &&
7272 (timer / 1000) < appData.icsAlarmTime)
7273 foregroundOrWarningColor = lowTimeWarningColor;
7275 if (appData.clockMode) {
7276 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7277 XtSetArg(args[0], XtNlabel, buf);
7279 snprintf(buf, MSG_SIZ, "%s ", color);
7280 XtSetArg(args[0], XtNlabel, buf);
7285 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7286 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7288 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7289 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7292 XtSetValues(w, args, 3);
7296 DisplayWhiteClock (long timeRemaining, int highlight)
7300 if(appData.noGUI) return;
7301 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7302 if (highlight && iconPixmap == bIconPixmap) {
7303 iconPixmap = wIconPixmap;
7304 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7305 XtSetValues(shellWidget, args, 1);
7310 DisplayBlackClock (long timeRemaining, int highlight)
7314 if(appData.noGUI) return;
7315 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7316 if (highlight && iconPixmap == wIconPixmap) {
7317 iconPixmap = bIconPixmap;
7318 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7319 XtSetValues(shellWidget, args, 1);
7338 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7342 int to_prog[2], from_prog[2];
7346 if (appData.debugMode) {
7347 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7350 /* We do NOT feed the cmdLine to the shell; we just
7351 parse it into blank-separated arguments in the
7352 most simple-minded way possible.
7355 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7358 while(*p == ' ') p++;
7360 if(*p == '"' || *p == '\'')
7361 p = strchr(++argv[i-1], *p);
7362 else p = strchr(p, ' ');
7363 if (p == NULL) break;
7368 SetUpChildIO(to_prog, from_prog);
7370 if ((pid = fork()) == 0) {
7372 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7373 close(to_prog[1]); // first close the unused pipe ends
7374 close(from_prog[0]);
7375 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7376 dup2(from_prog[1], 1);
7377 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7378 close(from_prog[1]); // and closing again loses one of the pipes!
7379 if(fileno(stderr) >= 2) // better safe than sorry...
7380 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7382 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7387 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7389 execvp(argv[0], argv);
7391 /* If we get here, exec failed */
7396 /* Parent process */
7398 close(from_prog[1]);
7400 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7403 cp->fdFrom = from_prog[0];
7404 cp->fdTo = to_prog[1];
7409 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7411 AlarmCallBack (int n)
7417 DestroyChildProcess (ProcRef pr, int signalType)
7419 ChildProc *cp = (ChildProc *) pr;
7421 if (cp->kind != CPReal) return;
7423 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7424 signal(SIGALRM, AlarmCallBack);
7426 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7427 kill(cp->pid, SIGKILL); // kill it forcefully
7428 wait((int *) 0); // and wait again
7432 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7434 /* Process is exiting either because of the kill or because of
7435 a quit command sent by the backend; either way, wait for it to die.
7444 InterruptChildProcess (ProcRef pr)
7446 ChildProc *cp = (ChildProc *) pr;
7448 if (cp->kind != CPReal) return;
7449 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7453 OpenTelnet (char *host, char *port, ProcRef *pr)
7455 char cmdLine[MSG_SIZ];
7457 if (port[0] == NULLCHAR) {
7458 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7460 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7462 return StartChildProcess(cmdLine, "", pr);
7466 OpenTCP (char *host, char *port, ProcRef *pr)
7469 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7470 #else /* !OMIT_SOCKETS */
7471 struct addrinfo hints;
7472 struct addrinfo *ais, *ai;
7477 memset(&hints, 0, sizeof(hints));
7478 hints.ai_family = AF_UNSPEC;
7479 hints.ai_socktype = SOCK_STREAM;
7481 error = getaddrinfo(host, port, &hints, &ais);
7483 /* a getaddrinfo error is not an errno, so can't return it */
7484 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7485 host, port, gai_strerror(error));
7489 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7490 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7494 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7507 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7513 #endif /* !OMIT_SOCKETS */
7519 OpenCommPort (char *name, ProcRef *pr)
7524 fd = open(name, 2, 0);
7525 if (fd < 0) return errno;
7527 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7538 OpenLoopback (ProcRef *pr)
7543 SetUpChildIO(to, from);
7545 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7548 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7556 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7558 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7562 #define INPUT_SOURCE_BUF_SIZE 8192
7571 char buf[INPUT_SOURCE_BUF_SIZE];
7576 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7578 InputSource *is = (InputSource *) closure;
7583 if (is->lineByLine) {
7584 count = read(is->fd, is->unused,
7585 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7587 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7590 is->unused += count;
7592 while (p < is->unused) {
7593 q = memchr(p, '\n', is->unused - p);
7594 if (q == NULL) break;
7596 (is->func)(is, is->closure, p, q - p, 0);
7600 while (p < is->unused) {
7605 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7610 (is->func)(is, is->closure, is->buf, count, error);
7615 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7618 ChildProc *cp = (ChildProc *) pr;
7620 is = (InputSource *) calloc(1, sizeof(InputSource));
7621 is->lineByLine = lineByLine;
7625 is->fd = fileno(stdin);
7627 is->kind = cp->kind;
7628 is->fd = cp->fdFrom;
7631 is->unused = is->buf;
7634 is->xid = XtAppAddInput(appContext, is->fd,
7635 (XtPointer) (XtInputReadMask),
7636 (XtInputCallbackProc) DoInputCallback,
7638 is->closure = closure;
7639 return (InputSourceRef) is;
7643 RemoveInputSource (InputSourceRef isr)
7645 InputSource *is = (InputSource *) isr;
7647 if (is->xid == 0) return;
7648 XtRemoveInput(is->xid);
7653 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7655 static int line = 0;
7656 ChildProc *cp = (ChildProc *) pr;
7661 if (appData.noJoin || !appData.useInternalWrap)
7662 outCount = fwrite(message, 1, count, stdout);
7665 int width = get_term_width();
7666 int len = wrap(NULL, message, count, width, &line);
7667 char *msg = malloc(len);
7671 outCount = fwrite(message, 1, count, stdout);
7674 dbgchk = wrap(msg, message, count, width, &line);
7675 if (dbgchk != len && appData.debugMode)
7676 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7677 outCount = fwrite(msg, 1, dbgchk, stdout);
7683 outCount = write(cp->fdTo, message, count);
7693 /* Output message to process, with "ms" milliseconds of delay
7694 between each character. This is needed when sending the logon
7695 script to ICC, which for some reason doesn't like the
7696 instantaneous send. */
7698 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7700 ChildProc *cp = (ChildProc *) pr;
7705 r = write(cp->fdTo, message++, 1);
7718 /**** Animation code by Hugh Fisher, DCS, ANU.
7720 Known problem: if a window overlapping the board is
7721 moved away while a piece is being animated underneath,
7722 the newly exposed area won't be updated properly.
7723 I can live with this.
7725 Known problem: if you look carefully at the animation
7726 of pieces in mono mode, they are being drawn as solid
7727 shapes without interior detail while moving. Fixing
7728 this would be a major complication for minimal return.
7731 /* Masks for XPM pieces. Black and white pieces can have
7732 different shapes, but in the interest of retaining my
7733 sanity pieces must have the same outline on both light
7734 and dark squares, and all pieces must use the same
7735 background square colors/images. */
7737 static int xpmDone = 0;
7740 CreateAnimMasks (int pieceDepth)
7746 unsigned long plane;
7749 /* Need a bitmap just to get a GC with right depth */
7750 buf = XCreatePixmap(xDisplay, xBoardWindow,
7752 values.foreground = 1;
7753 values.background = 0;
7754 /* Don't use XtGetGC, not read only */
7755 maskGC = XCreateGC(xDisplay, buf,
7756 GCForeground | GCBackground, &values);
7757 XFreePixmap(xDisplay, buf);
7759 buf = XCreatePixmap(xDisplay, xBoardWindow,
7760 squareSize, squareSize, pieceDepth);
7761 values.foreground = XBlackPixel(xDisplay, xScreen);
7762 values.background = XWhitePixel(xDisplay, xScreen);
7763 bufGC = XCreateGC(xDisplay, buf,
7764 GCForeground | GCBackground, &values);
7766 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7767 /* Begin with empty mask */
7768 if(!xpmDone) // [HGM] pieces: keep using existing
7769 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7770 squareSize, squareSize, 1);
7771 XSetFunction(xDisplay, maskGC, GXclear);
7772 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7773 0, 0, squareSize, squareSize);
7775 /* Take a copy of the piece */
7780 XSetFunction(xDisplay, bufGC, GXcopy);
7781 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7783 0, 0, squareSize, squareSize, 0, 0);
7785 /* XOR the background (light) over the piece */
7786 XSetFunction(xDisplay, bufGC, GXxor);
7788 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7789 0, 0, squareSize, squareSize, 0, 0);
7791 XSetForeground(xDisplay, bufGC, lightSquareColor);
7792 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7795 /* We now have an inverted piece image with the background
7796 erased. Construct mask by just selecting all the non-zero
7797 pixels - no need to reconstruct the original image. */
7798 XSetFunction(xDisplay, maskGC, GXor);
7800 /* Might be quicker to download an XImage and create bitmap
7801 data from it rather than this N copies per piece, but it
7802 only takes a fraction of a second and there is a much
7803 longer delay for loading the pieces. */
7804 for (n = 0; n < pieceDepth; n ++) {
7805 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7806 0, 0, squareSize, squareSize,
7812 XFreePixmap(xDisplay, buf);
7813 XFreeGC(xDisplay, bufGC);
7814 XFreeGC(xDisplay, maskGC);
7818 InitAnimState (AnimState *anim, XWindowAttributes *info)
7823 /* Each buffer is square size, same depth as window */
7824 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7825 squareSize, squareSize, info->depth);
7826 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7827 squareSize, squareSize, info->depth);
7829 /* Create a plain GC for blitting */
7830 mask = GCForeground | GCBackground | GCFunction |
7831 GCPlaneMask | GCGraphicsExposures;
7832 values.foreground = XBlackPixel(xDisplay, xScreen);
7833 values.background = XWhitePixel(xDisplay, xScreen);
7834 values.function = GXcopy;
7835 values.plane_mask = AllPlanes;
7836 values.graphics_exposures = False;
7837 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7839 /* Piece will be copied from an existing context at
7840 the start of each new animation/drag. */
7841 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7843 /* Outline will be a read-only copy of an existing */
7844 anim->outlineGC = None;
7850 XWindowAttributes info;
7852 if (xpmDone && gameInfo.variant == oldVariant) return;
7853 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7854 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7856 InitAnimState(&game, &info);
7857 InitAnimState(&player, &info);
7859 /* For XPM pieces, we need bitmaps to use as masks. */
7861 CreateAnimMasks(info.depth), xpmDone = 1;
7866 static Boolean frameWaiting;
7869 FrameAlarm (int sig)
7871 frameWaiting = False;
7872 /* In case System-V style signals. Needed?? */
7873 signal(SIGALRM, FrameAlarm);
7877 FrameDelay (int time)
7879 struct itimerval delay;
7881 XSync(xDisplay, False);
7884 frameWaiting = True;
7885 signal(SIGALRM, FrameAlarm);
7886 delay.it_interval.tv_sec =
7887 delay.it_value.tv_sec = time / 1000;
7888 delay.it_interval.tv_usec =
7889 delay.it_value.tv_usec = (time % 1000) * 1000;
7890 setitimer(ITIMER_REAL, &delay, NULL);
7891 while (frameWaiting) pause();
7892 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7893 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7894 setitimer(ITIMER_REAL, &delay, NULL);
7901 FrameDelay (int time)
7903 XSync(xDisplay, False);
7905 usleep(time * 1000);
7916 /* Convert board position to corner of screen rect and color */
7919 ScreenSquare (int column, int row, XPoint *pt, int *color)
7922 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7923 pt->y = lineGap + row * (squareSize + lineGap);
7925 pt->x = lineGap + column * (squareSize + lineGap);
7926 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7928 *color = SquareColor(row, column);
7931 /* Convert window coords to square */
7934 BoardSquare (int x, int y, int *column, int *row)
7936 *column = EventToSquare(x, BOARD_WIDTH);
7937 if (flipView && *column >= 0)
7938 *column = BOARD_WIDTH - 1 - *column;
7939 *row = EventToSquare(y, BOARD_HEIGHT);
7940 if (!flipView && *row >= 0)
7941 *row = BOARD_HEIGHT - 1 - *row;
7946 #undef Max /* just in case */
7948 #define Max(a, b) ((a) > (b) ? (a) : (b))
7949 #define Min(a, b) ((a) < (b) ? (a) : (b))
7952 SetRect (XRectangle *rect, int x, int y, int width, int height)
7956 rect->width = width;
7957 rect->height = height;
7960 /* Test if two frames overlap. If they do, return
7961 intersection rect within old and location of
7962 that rect within new. */
7965 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
7967 if (old->x > new->x + size || new->x > old->x + size ||
7968 old->y > new->y + size || new->y > old->y + size) {
7971 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7972 size - abs(old->x - new->x), size - abs(old->y - new->y));
7973 pt->x = Max(old->x - new->x, 0);
7974 pt->y = Max(old->y - new->y, 0);
7979 /* For two overlapping frames, return the rect(s)
7980 in the old that do not intersect with the new. */
7983 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
7987 /* If old = new (shouldn't happen) then nothing to draw */
7988 if (old->x == new->x && old->y == new->y) {
7992 /* Work out what bits overlap. Since we know the rects
7993 are the same size we don't need a full intersect calc. */
7995 /* Top or bottom edge? */
7996 if (new->y > old->y) {
7997 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
7999 } else if (old->y > new->y) {
8000 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8001 size, old->y - new->y);
8004 /* Left or right edge - don't overlap any update calculated above. */
8005 if (new->x > old->x) {
8006 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8007 new->x - old->x, size - abs(new->y - old->y));
8009 } else if (old->x > new->x) {
8010 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8011 old->x - new->x, size - abs(new->y - old->y));
8018 /* Generate a series of frame coords from start->mid->finish.
8019 The movement rate doubles until the half way point is
8020 reached, then halves back down to the final destination,
8021 which gives a nice slow in/out effect. The algorithmn
8022 may seem to generate too many intermediates for short
8023 moves, but remember that the purpose is to attract the
8024 viewers attention to the piece about to be moved and
8025 then to where it ends up. Too few frames would be less
8029 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8031 int fraction, n, count;
8035 /* Slow in, stepping 1/16th, then 1/8th, ... */
8037 for (n = 0; n < factor; n++)
8039 for (n = 0; n < factor; n++) {
8040 frames[count].x = start->x + (mid->x - start->x) / fraction;
8041 frames[count].y = start->y + (mid->y - start->y) / fraction;
8043 fraction = fraction / 2;
8047 frames[count] = *mid;
8050 /* Slow out, stepping 1/2, then 1/4, ... */
8052 for (n = 0; n < factor; n++) {
8053 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8054 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8056 fraction = fraction * 2;
8061 /* Draw a piece on the screen without disturbing what's there */
8064 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8068 /* Bitmap for piece being moved. */
8069 if (appData.monoMode) {
8070 *mask = *pieceToSolid(piece);
8071 } else if (useImages) {
8073 *mask = xpmMask[piece];
8075 *mask = ximMaskPm[piece];
8078 *mask = *pieceToSolid(piece);
8081 /* GC for piece being moved. Square color doesn't matter, but
8082 since it gets modified we make a copy of the original. */
8084 if (appData.monoMode)
8089 if (appData.monoMode)
8094 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8096 /* Outline only used in mono mode and is not modified */
8098 *outline = bwPieceGC;
8100 *outline = wbPieceGC;
8104 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8109 /* Draw solid rectangle which will be clipped to shape of piece */
8110 XFillRectangle(xDisplay, dest, clip,
8111 0, 0, squareSize, squareSize);
8112 if (appData.monoMode)
8113 /* Also draw outline in contrasting color for black
8114 on black / white on white cases */
8115 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8116 0, 0, squareSize, squareSize, 0, 0, 1);
8118 /* Copy the piece */
8123 if(appData.upsideDown && flipView) kind ^= 2;
8124 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8126 0, 0, squareSize, squareSize,
8131 /* Animate the movement of a single piece */
8134 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8138 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8139 /* The old buffer is initialised with the start square (empty) */
8140 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8141 anim->prevFrame = *start;
8143 /* The piece will be drawn using its own bitmap as a matte */
8144 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8145 XSetClipMask(xDisplay, anim->pieceGC, mask);
8149 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8151 XRectangle updates[4];
8156 /* Save what we are about to draw into the new buffer */
8157 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8158 frame->x, frame->y, squareSize, squareSize,
8161 /* Erase bits of the previous frame */
8162 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8163 /* Where the new frame overlapped the previous,
8164 the contents in newBuf are wrong. */
8165 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8166 overlap.x, overlap.y,
8167 overlap.width, overlap.height,
8169 /* Repaint the areas in the old that don't overlap new */
8170 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8171 for (i = 0; i < count; i++)
8172 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8173 updates[i].x - anim->prevFrame.x,
8174 updates[i].y - anim->prevFrame.y,
8175 updates[i].width, updates[i].height,
8176 updates[i].x, updates[i].y);
8178 /* Easy when no overlap */
8179 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8180 0, 0, squareSize, squareSize,
8181 anim->prevFrame.x, anim->prevFrame.y);
8184 /* Save this frame for next time round */
8185 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8186 0, 0, squareSize, squareSize,
8188 anim->prevFrame = *frame;
8190 /* Draw piece over original screen contents, not current,
8191 and copy entire rect. Wipes out overlapping piece images. */
8192 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8193 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8194 0, 0, squareSize, squareSize,
8195 frame->x, frame->y);
8199 EndAnimation (AnimState *anim, XPoint *finish)
8201 XRectangle updates[4];
8206 /* The main code will redraw the final square, so we
8207 only need to erase the bits that don't overlap. */
8208 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8209 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8210 for (i = 0; i < count; i++)
8211 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8212 updates[i].x - anim->prevFrame.x,
8213 updates[i].y - anim->prevFrame.y,
8214 updates[i].width, updates[i].height,
8215 updates[i].x, updates[i].y);
8217 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8218 0, 0, squareSize, squareSize,
8219 anim->prevFrame.x, anim->prevFrame.y);
8224 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8228 BeginAnimation(anim, piece, startColor, start);
8229 for (n = 0; n < nFrames; n++) {
8230 AnimationFrame(anim, &(frames[n]), piece);
8231 FrameDelay(appData.animSpeed);
8233 EndAnimation(anim, finish);
8237 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8240 ChessSquare piece = board[fromY][toY];
8241 board[fromY][toY] = EmptySquare;
8242 DrawPosition(FALSE, board);
8244 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8245 y = lineGap + toY * (squareSize + lineGap);
8247 x = lineGap + toX * (squareSize + lineGap);
8248 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8250 for(i=1; i<4*kFactor; i++) {
8251 int r = squareSize * 9 * i/(20*kFactor - 5);
8252 XFillArc(xDisplay, xBoardWindow, highlineGC,
8253 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8254 FrameDelay(appData.animSpeed);
8256 board[fromY][toY] = piece;
8259 /* Main control logic for deciding what to animate and how */
8262 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8266 XPoint start, finish, mid;
8267 XPoint frames[kFactor * 2 + 1];
8268 int nFrames, startColor, endColor;
8270 /* Are we animating? */
8271 if (!appData.animate || appData.blindfold)
8274 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8275 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8276 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8278 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8279 piece = board[fromY][fromX];
8280 if (piece >= EmptySquare) return;
8285 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8288 ScreenSquare(fromX, fromY, &start, &startColor);
8289 ScreenSquare(toX, toY, &finish, &endColor);
8292 /* Knight: make straight movement then diagonal */
8293 if (abs(toY - fromY) < abs(toX - fromX)) {
8294 mid.x = start.x + (finish.x - start.x) / 2;
8298 mid.y = start.y + (finish.y - start.y) / 2;
8301 mid.x = start.x + (finish.x - start.x) / 2;
8302 mid.y = start.y + (finish.y - start.y) / 2;
8305 /* Don't use as many frames for very short moves */
8306 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8307 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8309 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8310 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8311 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8313 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8314 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8317 /* Be sure end square is redrawn */
8318 damage[0][toY][toX] = True;
8322 DragPieceBegin (int x, int y, Boolean instantly)
8324 int boardX, boardY, color;
8327 /* Are we animating? */
8328 if (!appData.animateDragging || appData.blindfold)
8331 /* Figure out which square we start in and the
8332 mouse position relative to top left corner. */
8333 BoardSquare(x, y, &boardX, &boardY);
8334 player.startBoardX = boardX;
8335 player.startBoardY = boardY;
8336 ScreenSquare(boardX, boardY, &corner, &color);
8337 player.startSquare = corner;
8338 player.startColor = color;
8339 /* As soon as we start dragging, the piece will jump slightly to
8340 be centered over the mouse pointer. */
8341 player.mouseDelta.x = squareSize/2;
8342 player.mouseDelta.y = squareSize/2;
8343 /* Initialise animation */
8344 player.dragPiece = PieceForSquare(boardX, boardY);
8346 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8347 player.dragActive = True;
8348 BeginAnimation(&player, player.dragPiece, color, &corner);
8349 /* Mark this square as needing to be redrawn. Note that
8350 we don't remove the piece though, since logically (ie
8351 as seen by opponent) the move hasn't been made yet. */
8352 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8353 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8354 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8355 corner.x, corner.y, squareSize, squareSize,
8356 0, 0); // [HGM] zh: unstack in stead of grab
8357 if(gatingPiece != EmptySquare) {
8358 /* Kludge alert: When gating we want the introduced
8359 piece to appear on the from square. To generate an
8360 image of it, we draw it on the board, copy the image,
8361 and draw the original piece again. */
8362 ChessSquare piece = boards[currentMove][boardY][boardX];
8363 DrawSquare(boardY, boardX, gatingPiece, 0);
8364 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8365 corner.x, corner.y, squareSize, squareSize, 0, 0);
8366 DrawSquare(boardY, boardX, piece, 0);
8368 damage[0][boardY][boardX] = True;
8370 player.dragActive = False;
8375 ChangeDragPiece (ChessSquare piece)
8378 player.dragPiece = piece;
8379 /* The piece will be drawn using its own bitmap as a matte */
8380 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8381 XSetClipMask(xDisplay, player.pieceGC, mask);
8385 DragPieceMove (int x, int y)
8389 /* Are we animating? */
8390 if (!appData.animateDragging || appData.blindfold)
8394 if (! player.dragActive)
8396 /* Move piece, maintaining same relative position
8397 of mouse within square */
8398 corner.x = x - player.mouseDelta.x;
8399 corner.y = y - player.mouseDelta.y;
8400 AnimationFrame(&player, &corner, player.dragPiece);
8402 if (appData.highlightDragging) {
8404 BoardSquare(x, y, &boardX, &boardY);
8405 SetHighlights(fromX, fromY, boardX, boardY);
8411 DragPieceEnd (int x, int y)
8413 int boardX, boardY, color;
8416 /* Are we animating? */
8417 if (!appData.animateDragging || appData.blindfold)
8421 if (! player.dragActive)
8423 /* Last frame in sequence is square piece is
8424 placed on, which may not match mouse exactly. */
8425 BoardSquare(x, y, &boardX, &boardY);
8426 ScreenSquare(boardX, boardY, &corner, &color);
8427 EndAnimation(&player, &corner);
8429 /* Be sure end square is redrawn */
8430 damage[0][boardY][boardX] = True;
8432 /* This prevents weird things happening with fast successive
8433 clicks which on my Sun at least can cause motion events
8434 without corresponding press/release. */
8435 player.dragActive = False;
8438 /* Handle expose event while piece being dragged */
8443 if (!player.dragActive || appData.blindfold)
8446 /* What we're doing: logically, the move hasn't been made yet,
8447 so the piece is still in it's original square. But visually
8448 it's being dragged around the board. So we erase the square
8449 that the piece is on and draw it at the last known drag point. */
8450 BlankSquare(player.startSquare.x, player.startSquare.y,
8451 player.startColor, EmptySquare, xBoardWindow, 1);
8452 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8453 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8456 #include <sys/ioctl.h>
8460 int fd, default_width;
8463 default_width = 79; // this is FICS default anyway...
8465 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8467 if (!ioctl(fd, TIOCGSIZE, &win))
8468 default_width = win.ts_cols;
8469 #elif defined(TIOCGWINSZ)
8471 if (!ioctl(fd, TIOCGWINSZ, &win))
8472 default_width = win.ws_col;
8474 return default_width;
8480 static int old_width = 0;
8481 int new_width = get_term_width();
8483 if (old_width != new_width)
8484 ics_printf("set width %d\n", new_width);
8485 old_width = new_width;
8489 NotifyFrontendLogin ()
8494 /* [AS] Arrow highlighting support */
8496 static double A_WIDTH = 5; /* Width of arrow body */
8498 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8499 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8510 return (int) (x + 0.5);
8514 SquareToPos (int rank, int file, int *x, int *y)
8517 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8518 *y = lineGap + rank * (squareSize + lineGap);
8520 *x = lineGap + file * (squareSize + lineGap);
8521 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8525 /* Draw an arrow between two points using current settings */
8527 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8530 double dx, dy, j, k, x, y;
8533 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8535 arrow[0].x = s_x + A_WIDTH + 0.5;
8538 arrow[1].x = s_x + A_WIDTH + 0.5;
8539 arrow[1].y = d_y - h;
8541 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8542 arrow[2].y = d_y - h;
8547 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8548 arrow[5].y = d_y - h;
8550 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8551 arrow[4].y = d_y - h;
8553 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8556 else if( d_y == s_y ) {
8557 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8560 arrow[0].y = s_y + A_WIDTH + 0.5;
8562 arrow[1].x = d_x - w;
8563 arrow[1].y = s_y + A_WIDTH + 0.5;
8565 arrow[2].x = d_x - w;
8566 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8571 arrow[5].x = d_x - w;
8572 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8574 arrow[4].x = d_x - w;
8575 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8578 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8581 /* [AS] Needed a lot of paper for this! :-) */
8582 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8583 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8585 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8587 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8592 arrow[0].x = Round(x - j);
8593 arrow[0].y = Round(y + j*dx);
8595 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8596 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8599 x = (double) d_x - k;
8600 y = (double) d_y - k*dy;
8603 x = (double) d_x + k;
8604 y = (double) d_y + k*dy;
8607 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8609 arrow[6].x = Round(x - j);
8610 arrow[6].y = Round(y + j*dx);
8612 arrow[2].x = Round(arrow[6].x + 2*j);
8613 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8615 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8616 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8621 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8622 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8625 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8626 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8627 // Polygon( hdc, arrow, 7 );
8631 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8634 hor = 64*s_col + 32; vert = 64*s_row + 32;
8635 for(i=0; i<= 64; i++) {
8636 damage[0][vert+6>>6][hor+6>>6] = True;
8637 damage[0][vert-6>>6][hor+6>>6] = True;
8638 damage[0][vert+6>>6][hor-6>>6] = True;
8639 damage[0][vert-6>>6][hor-6>>6] = True;
8640 hor += d_col - s_col; vert += d_row - s_row;
8644 /* [AS] Draw an arrow between two squares */
8646 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8648 int s_x, s_y, d_x, d_y;
8650 if( s_col == d_col && s_row == d_row ) {
8654 /* Get source and destination points */
8655 SquareToPos( s_row, s_col, &s_x, &s_y);
8656 SquareToPos( d_row, d_col, &d_x, &d_y);
8659 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8661 else if( d_y < s_y ) {
8662 d_y += squareSize / 2 + squareSize / 4;
8665 d_y += squareSize / 2;
8669 d_x += squareSize / 2 - squareSize / 4;
8671 else if( d_x < s_x ) {
8672 d_x += squareSize / 2 + squareSize / 4;
8675 d_x += squareSize / 2;
8678 s_x += squareSize / 2;
8679 s_y += squareSize / 2;
8682 A_WIDTH = squareSize / 14.; //[HGM] make float
8684 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8685 ArrowDamage(s_col, s_row, d_col, d_row);
8689 IsDrawArrowEnabled ()
8691 return appData.highlightMoveWithArrow && squareSize >= 32;
8695 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8697 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8698 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8702 UpdateLogos (int displ)
8704 return; // no logos in XBoard yet