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