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 XtRegisterGrabAction(PieceMenuPopup, True,
4088 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4089 GrabModeAsync, GrabModeAsync);
4091 XtSetArg(args[0], XtNlabel, _("Drop"));
4092 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4093 boardWidget, args, 1);
4094 for (i = 0; i < DROP_MENU_SIZE; i++) {
4095 String item = dropMenuStrings[i];
4097 if (strcmp(item, "----") == 0) {
4098 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4101 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4102 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4104 selection = dropMenuTranslation[i];
4105 XtAddCallback(entry, XtNcallback,
4106 (XtCallbackProc) DropMenuSelect,
4107 (caddr_t) selection);
4121 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4122 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4123 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4124 dmEnables[i].piece);
4125 XtSetSensitive(entry, p != NULL || !appData.testLegality
4126 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4127 && !appData.icsActive));
4129 while (p && *p++ == dmEnables[i].piece) count++;
4130 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4132 XtSetArg(args[j], XtNlabel, label); j++;
4133 XtSetValues(entry, args, j);
4138 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4140 String whichMenu; int menuNr = -2;
4141 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4142 if (event->type == ButtonRelease)
4143 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4144 else if (event->type == ButtonPress)
4145 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4147 case 0: whichMenu = params[0]; break;
4148 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4150 case -1: if (errorUp) ErrorPopDown();
4153 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4157 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4159 if (pmFromX < 0 || pmFromY < 0) return;
4160 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4164 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4166 if (pmFromX < 0 || pmFromY < 0) return;
4167 DropMenuEvent(piece, pmFromX, pmFromY);
4171 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4173 shiftKey = prms[0][0] & 1;
4178 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4180 shiftKey = prms[0][0] & 1;
4186 * If the user selects on a border boundary, return -1; if off the board,
4187 * return -2. Otherwise map the event coordinate to the square.
4190 EventToSquare (int x, int limit)
4197 if ((x % (squareSize + lineGap)) >= squareSize)
4199 x /= (squareSize + lineGap);
4206 do_flash_delay (unsigned long msec)
4212 drawHighlight (int file, int rank, GC gc)
4216 if (lineGap == 0) return;
4219 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4220 (squareSize + lineGap);
4221 y = lineGap/2 + rank * (squareSize + lineGap);
4223 x = lineGap/2 + file * (squareSize + lineGap);
4224 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4225 (squareSize + lineGap);
4228 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4229 squareSize+lineGap, squareSize+lineGap);
4232 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4233 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4236 SetHighlights (int fromX, int fromY, int toX, int toY)
4238 if (hi1X != fromX || hi1Y != fromY) {
4239 if (hi1X >= 0 && hi1Y >= 0) {
4240 drawHighlight(hi1X, hi1Y, lineGC);
4242 } // [HGM] first erase both, then draw new!
4243 if (hi2X != toX || hi2Y != toY) {
4244 if (hi2X >= 0 && hi2Y >= 0) {
4245 drawHighlight(hi2X, hi2Y, lineGC);
4248 if (hi1X != fromX || hi1Y != fromY) {
4249 if (fromX >= 0 && fromY >= 0) {
4250 drawHighlight(fromX, fromY, highlineGC);
4253 if (hi2X != toX || hi2Y != toY) {
4254 if (toX >= 0 && toY >= 0) {
4255 drawHighlight(toX, toY, highlineGC);
4258 if(toX<0) // clearing the highlights must have damaged arrow
4259 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4269 SetHighlights(-1, -1, -1, -1);
4274 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4276 if (pm1X != fromX || pm1Y != fromY) {
4277 if (pm1X >= 0 && pm1Y >= 0) {
4278 drawHighlight(pm1X, pm1Y, lineGC);
4280 if (fromX >= 0 && fromY >= 0) {
4281 drawHighlight(fromX, fromY, prelineGC);
4284 if (pm2X != toX || pm2Y != toY) {
4285 if (pm2X >= 0 && pm2Y >= 0) {
4286 drawHighlight(pm2X, pm2Y, lineGC);
4288 if (toX >= 0 && toY >= 0) {
4289 drawHighlight(toX, toY, prelineGC);
4299 ClearPremoveHighlights ()
4301 SetPremoveHighlights(-1, -1, -1, -1);
4305 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4307 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4308 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4310 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4311 if(textureW[kind] < W*squareSize)
4312 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4314 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4315 if(textureH[kind] < H*squareSize)
4316 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4318 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4323 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4324 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4326 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4327 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4328 squareSize, squareSize, x*fac, y*fac);
4330 if (useImages && useImageSqs) {
4334 pm = xpmLightSquare;
4339 case 2: /* neutral */
4344 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4345 squareSize, squareSize, x*fac, y*fac);
4355 case 2: /* neutral */
4360 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4365 I split out the routines to draw a piece so that I could
4366 make a generic flash routine.
4369 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4371 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4372 switch (square_color) {
4374 case 2: /* neutral */
4376 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4377 ? *pieceToOutline(piece)
4378 : *pieceToSolid(piece),
4379 dest, bwPieceGC, 0, 0,
4380 squareSize, squareSize, x, y);
4383 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4384 ? *pieceToSolid(piece)
4385 : *pieceToOutline(piece),
4386 dest, wbPieceGC, 0, 0,
4387 squareSize, squareSize, x, y);
4393 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4395 switch (square_color) {
4397 case 2: /* neutral */
4399 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4400 ? *pieceToOutline(piece)
4401 : *pieceToSolid(piece),
4402 dest, bwPieceGC, 0, 0,
4403 squareSize, squareSize, x, y, 1);
4406 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4407 ? *pieceToSolid(piece)
4408 : *pieceToOutline(piece),
4409 dest, wbPieceGC, 0, 0,
4410 squareSize, squareSize, x, y, 1);
4416 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4418 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4419 switch (square_color) {
4421 XCopyPlane(xDisplay, *pieceToSolid(piece),
4422 dest, (int) piece < (int) BlackPawn
4423 ? wlPieceGC : blPieceGC, 0, 0,
4424 squareSize, squareSize, x, y, 1);
4427 XCopyPlane(xDisplay, *pieceToSolid(piece),
4428 dest, (int) piece < (int) BlackPawn
4429 ? wdPieceGC : bdPieceGC, 0, 0,
4430 squareSize, squareSize, x, y, 1);
4432 case 2: /* neutral */
4434 XCopyPlane(xDisplay, *pieceToSolid(piece),
4435 dest, (int) piece < (int) BlackPawn
4436 ? wjPieceGC : bjPieceGC, 0, 0,
4437 squareSize, squareSize, x, y, 1);
4443 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4445 int kind, p = piece;
4447 switch (square_color) {
4449 case 2: /* neutral */
4451 if ((int)piece < (int) BlackPawn) {
4459 if ((int)piece < (int) BlackPawn) {
4467 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4468 if(useTexture & square_color+1) {
4469 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4470 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4471 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4472 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4473 XSetClipMask(xDisplay, wlPieceGC, None);
4474 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4476 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4477 dest, wlPieceGC, 0, 0,
4478 squareSize, squareSize, x, y);
4481 typedef void (*DrawFunc)();
4486 if (appData.monoMode) {
4487 if (DefaultDepth(xDisplay, xScreen) == 1) {
4488 return monoDrawPiece_1bit;
4490 return monoDrawPiece;
4494 return colorDrawPieceImage;
4496 return colorDrawPiece;
4500 /* [HR] determine square color depending on chess variant. */
4502 SquareColor (int row, int column)
4506 if (gameInfo.variant == VariantXiangqi) {
4507 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4509 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4511 } else if (row <= 4) {
4517 square_color = ((column + row) % 2) == 1;
4520 /* [hgm] holdings: next line makes all holdings squares light */
4521 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4523 return square_color;
4527 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4529 int square_color, x, y, direction, font_ascent, font_descent;
4532 XCharStruct overall;
4536 /* Calculate delay in milliseconds (2-delays per complete flash) */
4537 flash_delay = 500 / appData.flashRate;
4540 x = lineGap + ((BOARD_WIDTH-1)-column) *
4541 (squareSize + lineGap);
4542 y = lineGap + row * (squareSize + lineGap);
4544 x = lineGap + column * (squareSize + lineGap);
4545 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4546 (squareSize + lineGap);
4549 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4551 square_color = SquareColor(row, column);
4553 if ( // [HGM] holdings: blank out area between board and holdings
4554 column == BOARD_LEFT-1 || column == BOARD_RGHT
4555 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4556 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4557 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4559 // [HGM] print piece counts next to holdings
4560 string[1] = NULLCHAR;
4561 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4562 string[0] = '0' + piece;
4563 XTextExtents(countFontStruct, string, 1, &direction,
4564 &font_ascent, &font_descent, &overall);
4565 if (appData.monoMode) {
4566 XDrawImageString(xDisplay, xBoardWindow, countGC,
4567 x + squareSize - overall.width - 2,
4568 y + font_ascent + 1, string, 1);
4570 XDrawString(xDisplay, xBoardWindow, countGC,
4571 x + squareSize - overall.width - 2,
4572 y + font_ascent + 1, string, 1);
4575 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4576 string[0] = '0' + piece;
4577 XTextExtents(countFontStruct, string, 1, &direction,
4578 &font_ascent, &font_descent, &overall);
4579 if (appData.monoMode) {
4580 XDrawImageString(xDisplay, xBoardWindow, countGC,
4581 x + 2, y + font_ascent + 1, string, 1);
4583 XDrawString(xDisplay, xBoardWindow, countGC,
4584 x + 2, y + font_ascent + 1, string, 1);
4588 if (piece == EmptySquare || appData.blindfold) {
4589 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4591 drawfunc = ChooseDrawFunc();
4593 if (do_flash && appData.flashCount > 0) {
4594 for (i=0; i<appData.flashCount; ++i) {
4595 drawfunc(piece, square_color, x, y, xBoardWindow);
4596 XSync(xDisplay, False);
4597 do_flash_delay(flash_delay);
4599 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4600 XSync(xDisplay, False);
4601 do_flash_delay(flash_delay);
4604 drawfunc(piece, square_color, x, y, xBoardWindow);
4608 string[1] = NULLCHAR;
4609 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4610 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4611 string[0] = 'a' + column - BOARD_LEFT;
4612 XTextExtents(coordFontStruct, string, 1, &direction,
4613 &font_ascent, &font_descent, &overall);
4614 if (appData.monoMode) {
4615 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4616 x + squareSize - overall.width - 2,
4617 y + squareSize - font_descent - 1, string, 1);
4619 XDrawString(xDisplay, xBoardWindow, coordGC,
4620 x + squareSize - overall.width - 2,
4621 y + squareSize - font_descent - 1, string, 1);
4624 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4625 string[0] = ONE + row;
4626 XTextExtents(coordFontStruct, string, 1, &direction,
4627 &font_ascent, &font_descent, &overall);
4628 if (appData.monoMode) {
4629 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4630 x + 2, y + font_ascent + 1, string, 1);
4632 XDrawString(xDisplay, xBoardWindow, coordGC,
4633 x + 2, y + font_ascent + 1, string, 1);
4636 if(!partnerUp && marker[row][column]) {
4637 if(appData.monoMode) {
4638 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4639 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4640 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4641 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4643 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4644 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4649 /* Why is this needed on some versions of X? */
4651 EventProc (Widget widget, caddr_t unused, XEvent *event)
4653 if (!XtIsRealized(widget))
4656 switch (event->type) {
4658 if (event->xexpose.count > 0) return; /* no clipping is done */
4659 XDrawPosition(widget, True, NULL);
4660 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4661 flipView = !flipView; partnerUp = !partnerUp;
4662 XDrawPosition(widget, True, NULL);
4663 flipView = !flipView; partnerUp = !partnerUp;
4667 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4675 DrawPosition (int fullRedraw, Board board)
4677 XDrawPosition(boardWidget, fullRedraw, board);
4680 /* Returns 1 if there are "too many" differences between b1 and b2
4681 (i.e. more than 1 move was made) */
4683 too_many_diffs (Board b1, Board b2)
4688 for (i=0; i<BOARD_HEIGHT; ++i) {
4689 for (j=0; j<BOARD_WIDTH; ++j) {
4690 if (b1[i][j] != b2[i][j]) {
4691 if (++c > 4) /* Castling causes 4 diffs */
4699 /* Matrix describing castling maneuvers */
4700 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4701 static int castling_matrix[4][5] = {
4702 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4703 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4704 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4705 { 7, 7, 4, 5, 6 } /* 0-0, black */
4708 /* Checks whether castling occurred. If it did, *rrow and *rcol
4709 are set to the destination (row,col) of the rook that moved.
4711 Returns 1 if castling occurred, 0 if not.
4713 Note: Only handles a max of 1 castling move, so be sure
4714 to call too_many_diffs() first.
4717 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4722 /* For each type of castling... */
4723 for (i=0; i<4; ++i) {
4724 r = castling_matrix[i];
4726 /* Check the 4 squares involved in the castling move */
4728 for (j=1; j<=4; ++j) {
4729 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4736 /* All 4 changed, so it must be a castling move */
4745 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4747 DrawSeekAxis (int x, int y, int xTo, int yTo)
4749 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4753 DrawSeekBackground (int left, int top, int right, int bottom)
4755 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4759 DrawSeekText (char *buf, int x, int y)
4761 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4765 DrawSeekDot (int x, int y, int colorNr)
4767 int square = colorNr & 0x80;
4770 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4772 XFillRectangle(xDisplay, xBoardWindow, color,
4773 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4775 XFillArc(xDisplay, xBoardWindow, color,
4776 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4779 static int damage[2][BOARD_RANKS][BOARD_FILES];
4782 * event handler for redrawing the board
4785 XDrawPosition (Widget w, int repaint, Board board)
4788 static int lastFlipView = 0;
4789 static int lastBoardValid[2] = {0, 0};
4790 static Board lastBoard[2];
4793 int nr = twoBoards*partnerUp;
4795 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4797 if (board == NULL) {
4798 if (!lastBoardValid[nr]) return;
4799 board = lastBoard[nr];
4801 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4802 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4803 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4808 * It would be simpler to clear the window with XClearWindow()
4809 * but this causes a very distracting flicker.
4812 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4814 if ( lineGap && IsDrawArrowEnabled())
4815 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4816 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4818 /* If too much changes (begin observing new game, etc.), don't
4820 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4822 /* Special check for castling so we don't flash both the king
4823 and the rook (just flash the king). */
4825 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4826 /* Draw rook with NO flashing. King will be drawn flashing later */
4827 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4828 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4832 /* First pass -- Draw (newly) empty squares and repair damage.
4833 This prevents you from having a piece show up twice while it
4834 is flashing on its new square */
4835 for (i = 0; i < BOARD_HEIGHT; i++)
4836 for (j = 0; j < BOARD_WIDTH; j++)
4837 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4838 || damage[nr][i][j]) {
4839 DrawSquare(i, j, board[i][j], 0);
4840 damage[nr][i][j] = False;
4843 /* Second pass -- Draw piece(s) in new position and flash them */
4844 for (i = 0; i < BOARD_HEIGHT; i++)
4845 for (j = 0; j < BOARD_WIDTH; j++)
4846 if (board[i][j] != lastBoard[nr][i][j]) {
4847 DrawSquare(i, j, board[i][j], do_flash);
4851 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4852 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4853 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4855 for (i = 0; i < BOARD_HEIGHT; i++)
4856 for (j = 0; j < BOARD_WIDTH; j++) {
4857 DrawSquare(i, j, board[i][j], 0);
4858 damage[nr][i][j] = False;
4862 CopyBoard(lastBoard[nr], board);
4863 lastBoardValid[nr] = 1;
4864 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4865 lastFlipView = flipView;
4867 /* Draw highlights */
4868 if (pm1X >= 0 && pm1Y >= 0) {
4869 drawHighlight(pm1X, pm1Y, prelineGC);
4871 if (pm2X >= 0 && pm2Y >= 0) {
4872 drawHighlight(pm2X, pm2Y, prelineGC);
4874 if (hi1X >= 0 && hi1Y >= 0) {
4875 drawHighlight(hi1X, hi1Y, highlineGC);
4877 if (hi2X >= 0 && hi2Y >= 0) {
4878 drawHighlight(hi2X, hi2Y, highlineGC);
4880 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4882 /* If piece being dragged around board, must redraw that too */
4885 XSync(xDisplay, False);
4890 * event handler for redrawing the board
4893 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4895 XDrawPosition(w, True, NULL);
4900 * event handler for parsing user moves
4902 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4903 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4904 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4905 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4906 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4907 // and at the end FinishMove() to perform the move after optional promotion popups.
4908 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4910 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4912 if (w != boardWidget || errorExitStatus != -1) return;
4913 if(nprms) shiftKey = !strcmp(prms[0], "1");
4916 if (event->type == ButtonPress) {
4917 XtPopdown(promotionShell);
4918 XtDestroyWidget(promotionShell);
4919 promotionUp = False;
4927 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4928 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4929 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4933 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4935 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4936 DragPieceMove(event->xmotion.x, event->xmotion.y);
4940 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4941 { // [HGM] pv: walk PV
4942 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4945 static int savedIndex; /* gross that this is global */
4948 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4951 XawTextPosition index, dummy;
4954 XawTextGetSelectionPos(w, &index, &dummy);
4955 XtSetArg(arg, XtNstring, &val);
4956 XtGetValues(w, &arg, 1);
4957 ReplaceComment(savedIndex, val);
4958 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4959 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4963 EditCommentPopUp (int index, char *title, char *text)
4966 if (text == NULL) text = "";
4967 NewCommentPopup(title, text, index);
4976 extern Option boxOptions[];
4986 edit = boxOptions[0].handle;
4988 XtSetArg(args[j], XtNstring, &val); j++;
4989 XtGetValues(edit, args, j);
4991 SendMultiLineToICS(val);
4992 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4993 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4997 ICSInputBoxPopDown ()
5003 CommentPopUp (char *title, char *text)
5005 savedIndex = currentMove; // [HGM] vari
5006 NewCommentPopup(title, text, currentMove);
5015 static char *openName;
5021 (void) (*fileProc)(openFP, 0, openName);
5025 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
5027 fileProc = proc; /* I can't see a way not */
5028 fileOpenMode = openMode; /* to use globals here */
5029 { // [HGM] use file-selector dialog stolen from Ghostview
5030 int index; // this is not supported yet
5031 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5032 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5033 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5034 ScheduleDelayedEvent(&DelayedLoad, 50);
5041 if (!filenameUp) return;
5042 XtPopdown(fileNameShell);
5043 XtDestroyWidget(fileNameShell);
5049 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5054 XtSetArg(args[0], XtNlabel, &name);
5055 XtGetValues(w, args, 1);
5057 if (strcmp(name, _("cancel")) == 0) {
5062 FileNameAction(w, NULL, NULL, NULL);
5066 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5074 name = XawDialogGetValueString(w = XtParent(w));
5076 if ((name != NULL) && (*name != NULLCHAR)) {
5077 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5078 XtPopdown(w = XtParent(XtParent(w)));
5082 p = strrchr(buf, ' ');
5089 fullname = ExpandPathName(buf);
5091 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5094 f = fopen(fullname, fileOpenMode);
5096 DisplayError(_("Failed to open file"), errno);
5098 (void) (*fileProc)(f, index, buf);
5105 XtPopdown(w = XtParent(XtParent(w)));
5115 Widget dialog, layout;
5117 Dimension bw_width, pw_width;
5119 char *PromoChars = "wglcqrbnkac+=\0";
5122 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5123 XtGetValues(boardWidget, args, j);
5126 XtSetArg(args[j], XtNresizable, True); j++;
5127 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5129 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5130 shellWidget, args, j);
5132 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5133 layoutArgs, XtNumber(layoutArgs));
5136 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5137 XtSetArg(args[j], XtNborderWidth, 0); j++;
5138 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5141 if(gameInfo.variant != VariantShogi) {
5142 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5143 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5144 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5145 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5146 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5148 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5149 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5150 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5151 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5153 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5154 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5155 gameInfo.variant == VariantGiveaway) {
5156 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5158 if(gameInfo.variant == VariantCapablanca ||
5159 gameInfo.variant == VariantGothic ||
5160 gameInfo.variant == VariantCapaRandom) {
5161 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5162 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5164 } else // [HGM] shogi
5166 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5167 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5169 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5171 XtRealizeWidget(promotionShell);
5172 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5175 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5176 XtGetValues(promotionShell, args, j);
5178 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5179 lineGap + squareSize/3 +
5180 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5181 0 : 6*(squareSize + lineGap)), &x, &y);
5184 XtSetArg(args[j], XtNx, x); j++;
5185 XtSetArg(args[j], XtNy, y); j++;
5186 XtSetValues(promotionShell, args, j);
5188 XtPopup(promotionShell, XtGrabNone);
5196 if (!promotionUp) return;
5197 XtPopdown(promotionShell);
5198 XtDestroyWidget(promotionShell);
5199 promotionUp = False;
5203 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5205 int promoChar = * (const char *) client_data;
5209 if (fromX == -1) return;
5216 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5218 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5219 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5225 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5227 dialogError = errorUp = False;
5228 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5230 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5237 if (!errorUp) return;
5238 dialogError = errorUp = False;
5239 XtPopdown(errorShell);
5240 XtDestroyWidget(errorShell);
5241 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5245 ErrorPopUp (char *title, char *label, int modal)
5248 Widget dialog, layout;
5252 Dimension bw_width, pw_width;
5253 Dimension pw_height;
5257 XtSetArg(args[i], XtNresizable, True); i++;
5258 XtSetArg(args[i], XtNtitle, title); i++;
5260 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5261 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
5263 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5264 layoutArgs, XtNumber(layoutArgs));
5267 XtSetArg(args[i], XtNlabel, label); i++;
5268 XtSetArg(args[i], XtNborderWidth, 0); i++;
5269 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5272 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5274 XtRealizeWidget(errorShell);
5275 CatchDeleteWindow(errorShell, "ErrorPopDown");
5278 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5279 XtGetValues(boardWidget, args, i);
5281 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5282 XtSetArg(args[i], XtNheight, &pw_height); i++;
5283 XtGetValues(errorShell, args, i);
5286 /* This code seems to tickle an X bug if it is executed too soon
5287 after xboard starts up. The coordinates get transformed as if
5288 the main window was positioned at (0, 0).
5290 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5291 0 - pw_height + squareSize / 3, &x, &y);
5293 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5294 RootWindowOfScreen(XtScreen(boardWidget)),
5295 (bw_width - pw_width) / 2,
5296 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5300 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5303 XtSetArg(args[i], XtNx, x); i++;
5304 XtSetArg(args[i], XtNy, y); i++;
5305 XtSetValues(errorShell, args, i);
5308 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5311 /* Disable all user input other than deleting the window */
5312 static int frozen = 0;
5318 /* Grab by a widget that doesn't accept input */
5319 XtAddGrab(messageWidget, TRUE, FALSE);
5323 /* Undo a FreezeUI */
5327 if (!frozen) return;
5328 XtRemoveGrab(messageWidget);
5333 ModeToWidgetName (GameMode mode)
5336 case BeginningOfGame:
5337 if (appData.icsActive)
5338 return "menuMode.ICS Client";
5339 else if (appData.noChessProgram ||
5340 *appData.cmailGameName != NULLCHAR)
5341 return "menuMode.Edit Game";
5343 return "menuMode.Machine Black";
5344 case MachinePlaysBlack:
5345 return "menuMode.Machine Black";
5346 case MachinePlaysWhite:
5347 return "menuMode.Machine White";
5349 return "menuMode.Analysis Mode";
5351 return "menuMode.Analyze File";
5352 case TwoMachinesPlay:
5353 return "menuMode.Two Machines";
5355 return "menuMode.Edit Game";
5356 case PlayFromGameFile:
5357 return "menuFile.Load Game";
5359 return "menuMode.Edit Position";
5361 return "menuMode.Training";
5362 case IcsPlayingWhite:
5363 case IcsPlayingBlack:
5367 return "menuMode.ICS Client";
5378 static int oldPausing = FALSE;
5379 static GameMode oldmode = (GameMode) -1;
5382 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5384 if (pausing != oldPausing) {
5385 oldPausing = pausing;
5387 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5389 XtSetArg(args[0], XtNleftBitmap, None);
5391 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5394 if (appData.showButtonBar) {
5395 /* Always toggle, don't set. Previous code messes up when
5396 invoked while the button is pressed, as releasing it
5397 toggles the state again. */
5400 XtSetArg(args[0], XtNbackground, &oldbg);
5401 XtSetArg(args[1], XtNforeground, &oldfg);
5402 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5404 XtSetArg(args[0], XtNbackground, oldfg);
5405 XtSetArg(args[1], XtNforeground, oldbg);
5407 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5411 wname = ModeToWidgetName(oldmode);
5412 if (wname != NULL) {
5413 XtSetArg(args[0], XtNleftBitmap, None);
5414 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5416 wname = ModeToWidgetName(gameMode);
5417 if (wname != NULL) {
5418 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5419 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5422 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5423 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5425 /* Maybe all the enables should be handled here, not just this one */
5426 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5427 gameMode == Training || gameMode == PlayFromGameFile);
5432 * Button/menu procedures
5435 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5441 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5443 cmailMsgLoaded = FALSE;
5444 if (gameNumber == 0) {
5445 int error = GameListBuild(f);
5447 DisplayError(_("Cannot build game list"), error);
5448 } else if (!ListEmpty(&gameList) &&
5449 ((ListGame *) gameList.tailPred)->number > 1) {
5450 GameListPopUp(f, title);
5456 return LoadGame(f, gameNumber, title, FALSE);
5460 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5462 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5465 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5469 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5475 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5481 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5487 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5493 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5499 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5505 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5507 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5510 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5514 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5516 FileNamePopUp(_("Save game file name?"),
5517 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5518 appData.oldSaveStyle ? ".game" : ".pgn",
5523 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5525 FileNamePopUp(_("Save position file name?"),
5526 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5527 appData.oldSaveStyle ? ".pos" : ".fen",
5532 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5534 ReloadCmailMsgEvent(FALSE);
5538 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5543 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5544 char *selected_fen_position=NULL;
5547 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5548 Atom *type_return, XtPointer *value_return,
5549 unsigned long *length_return, int *format_return)
5551 char *selection_tmp;
5553 if (!selected_fen_position) return False; /* should never happen */
5554 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5555 /* note: since no XtSelectionDoneProc was registered, Xt will
5556 * automatically call XtFree on the value returned. So have to
5557 * make a copy of it allocated with XtMalloc */
5558 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5559 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5561 *value_return=selection_tmp;
5562 *length_return=strlen(selection_tmp);
5563 *type_return=*target;
5564 *format_return = 8; /* bits per byte */
5566 } else if (*target == XA_TARGETS(xDisplay)) {
5567 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5568 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5569 targets_tmp[1] = XA_STRING;
5570 *value_return = targets_tmp;
5571 *type_return = XA_ATOM;
5574 // This code leads to a read of value_return out of bounds on 64-bit systems.
5575 // Other code which I have seen always sets *format_return to 32 independent of
5576 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5577 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5578 *format_return = 8 * sizeof(Atom);
5579 if (*format_return > 32) {
5580 *length_return *= *format_return / 32;
5581 *format_return = 32;
5584 *format_return = 32;
5592 /* note: when called from menu all parameters are NULL, so no clue what the
5593 * Widget which was clicked on was, or what the click event was
5596 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5599 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5600 * have a notion of a position that is selected but not copied.
5601 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5603 if(gameMode == EditPosition) EditPositionDone(TRUE);
5604 if (selected_fen_position) free(selected_fen_position);
5605 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5606 if (!selected_fen_position) return;
5607 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5609 SendPositionSelection,
5610 NULL/* lose_ownership_proc */ ,
5611 NULL/* transfer_done_proc */);
5612 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5614 SendPositionSelection,
5615 NULL/* lose_ownership_proc */ ,
5616 NULL/* transfer_done_proc */);
5619 /* function called when the data to Paste is ready */
5621 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5622 Atom *type, XtPointer value, unsigned long *len, int *format)
5625 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5626 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5627 EditPositionPasteFEN(fenstr);
5631 /* called when Paste Position button is pressed,
5632 * all parameters will be NULL */
5633 void PastePositionProc(w, event, prms, nprms)
5639 XtGetSelectionValue(menuBarWidget,
5640 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5641 /* (XtSelectionCallbackProc) */ PastePositionCB,
5642 NULL, /* client_data passed to PastePositionCB */
5644 /* better to use the time field from the event that triggered the
5645 * call to this function, but that isn't trivial to get
5653 SendGameSelection (Widget w, Atom *selection, Atom *target,
5654 Atom *type_return, XtPointer *value_return,
5655 unsigned long *length_return, int *format_return)
5657 char *selection_tmp;
5659 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5660 FILE* f = fopen(gameCopyFilename, "r");
5663 if (f == NULL) return False;
5667 selection_tmp = XtMalloc(len + 1);
5668 count = fread(selection_tmp, 1, len, f);
5671 XtFree(selection_tmp);
5674 selection_tmp[len] = NULLCHAR;
5675 *value_return = selection_tmp;
5676 *length_return = len;
5677 *type_return = *target;
5678 *format_return = 8; /* bits per byte */
5680 } else if (*target == XA_TARGETS(xDisplay)) {
5681 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5682 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5683 targets_tmp[1] = XA_STRING;
5684 *value_return = targets_tmp;
5685 *type_return = XA_ATOM;
5688 // This code leads to a read of value_return out of bounds on 64-bit systems.
5689 // Other code which I have seen always sets *format_return to 32 independent of
5690 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5691 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5692 *format_return = 8 * sizeof(Atom);
5693 if (*format_return > 32) {
5694 *length_return *= *format_return / 32;
5695 *format_return = 32;
5698 *format_return = 32;
5710 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5711 * have a notion of a game that is selected but not copied.
5712 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5714 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5717 NULL/* lose_ownership_proc */ ,
5718 NULL/* transfer_done_proc */);
5719 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5722 NULL/* lose_ownership_proc */ ,
5723 NULL/* transfer_done_proc */);
5726 /* note: when called from menu all parameters are NULL, so no clue what the
5727 * Widget which was clicked on was, or what the click event was
5730 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5734 ret = SaveGameToFile(gameCopyFilename, FALSE);
5741 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5743 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5747 /* function called when the data to Paste is ready */
5749 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5750 Atom *type, XtPointer value, unsigned long *len, int *format)
5753 if (value == NULL || *len == 0) {
5754 return; /* nothing had been selected to copy */
5756 f = fopen(gamePasteFilename, "w");
5758 DisplayError(_("Can't open temp file"), errno);
5761 fwrite(value, 1, *len, f);
5764 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5767 /* called when Paste Game button is pressed,
5768 * all parameters will be NULL */
5770 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5772 XtGetSelectionValue(menuBarWidget,
5773 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5774 /* (XtSelectionCallbackProc) */ PasteGameCB,
5775 NULL, /* client_data passed to PasteGameCB */
5777 /* better to use the time field from the event that triggered the
5778 * call to this function, but that isn't trivial to get
5789 SaveGameProc(NULL, NULL, NULL, NULL);
5794 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5800 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5806 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5808 MachineBlackEvent();
5812 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5814 MachineWhiteEvent();
5818 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5822 if (!first.analysisSupport) {
5823 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5824 DisplayError(buf, 0);
5827 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5828 if (appData.icsActive) {
5829 if (gameMode != IcsObserving) {
5830 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5831 DisplayError(buf, 0);
5833 if (appData.icsEngineAnalyze) {
5834 if (appData.debugMode)
5835 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5841 /* if enable, use want disable icsEngineAnalyze */
5842 if (appData.icsEngineAnalyze) {
5847 appData.icsEngineAnalyze = TRUE;
5848 if (appData.debugMode)
5849 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5851 #ifndef OPTIONSDIALOG
5852 if (!appData.showThinking)
5853 ShowThinkingProc(w,event,prms,nprms);
5860 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5862 if (!first.analysisSupport) {
5864 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5865 DisplayError(buf, 0);
5868 // Reset(FALSE, TRUE);
5869 #ifndef OPTIONSDIALOG
5870 if (!appData.showThinking)
5871 ShowThinkingProc(w,event,prms,nprms);
5874 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5875 AnalysisPeriodicEvent(1);
5879 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5885 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5891 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5897 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5903 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5905 EditPositionEvent();
5909 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5915 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5919 if (PopDown(1)) { // popdown succesful
5921 XtSetArg(args[j], XtNleftBitmap, None); j++;
5922 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5923 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5924 } else // was not up
5929 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5931 if (!PopDown(4)) ICSInputBoxPopUp();
5935 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5941 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5947 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5953 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5959 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5965 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5971 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5977 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5983 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5985 UserAdjudicationEvent(+1);
5989 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5991 UserAdjudicationEvent(-1);
5995 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5997 UserAdjudicationEvent(0);
6001 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6003 if (shellUp[4] == True)
6008 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6009 { // [HGM] input: let up-arrow recall previous line from history
6016 if (!shellUp[4]) return;
6017 edit = boxOptions[0].handle;
6019 XtSetArg(args[j], XtNstring, &val); j++;
6020 XtGetValues(edit, args, j);
6021 val = PrevInHistory(val);
6022 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6023 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6025 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6026 XawTextReplace(edit, 0, 0, &t);
6027 XawTextSetInsertionPoint(edit, 9999);
6032 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6033 { // [HGM] input: let down-arrow recall next line from history
6038 if (!shellUp[4]) return;
6039 edit = boxOptions[0].handle;
6040 val = NextInHistory();
6041 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6042 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6044 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6045 XawTextReplace(edit, 0, 0, &t);
6046 XawTextSetInsertionPoint(edit, 9999);
6051 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6053 StopObservingEvent();
6057 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6059 StopExaminingEvent();
6063 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6070 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6077 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6083 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6085 if (!TempBackwardActive) {
6086 TempBackwardActive = True;
6092 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6094 /* Check to see if triggered by a key release event for a repeating key.
6095 * If so the next queued event will be a key press of the same key at the same time */
6096 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6098 XPeekEvent(xDisplay, &next);
6099 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6100 next.xkey.keycode == event->xkey.keycode)
6104 TempBackwardActive = False;
6108 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6114 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6120 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6126 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6132 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6134 TruncateGameEvent();
6138 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6144 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6150 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6152 flipView = !flipView;
6153 DrawPosition(True, NULL);
6157 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6161 PonderNextMoveEvent(!appData.ponderNextMove);
6162 #ifndef OPTIONSDIALOG
6163 if (appData.ponderNextMove) {
6164 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6166 XtSetArg(args[0], XtNleftBitmap, None);
6168 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6173 #ifndef OPTIONSDIALOG
6175 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6179 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6181 if (appData.alwaysPromoteToQueen) {
6182 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6184 XtSetArg(args[0], XtNleftBitmap, None);
6186 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6191 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6195 appData.animateDragging = !appData.animateDragging;
6197 if (appData.animateDragging) {
6198 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6201 XtSetArg(args[0], XtNleftBitmap, None);
6203 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6208 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6212 appData.animate = !appData.animate;
6214 if (appData.animate) {
6215 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6218 XtSetArg(args[0], XtNleftBitmap, None);
6220 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6225 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6229 appData.autoCallFlag = !appData.autoCallFlag;
6231 if (appData.autoCallFlag) {
6232 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6234 XtSetArg(args[0], XtNleftBitmap, None);
6236 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6241 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6245 appData.autoFlipView = !appData.autoFlipView;
6247 if (appData.autoFlipView) {
6248 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6250 XtSetArg(args[0], XtNleftBitmap, None);
6252 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6257 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6261 appData.blindfold = !appData.blindfold;
6263 if (appData.blindfold) {
6264 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6266 XtSetArg(args[0], XtNleftBitmap, None);
6268 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6271 DrawPosition(True, NULL);
6275 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6279 appData.testLegality = !appData.testLegality;
6281 if (appData.testLegality) {
6282 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6284 XtSetArg(args[0], XtNleftBitmap, None);
6286 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6292 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6296 if (appData.flashCount == 0) {
6297 appData.flashCount = 3;
6299 appData.flashCount = -appData.flashCount;
6302 if (appData.flashCount > 0) {
6303 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6305 XtSetArg(args[0], XtNleftBitmap, None);
6307 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6313 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6317 appData.highlightDragging = !appData.highlightDragging;
6319 if (appData.highlightDragging) {
6320 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6322 XtSetArg(args[0], XtNleftBitmap, None);
6324 XtSetValues(XtNameToWidget(menuBarWidget,
6325 "menuOptions.Highlight Dragging"), args, 1);
6330 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6334 appData.highlightLastMove = !appData.highlightLastMove;
6336 if (appData.highlightLastMove) {
6337 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6339 XtSetArg(args[0], XtNleftBitmap, None);
6341 XtSetValues(XtNameToWidget(menuBarWidget,
6342 "menuOptions.Highlight Last Move"), args, 1);
6346 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6350 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6352 if (appData.highlightMoveWithArrow) {
6353 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6355 XtSetArg(args[0], XtNleftBitmap, None);
6357 XtSetValues(XtNameToWidget(menuBarWidget,
6358 "menuOptions.Arrow"), args, 1);
6363 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6367 appData.icsAlarm = !appData.icsAlarm;
6369 if (appData.icsAlarm) {
6370 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6372 XtSetArg(args[0], XtNleftBitmap, None);
6374 XtSetValues(XtNameToWidget(menuBarWidget,
6375 "menuOptions.ICS Alarm"), args, 1);
6380 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6384 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6386 if (appData.ringBellAfterMoves) {
6387 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6389 XtSetArg(args[0], XtNleftBitmap, None);
6391 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6396 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6400 appData.oneClick = !appData.oneClick;
6402 if (appData.oneClick) {
6403 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6405 XtSetArg(args[0], XtNleftBitmap, None);
6407 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6412 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6416 PeriodicUpdatesEvent(!appData.periodicUpdates);
6418 if (appData.periodicUpdates) {
6419 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6421 XtSetArg(args[0], XtNleftBitmap, None);
6423 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6428 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6432 appData.popupExitMessage = !appData.popupExitMessage;
6434 if (appData.popupExitMessage) {
6435 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6437 XtSetArg(args[0], XtNleftBitmap, None);
6439 XtSetValues(XtNameToWidget(menuBarWidget,
6440 "menuOptions.Popup Exit Message"), args, 1);
6444 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6448 appData.popupMoveErrors = !appData.popupMoveErrors;
6450 if (appData.popupMoveErrors) {
6451 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6453 XtSetArg(args[0], XtNleftBitmap, None);
6455 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6461 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6465 appData.premove = !appData.premove;
6467 if (appData.premove) {
6468 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6470 XtSetArg(args[0], XtNleftBitmap, None);
6472 XtSetValues(XtNameToWidget(menuBarWidget,
6473 "menuOptions.Premove"), args, 1);
6478 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6482 appData.showCoords = !appData.showCoords;
6484 if (appData.showCoords) {
6485 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6487 XtSetArg(args[0], XtNleftBitmap, None);
6489 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6492 DrawPosition(True, NULL);
6496 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6498 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6499 ShowThinkingEvent();
6503 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6507 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6508 ShowThinkingEvent();
6510 if (appData.hideThinkingFromHuman) {
6511 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6513 XtSetArg(args[0], XtNleftBitmap, None);
6515 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6521 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6525 saveSettingsOnExit = !saveSettingsOnExit;
6527 if (saveSettingsOnExit) {
6528 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6530 XtSetArg(args[0], XtNleftBitmap, None);
6532 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6537 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6539 SaveSettings(settingsFileName);
6543 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6546 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6552 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6556 if (nprms && *nprms > 0)
6560 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6565 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6571 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6577 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6581 char *zippy = _(" (with Zippy code)");
6585 snprintf(buf, sizeof(buf),
6587 "Copyright 1991 Digital Equipment Corporation\n"
6588 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6589 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6590 "%s is free software and carries NO WARRANTY;"
6591 "see the file COPYING for more information."),
6592 programVersion, zippy, PACKAGE);
6593 ErrorPopUp(_("About XBoard"), buf, FALSE);
6597 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6599 appData.debugMode = !appData.debugMode;
6603 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6609 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6615 DisplayMessage (char *message, char *extMessage)
6617 /* display a message in the message widget */
6626 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6631 message = extMessage;
6635 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6637 /* need to test if messageWidget already exists, since this function
6638 can also be called during the startup, if for example a Xresource
6639 is not set up correctly */
6642 XtSetArg(arg, XtNlabel, message);
6643 XtSetValues(messageWidget, &arg, 1);
6650 DisplayTitle (char *text)
6654 char title[MSG_SIZ];
6657 if (text == NULL) text = "";
6659 if (appData.titleInWindow) {
6661 XtSetArg(args[i], XtNlabel, text); i++;
6662 XtSetValues(titleWidget, args, i);
6665 if (*text != NULLCHAR) {
6666 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6667 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6668 } else if (appData.icsActive) {
6669 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6670 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6671 } else if (appData.cmailGameName[0] != NULLCHAR) {
6672 snprintf(icon, sizeof(icon), "%s", "CMail");
6673 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6675 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6676 } else if (gameInfo.variant == VariantGothic) {
6677 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6678 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6681 } else if (gameInfo.variant == VariantFalcon) {
6682 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6683 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6685 } else if (appData.noChessProgram) {
6686 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6687 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6689 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6690 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6693 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6694 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6695 XtSetValues(shellWidget, args, i);
6696 XSync(xDisplay, False);
6701 DisplayError (String message, int error)
6706 if (appData.debugMode || appData.matchMode) {
6707 fprintf(stderr, "%s: %s\n", programName, message);
6710 if (appData.debugMode || appData.matchMode) {
6711 fprintf(stderr, "%s: %s: %s\n",
6712 programName, message, strerror(error));
6714 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6717 ErrorPopUp(_("Error"), message, FALSE);
6722 DisplayMoveError (String message)
6726 DrawPosition(FALSE, NULL);
6727 if (appData.debugMode || appData.matchMode) {
6728 fprintf(stderr, "%s: %s\n", programName, message);
6730 if (appData.popupMoveErrors) {
6731 ErrorPopUp(_("Error"), message, FALSE);
6733 DisplayMessage(message, "");
6739 DisplayFatalError (String message, int error, int status)
6743 errorExitStatus = status;
6745 fprintf(stderr, "%s: %s\n", programName, message);
6747 fprintf(stderr, "%s: %s: %s\n",
6748 programName, message, strerror(error));
6749 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6752 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6753 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6760 DisplayInformation (String message)
6763 ErrorPopUp(_("Information"), message, TRUE);
6767 DisplayNote (String message)
6770 ErrorPopUp(_("Note"), message, FALSE);
6774 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6780 DisplayIcsInteractionTitle (String message)
6782 if (oldICSInteractionTitle == NULL) {
6783 /* Magic to find the old window title, adapted from vim */
6784 char *wina = getenv("WINDOWID");
6786 Window win = (Window) atoi(wina);
6787 Window root, parent, *children;
6788 unsigned int nchildren;
6789 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6791 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6792 if (!XQueryTree(xDisplay, win, &root, &parent,
6793 &children, &nchildren)) break;
6794 if (children) XFree((void *)children);
6795 if (parent == root || parent == 0) break;
6798 XSetErrorHandler(oldHandler);
6800 if (oldICSInteractionTitle == NULL) {
6801 oldICSInteractionTitle = "xterm";
6804 printf("\033]0;%s\007", message);
6808 char pendingReplyPrefix[MSG_SIZ];
6809 ProcRef pendingReplyPR;
6812 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6815 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6819 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6823 AskQuestionPopDown ()
6825 if (!askQuestionUp) return;
6826 XtPopdown(askQuestionShell);
6827 XtDestroyWidget(askQuestionShell);
6828 askQuestionUp = False;
6832 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6838 reply = XawDialogGetValueString(w = XtParent(w));
6839 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6840 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6841 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6842 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6843 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6844 AskQuestionPopDown();
6846 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6850 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6855 XtSetArg(args[0], XtNlabel, &name);
6856 XtGetValues(w, args, 1);
6858 if (strcmp(name, _("cancel")) == 0) {
6859 AskQuestionPopDown();
6861 AskQuestionReplyAction(w, NULL, NULL, NULL);
6866 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6869 Widget popup, layout, dialog, edit;
6875 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6876 pendingReplyPR = pr;
6879 XtSetArg(args[i], XtNresizable, True); i++;
6880 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6881 askQuestionShell = popup =
6882 XtCreatePopupShell(title, transientShellWidgetClass,
6883 shellWidget, args, i);
6886 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6887 layoutArgs, XtNumber(layoutArgs));
6890 XtSetArg(args[i], XtNlabel, question); i++;
6891 XtSetArg(args[i], XtNvalue, ""); i++;
6892 XtSetArg(args[i], XtNborderWidth, 0); i++;
6893 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6896 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6897 (XtPointer) dialog);
6898 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6899 (XtPointer) dialog);
6901 XtRealizeWidget(popup);
6902 CatchDeleteWindow(popup, "AskQuestionPopDown");
6904 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6905 &x, &y, &win_x, &win_y, &mask);
6907 XtSetArg(args[0], XtNx, x - 10);
6908 XtSetArg(args[1], XtNy, y - 30);
6909 XtSetValues(popup, args, 2);
6911 XtPopup(popup, XtGrabExclusive);
6912 askQuestionUp = True;
6914 edit = XtNameToWidget(dialog, "*value");
6915 XtSetKeyboardFocus(popup, edit);
6920 PlaySound (char *name)
6922 if (*name == NULLCHAR) {
6924 } else if (strcmp(name, "$") == 0) {
6925 putc(BELLCHAR, stderr);
6928 char *prefix = "", *sep = "";
6929 if(appData.soundProgram[0] == NULLCHAR) return;
6930 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6931 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6939 PlaySound(appData.soundMove);
6945 PlaySound(appData.soundIcsWin);
6951 PlaySound(appData.soundIcsLoss);
6957 PlaySound(appData.soundIcsDraw);
6961 PlayIcsUnfinishedSound ()
6963 PlaySound(appData.soundIcsUnfinished);
6969 PlaySound(appData.soundIcsAlarm);
6975 PlaySound(appData.soundTell);
6981 system("stty echo");
6988 system("stty -echo");
6993 RunCommand (char *buf)
6999 Colorize (ColorClass cc, int continuation)
7002 int count, outCount, error;
7004 if (textColors[(int)cc].bg > 0) {
7005 if (textColors[(int)cc].fg > 0) {
7006 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7007 textColors[(int)cc].fg, textColors[(int)cc].bg);
7009 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7010 textColors[(int)cc].bg);
7013 if (textColors[(int)cc].fg > 0) {
7014 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7015 textColors[(int)cc].fg);
7017 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7020 count = strlen(buf);
7021 outCount = OutputToProcess(NoProc, buf, count, &error);
7022 if (outCount < count) {
7023 DisplayFatalError(_("Error writing to display"), error, 1);
7026 if (continuation) return;
7029 PlaySound(appData.soundShout);
7032 PlaySound(appData.soundSShout);
7035 PlaySound(appData.soundChannel1);
7038 PlaySound(appData.soundChannel);
7041 PlaySound(appData.soundKibitz);
7044 PlaySound(appData.soundTell);
7046 case ColorChallenge:
7047 PlaySound(appData.soundChallenge);
7050 PlaySound(appData.soundRequest);
7053 PlaySound(appData.soundSeek);
7065 return getpwuid(getuid())->pw_name;
7069 ExpandPathName (char *path)
7071 static char static_buf[4*MSG_SIZ];
7072 char *d, *s, buf[4*MSG_SIZ];
7078 while (*s && isspace(*s))
7087 if (*(s+1) == '/') {
7088 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7092 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7093 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7094 pwd = getpwnam(buf);
7097 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7101 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7102 strcat(d, strchr(s+1, '/'));
7106 safeStrCpy(d, s, 4*MSG_SIZ );
7114 static char host_name[MSG_SIZ];
7116 #if HAVE_GETHOSTNAME
7117 gethostname(host_name, MSG_SIZ);
7119 #else /* not HAVE_GETHOSTNAME */
7120 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7121 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7123 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7125 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7126 #endif /* not HAVE_GETHOSTNAME */
7129 XtIntervalId delayedEventTimerXID = 0;
7130 DelayedEventCallback delayedEventCallback = 0;
7135 delayedEventTimerXID = 0;
7136 delayedEventCallback();
7140 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7142 if(delayedEventTimerXID && delayedEventCallback == cb)
7143 // [HGM] alive: replace, rather than add or flush identical event
7144 XtRemoveTimeOut(delayedEventTimerXID);
7145 delayedEventCallback = cb;
7146 delayedEventTimerXID =
7147 XtAppAddTimeOut(appContext, millisec,
7148 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7151 DelayedEventCallback
7154 if (delayedEventTimerXID) {
7155 return delayedEventCallback;
7162 CancelDelayedEvent ()
7164 if (delayedEventTimerXID) {
7165 XtRemoveTimeOut(delayedEventTimerXID);
7166 delayedEventTimerXID = 0;
7170 XtIntervalId loadGameTimerXID = 0;
7173 LoadGameTimerRunning ()
7175 return loadGameTimerXID != 0;
7179 StopLoadGameTimer ()
7181 if (loadGameTimerXID != 0) {
7182 XtRemoveTimeOut(loadGameTimerXID);
7183 loadGameTimerXID = 0;
7191 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7193 loadGameTimerXID = 0;
7198 StartLoadGameTimer (long millisec)
7201 XtAppAddTimeOut(appContext, millisec,
7202 (XtTimerCallbackProc) LoadGameTimerCallback,
7206 XtIntervalId analysisClockXID = 0;
7209 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7211 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7212 || appData.icsEngineAnalyze) { // [DM]
7213 AnalysisPeriodicEvent(0);
7214 StartAnalysisClock();
7219 StartAnalysisClock ()
7222 XtAppAddTimeOut(appContext, 2000,
7223 (XtTimerCallbackProc) AnalysisClockCallback,
7227 XtIntervalId clockTimerXID = 0;
7230 ClockTimerRunning ()
7232 return clockTimerXID != 0;
7238 if (clockTimerXID != 0) {
7239 XtRemoveTimeOut(clockTimerXID);
7248 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7255 StartClockTimer (long millisec)
7258 XtAppAddTimeOut(appContext, millisec,
7259 (XtTimerCallbackProc) ClockTimerCallback,
7264 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7269 /* check for low time warning */
7270 Pixel foregroundOrWarningColor = timerForegroundPixel;
7273 appData.lowTimeWarning &&
7274 (timer / 1000) < appData.icsAlarmTime)
7275 foregroundOrWarningColor = lowTimeWarningColor;
7277 if (appData.clockMode) {
7278 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7279 XtSetArg(args[0], XtNlabel, buf);
7281 snprintf(buf, MSG_SIZ, "%s ", color);
7282 XtSetArg(args[0], XtNlabel, buf);
7287 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7288 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7290 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7291 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7294 XtSetValues(w, args, 3);
7298 DisplayWhiteClock (long timeRemaining, int highlight)
7302 if(appData.noGUI) return;
7303 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7304 if (highlight && iconPixmap == bIconPixmap) {
7305 iconPixmap = wIconPixmap;
7306 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7307 XtSetValues(shellWidget, args, 1);
7312 DisplayBlackClock (long timeRemaining, int highlight)
7316 if(appData.noGUI) return;
7317 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7318 if (highlight && iconPixmap == wIconPixmap) {
7319 iconPixmap = bIconPixmap;
7320 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7321 XtSetValues(shellWidget, args, 1);
7340 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7344 int to_prog[2], from_prog[2];
7348 if (appData.debugMode) {
7349 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7352 /* We do NOT feed the cmdLine to the shell; we just
7353 parse it into blank-separated arguments in the
7354 most simple-minded way possible.
7357 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7360 while(*p == ' ') p++;
7362 if(*p == '"' || *p == '\'')
7363 p = strchr(++argv[i-1], *p);
7364 else p = strchr(p, ' ');
7365 if (p == NULL) break;
7370 SetUpChildIO(to_prog, from_prog);
7372 if ((pid = fork()) == 0) {
7374 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7375 close(to_prog[1]); // first close the unused pipe ends
7376 close(from_prog[0]);
7377 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7378 dup2(from_prog[1], 1);
7379 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7380 close(from_prog[1]); // and closing again loses one of the pipes!
7381 if(fileno(stderr) >= 2) // better safe than sorry...
7382 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7384 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7389 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7391 execvp(argv[0], argv);
7393 /* If we get here, exec failed */
7398 /* Parent process */
7400 close(from_prog[1]);
7402 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7405 cp->fdFrom = from_prog[0];
7406 cp->fdTo = to_prog[1];
7411 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7413 AlarmCallBack (int n)
7419 DestroyChildProcess (ProcRef pr, int signalType)
7421 ChildProc *cp = (ChildProc *) pr;
7423 if (cp->kind != CPReal) return;
7425 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7426 signal(SIGALRM, AlarmCallBack);
7428 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7429 kill(cp->pid, SIGKILL); // kill it forcefully
7430 wait((int *) 0); // and wait again
7434 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7436 /* Process is exiting either because of the kill or because of
7437 a quit command sent by the backend; either way, wait for it to die.
7446 InterruptChildProcess (ProcRef pr)
7448 ChildProc *cp = (ChildProc *) pr;
7450 if (cp->kind != CPReal) return;
7451 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7455 OpenTelnet (char *host, char *port, ProcRef *pr)
7457 char cmdLine[MSG_SIZ];
7459 if (port[0] == NULLCHAR) {
7460 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7462 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7464 return StartChildProcess(cmdLine, "", pr);
7468 OpenTCP (char *host, char *port, ProcRef *pr)
7471 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7472 #else /* !OMIT_SOCKETS */
7473 struct addrinfo hints;
7474 struct addrinfo *ais, *ai;
7479 memset(&hints, 0, sizeof(hints));
7480 hints.ai_family = AF_UNSPEC;
7481 hints.ai_socktype = SOCK_STREAM;
7483 error = getaddrinfo(host, port, &hints, &ais);
7485 /* a getaddrinfo error is not an errno, so can't return it */
7486 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7487 host, port, gai_strerror(error));
7491 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7492 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7496 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7509 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7515 #endif /* !OMIT_SOCKETS */
7521 OpenCommPort (char *name, ProcRef *pr)
7526 fd = open(name, 2, 0);
7527 if (fd < 0) return errno;
7529 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7540 OpenLoopback (ProcRef *pr)
7545 SetUpChildIO(to, from);
7547 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7550 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7558 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7560 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7564 #define INPUT_SOURCE_BUF_SIZE 8192
7573 char buf[INPUT_SOURCE_BUF_SIZE];
7578 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7580 InputSource *is = (InputSource *) closure;
7585 if (is->lineByLine) {
7586 count = read(is->fd, is->unused,
7587 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7589 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7592 is->unused += count;
7594 while (p < is->unused) {
7595 q = memchr(p, '\n', is->unused - p);
7596 if (q == NULL) break;
7598 (is->func)(is, is->closure, p, q - p, 0);
7602 while (p < is->unused) {
7607 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7612 (is->func)(is, is->closure, is->buf, count, error);
7617 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7620 ChildProc *cp = (ChildProc *) pr;
7622 is = (InputSource *) calloc(1, sizeof(InputSource));
7623 is->lineByLine = lineByLine;
7627 is->fd = fileno(stdin);
7629 is->kind = cp->kind;
7630 is->fd = cp->fdFrom;
7633 is->unused = is->buf;
7636 is->xid = XtAppAddInput(appContext, is->fd,
7637 (XtPointer) (XtInputReadMask),
7638 (XtInputCallbackProc) DoInputCallback,
7640 is->closure = closure;
7641 return (InputSourceRef) is;
7645 RemoveInputSource (InputSourceRef isr)
7647 InputSource *is = (InputSource *) isr;
7649 if (is->xid == 0) return;
7650 XtRemoveInput(is->xid);
7655 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7657 static int line = 0;
7658 ChildProc *cp = (ChildProc *) pr;
7663 if (appData.noJoin || !appData.useInternalWrap)
7664 outCount = fwrite(message, 1, count, stdout);
7667 int width = get_term_width();
7668 int len = wrap(NULL, message, count, width, &line);
7669 char *msg = malloc(len);
7673 outCount = fwrite(message, 1, count, stdout);
7676 dbgchk = wrap(msg, message, count, width, &line);
7677 if (dbgchk != len && appData.debugMode)
7678 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7679 outCount = fwrite(msg, 1, dbgchk, stdout);
7685 outCount = write(cp->fdTo, message, count);
7695 /* Output message to process, with "ms" milliseconds of delay
7696 between each character. This is needed when sending the logon
7697 script to ICC, which for some reason doesn't like the
7698 instantaneous send. */
7700 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7702 ChildProc *cp = (ChildProc *) pr;
7707 r = write(cp->fdTo, message++, 1);
7720 /**** Animation code by Hugh Fisher, DCS, ANU.
7722 Known problem: if a window overlapping the board is
7723 moved away while a piece is being animated underneath,
7724 the newly exposed area won't be updated properly.
7725 I can live with this.
7727 Known problem: if you look carefully at the animation
7728 of pieces in mono mode, they are being drawn as solid
7729 shapes without interior detail while moving. Fixing
7730 this would be a major complication for minimal return.
7733 /* Masks for XPM pieces. Black and white pieces can have
7734 different shapes, but in the interest of retaining my
7735 sanity pieces must have the same outline on both light
7736 and dark squares, and all pieces must use the same
7737 background square colors/images. */
7739 static int xpmDone = 0;
7742 CreateAnimMasks (int pieceDepth)
7748 unsigned long plane;
7751 /* Need a bitmap just to get a GC with right depth */
7752 buf = XCreatePixmap(xDisplay, xBoardWindow,
7754 values.foreground = 1;
7755 values.background = 0;
7756 /* Don't use XtGetGC, not read only */
7757 maskGC = XCreateGC(xDisplay, buf,
7758 GCForeground | GCBackground, &values);
7759 XFreePixmap(xDisplay, buf);
7761 buf = XCreatePixmap(xDisplay, xBoardWindow,
7762 squareSize, squareSize, pieceDepth);
7763 values.foreground = XBlackPixel(xDisplay, xScreen);
7764 values.background = XWhitePixel(xDisplay, xScreen);
7765 bufGC = XCreateGC(xDisplay, buf,
7766 GCForeground | GCBackground, &values);
7768 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7769 /* Begin with empty mask */
7770 if(!xpmDone) // [HGM] pieces: keep using existing
7771 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7772 squareSize, squareSize, 1);
7773 XSetFunction(xDisplay, maskGC, GXclear);
7774 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7775 0, 0, squareSize, squareSize);
7777 /* Take a copy of the piece */
7782 XSetFunction(xDisplay, bufGC, GXcopy);
7783 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7785 0, 0, squareSize, squareSize, 0, 0);
7787 /* XOR the background (light) over the piece */
7788 XSetFunction(xDisplay, bufGC, GXxor);
7790 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7791 0, 0, squareSize, squareSize, 0, 0);
7793 XSetForeground(xDisplay, bufGC, lightSquareColor);
7794 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7797 /* We now have an inverted piece image with the background
7798 erased. Construct mask by just selecting all the non-zero
7799 pixels - no need to reconstruct the original image. */
7800 XSetFunction(xDisplay, maskGC, GXor);
7802 /* Might be quicker to download an XImage and create bitmap
7803 data from it rather than this N copies per piece, but it
7804 only takes a fraction of a second and there is a much
7805 longer delay for loading the pieces. */
7806 for (n = 0; n < pieceDepth; n ++) {
7807 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7808 0, 0, squareSize, squareSize,
7814 XFreePixmap(xDisplay, buf);
7815 XFreeGC(xDisplay, bufGC);
7816 XFreeGC(xDisplay, maskGC);
7820 InitAnimState (AnimState *anim, XWindowAttributes *info)
7825 /* Each buffer is square size, same depth as window */
7826 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7827 squareSize, squareSize, info->depth);
7828 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7829 squareSize, squareSize, info->depth);
7831 /* Create a plain GC for blitting */
7832 mask = GCForeground | GCBackground | GCFunction |
7833 GCPlaneMask | GCGraphicsExposures;
7834 values.foreground = XBlackPixel(xDisplay, xScreen);
7835 values.background = XWhitePixel(xDisplay, xScreen);
7836 values.function = GXcopy;
7837 values.plane_mask = AllPlanes;
7838 values.graphics_exposures = False;
7839 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7841 /* Piece will be copied from an existing context at
7842 the start of each new animation/drag. */
7843 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7845 /* Outline will be a read-only copy of an existing */
7846 anim->outlineGC = None;
7852 XWindowAttributes info;
7854 if (xpmDone && gameInfo.variant == oldVariant) return;
7855 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7856 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7858 InitAnimState(&game, &info);
7859 InitAnimState(&player, &info);
7861 /* For XPM pieces, we need bitmaps to use as masks. */
7863 CreateAnimMasks(info.depth), xpmDone = 1;
7868 static Boolean frameWaiting;
7871 FrameAlarm (int sig)
7873 frameWaiting = False;
7874 /* In case System-V style signals. Needed?? */
7875 signal(SIGALRM, FrameAlarm);
7879 FrameDelay (int time)
7881 struct itimerval delay;
7883 XSync(xDisplay, False);
7886 frameWaiting = True;
7887 signal(SIGALRM, FrameAlarm);
7888 delay.it_interval.tv_sec =
7889 delay.it_value.tv_sec = time / 1000;
7890 delay.it_interval.tv_usec =
7891 delay.it_value.tv_usec = (time % 1000) * 1000;
7892 setitimer(ITIMER_REAL, &delay, NULL);
7893 while (frameWaiting) pause();
7894 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7895 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7896 setitimer(ITIMER_REAL, &delay, NULL);
7903 FrameDelay (int time)
7905 XSync(xDisplay, False);
7907 usleep(time * 1000);
7918 /* Convert board position to corner of screen rect and color */
7921 ScreenSquare (int column, int row, XPoint *pt, int *color)
7924 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7925 pt->y = lineGap + row * (squareSize + lineGap);
7927 pt->x = lineGap + column * (squareSize + lineGap);
7928 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7930 *color = SquareColor(row, column);
7933 /* Convert window coords to square */
7936 BoardSquare (int x, int y, int *column, int *row)
7938 *column = EventToSquare(x, BOARD_WIDTH);
7939 if (flipView && *column >= 0)
7940 *column = BOARD_WIDTH - 1 - *column;
7941 *row = EventToSquare(y, BOARD_HEIGHT);
7942 if (!flipView && *row >= 0)
7943 *row = BOARD_HEIGHT - 1 - *row;
7948 #undef Max /* just in case */
7950 #define Max(a, b) ((a) > (b) ? (a) : (b))
7951 #define Min(a, b) ((a) < (b) ? (a) : (b))
7954 SetRect (XRectangle *rect, int x, int y, int width, int height)
7958 rect->width = width;
7959 rect->height = height;
7962 /* Test if two frames overlap. If they do, return
7963 intersection rect within old and location of
7964 that rect within new. */
7967 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
7969 if (old->x > new->x + size || new->x > old->x + size ||
7970 old->y > new->y + size || new->y > old->y + size) {
7973 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7974 size - abs(old->x - new->x), size - abs(old->y - new->y));
7975 pt->x = Max(old->x - new->x, 0);
7976 pt->y = Max(old->y - new->y, 0);
7981 /* For two overlapping frames, return the rect(s)
7982 in the old that do not intersect with the new. */
7985 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
7989 /* If old = new (shouldn't happen) then nothing to draw */
7990 if (old->x == new->x && old->y == new->y) {
7994 /* Work out what bits overlap. Since we know the rects
7995 are the same size we don't need a full intersect calc. */
7997 /* Top or bottom edge? */
7998 if (new->y > old->y) {
7999 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8001 } else if (old->y > new->y) {
8002 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8003 size, old->y - new->y);
8006 /* Left or right edge - don't overlap any update calculated above. */
8007 if (new->x > old->x) {
8008 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8009 new->x - old->x, size - abs(new->y - old->y));
8011 } else if (old->x > new->x) {
8012 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8013 old->x - new->x, size - abs(new->y - old->y));
8020 /* Generate a series of frame coords from start->mid->finish.
8021 The movement rate doubles until the half way point is
8022 reached, then halves back down to the final destination,
8023 which gives a nice slow in/out effect. The algorithmn
8024 may seem to generate too many intermediates for short
8025 moves, but remember that the purpose is to attract the
8026 viewers attention to the piece about to be moved and
8027 then to where it ends up. Too few frames would be less
8031 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8033 int fraction, n, count;
8037 /* Slow in, stepping 1/16th, then 1/8th, ... */
8039 for (n = 0; n < factor; n++)
8041 for (n = 0; n < factor; n++) {
8042 frames[count].x = start->x + (mid->x - start->x) / fraction;
8043 frames[count].y = start->y + (mid->y - start->y) / fraction;
8045 fraction = fraction / 2;
8049 frames[count] = *mid;
8052 /* Slow out, stepping 1/2, then 1/4, ... */
8054 for (n = 0; n < factor; n++) {
8055 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8056 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8058 fraction = fraction * 2;
8063 /* Draw a piece on the screen without disturbing what's there */
8066 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8070 /* Bitmap for piece being moved. */
8071 if (appData.monoMode) {
8072 *mask = *pieceToSolid(piece);
8073 } else if (useImages) {
8075 *mask = xpmMask[piece];
8077 *mask = ximMaskPm[piece];
8080 *mask = *pieceToSolid(piece);
8083 /* GC for piece being moved. Square color doesn't matter, but
8084 since it gets modified we make a copy of the original. */
8086 if (appData.monoMode)
8091 if (appData.monoMode)
8096 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8098 /* Outline only used in mono mode and is not modified */
8100 *outline = bwPieceGC;
8102 *outline = wbPieceGC;
8106 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8111 /* Draw solid rectangle which will be clipped to shape of piece */
8112 XFillRectangle(xDisplay, dest, clip,
8113 0, 0, squareSize, squareSize);
8114 if (appData.monoMode)
8115 /* Also draw outline in contrasting color for black
8116 on black / white on white cases */
8117 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8118 0, 0, squareSize, squareSize, 0, 0, 1);
8120 /* Copy the piece */
8125 if(appData.upsideDown && flipView) kind ^= 2;
8126 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8128 0, 0, squareSize, squareSize,
8133 /* Animate the movement of a single piece */
8136 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8140 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8141 /* The old buffer is initialised with the start square (empty) */
8142 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8143 anim->prevFrame = *start;
8145 /* The piece will be drawn using its own bitmap as a matte */
8146 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8147 XSetClipMask(xDisplay, anim->pieceGC, mask);
8151 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8153 XRectangle updates[4];
8158 /* Save what we are about to draw into the new buffer */
8159 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8160 frame->x, frame->y, squareSize, squareSize,
8163 /* Erase bits of the previous frame */
8164 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8165 /* Where the new frame overlapped the previous,
8166 the contents in newBuf are wrong. */
8167 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8168 overlap.x, overlap.y,
8169 overlap.width, overlap.height,
8171 /* Repaint the areas in the old that don't overlap new */
8172 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8173 for (i = 0; i < count; i++)
8174 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8175 updates[i].x - anim->prevFrame.x,
8176 updates[i].y - anim->prevFrame.y,
8177 updates[i].width, updates[i].height,
8178 updates[i].x, updates[i].y);
8180 /* Easy when no overlap */
8181 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8182 0, 0, squareSize, squareSize,
8183 anim->prevFrame.x, anim->prevFrame.y);
8186 /* Save this frame for next time round */
8187 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8188 0, 0, squareSize, squareSize,
8190 anim->prevFrame = *frame;
8192 /* Draw piece over original screen contents, not current,
8193 and copy entire rect. Wipes out overlapping piece images. */
8194 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8195 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8196 0, 0, squareSize, squareSize,
8197 frame->x, frame->y);
8201 EndAnimation (AnimState *anim, XPoint *finish)
8203 XRectangle updates[4];
8208 /* The main code will redraw the final square, so we
8209 only need to erase the bits that don't overlap. */
8210 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8211 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8212 for (i = 0; i < count; i++)
8213 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8214 updates[i].x - anim->prevFrame.x,
8215 updates[i].y - anim->prevFrame.y,
8216 updates[i].width, updates[i].height,
8217 updates[i].x, updates[i].y);
8219 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8220 0, 0, squareSize, squareSize,
8221 anim->prevFrame.x, anim->prevFrame.y);
8226 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8230 BeginAnimation(anim, piece, startColor, start);
8231 for (n = 0; n < nFrames; n++) {
8232 AnimationFrame(anim, &(frames[n]), piece);
8233 FrameDelay(appData.animSpeed);
8235 EndAnimation(anim, finish);
8239 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8242 ChessSquare piece = board[fromY][toY];
8243 board[fromY][toY] = EmptySquare;
8244 DrawPosition(FALSE, board);
8246 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8247 y = lineGap + toY * (squareSize + lineGap);
8249 x = lineGap + toX * (squareSize + lineGap);
8250 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8252 for(i=1; i<4*kFactor; i++) {
8253 int r = squareSize * 9 * i/(20*kFactor - 5);
8254 XFillArc(xDisplay, xBoardWindow, highlineGC,
8255 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8256 FrameDelay(appData.animSpeed);
8258 board[fromY][toY] = piece;
8261 /* Main control logic for deciding what to animate and how */
8264 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8268 XPoint start, finish, mid;
8269 XPoint frames[kFactor * 2 + 1];
8270 int nFrames, startColor, endColor;
8272 /* Are we animating? */
8273 if (!appData.animate || appData.blindfold)
8276 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8277 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8278 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8280 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8281 piece = board[fromY][fromX];
8282 if (piece >= EmptySquare) return;
8287 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8290 ScreenSquare(fromX, fromY, &start, &startColor);
8291 ScreenSquare(toX, toY, &finish, &endColor);
8294 /* Knight: make straight movement then diagonal */
8295 if (abs(toY - fromY) < abs(toX - fromX)) {
8296 mid.x = start.x + (finish.x - start.x) / 2;
8300 mid.y = start.y + (finish.y - start.y) / 2;
8303 mid.x = start.x + (finish.x - start.x) / 2;
8304 mid.y = start.y + (finish.y - start.y) / 2;
8307 /* Don't use as many frames for very short moves */
8308 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8309 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8311 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8312 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8313 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8315 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8316 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8319 /* Be sure end square is redrawn */
8320 damage[0][toY][toX] = True;
8324 DragPieceBegin (int x, int y, Boolean instantly)
8326 int boardX, boardY, color;
8329 /* Are we animating? */
8330 if (!appData.animateDragging || appData.blindfold)
8333 /* Figure out which square we start in and the
8334 mouse position relative to top left corner. */
8335 BoardSquare(x, y, &boardX, &boardY);
8336 player.startBoardX = boardX;
8337 player.startBoardY = boardY;
8338 ScreenSquare(boardX, boardY, &corner, &color);
8339 player.startSquare = corner;
8340 player.startColor = color;
8341 /* As soon as we start dragging, the piece will jump slightly to
8342 be centered over the mouse pointer. */
8343 player.mouseDelta.x = squareSize/2;
8344 player.mouseDelta.y = squareSize/2;
8345 /* Initialise animation */
8346 player.dragPiece = PieceForSquare(boardX, boardY);
8348 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8349 player.dragActive = True;
8350 BeginAnimation(&player, player.dragPiece, color, &corner);
8351 /* Mark this square as needing to be redrawn. Note that
8352 we don't remove the piece though, since logically (ie
8353 as seen by opponent) the move hasn't been made yet. */
8354 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8355 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8356 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8357 corner.x, corner.y, squareSize, squareSize,
8358 0, 0); // [HGM] zh: unstack in stead of grab
8359 if(gatingPiece != EmptySquare) {
8360 /* Kludge alert: When gating we want the introduced
8361 piece to appear on the from square. To generate an
8362 image of it, we draw it on the board, copy the image,
8363 and draw the original piece again. */
8364 ChessSquare piece = boards[currentMove][boardY][boardX];
8365 DrawSquare(boardY, boardX, gatingPiece, 0);
8366 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8367 corner.x, corner.y, squareSize, squareSize, 0, 0);
8368 DrawSquare(boardY, boardX, piece, 0);
8370 damage[0][boardY][boardX] = True;
8372 player.dragActive = False;
8377 ChangeDragPiece (ChessSquare piece)
8380 player.dragPiece = piece;
8381 /* The piece will be drawn using its own bitmap as a matte */
8382 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8383 XSetClipMask(xDisplay, player.pieceGC, mask);
8387 DragPieceMove (int x, int y)
8391 /* Are we animating? */
8392 if (!appData.animateDragging || appData.blindfold)
8396 if (! player.dragActive)
8398 /* Move piece, maintaining same relative position
8399 of mouse within square */
8400 corner.x = x - player.mouseDelta.x;
8401 corner.y = y - player.mouseDelta.y;
8402 AnimationFrame(&player, &corner, player.dragPiece);
8404 if (appData.highlightDragging) {
8406 BoardSquare(x, y, &boardX, &boardY);
8407 SetHighlights(fromX, fromY, boardX, boardY);
8413 DragPieceEnd (int x, int y)
8415 int boardX, boardY, color;
8418 /* Are we animating? */
8419 if (!appData.animateDragging || appData.blindfold)
8423 if (! player.dragActive)
8425 /* Last frame in sequence is square piece is
8426 placed on, which may not match mouse exactly. */
8427 BoardSquare(x, y, &boardX, &boardY);
8428 ScreenSquare(boardX, boardY, &corner, &color);
8429 EndAnimation(&player, &corner);
8431 /* Be sure end square is redrawn */
8432 damage[0][boardY][boardX] = True;
8434 /* This prevents weird things happening with fast successive
8435 clicks which on my Sun at least can cause motion events
8436 without corresponding press/release. */
8437 player.dragActive = False;
8440 /* Handle expose event while piece being dragged */
8445 if (!player.dragActive || appData.blindfold)
8448 /* What we're doing: logically, the move hasn't been made yet,
8449 so the piece is still in it's original square. But visually
8450 it's being dragged around the board. So we erase the square
8451 that the piece is on and draw it at the last known drag point. */
8452 BlankSquare(player.startSquare.x, player.startSquare.y,
8453 player.startColor, EmptySquare, xBoardWindow, 1);
8454 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8455 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8458 #include <sys/ioctl.h>
8462 int fd, default_width;
8465 default_width = 79; // this is FICS default anyway...
8467 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8469 if (!ioctl(fd, TIOCGSIZE, &win))
8470 default_width = win.ts_cols;
8471 #elif defined(TIOCGWINSZ)
8473 if (!ioctl(fd, TIOCGWINSZ, &win))
8474 default_width = win.ws_col;
8476 return default_width;
8482 static int old_width = 0;
8483 int new_width = get_term_width();
8485 if (old_width != new_width)
8486 ics_printf("set width %d\n", new_width);
8487 old_width = new_width;
8491 NotifyFrontendLogin ()
8496 /* [AS] Arrow highlighting support */
8498 static double A_WIDTH = 5; /* Width of arrow body */
8500 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8501 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8512 return (int) (x + 0.5);
8516 SquareToPos (int rank, int file, int *x, int *y)
8519 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8520 *y = lineGap + rank * (squareSize + lineGap);
8522 *x = lineGap + file * (squareSize + lineGap);
8523 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8527 /* Draw an arrow between two points using current settings */
8529 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8532 double dx, dy, j, k, x, y;
8535 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8537 arrow[0].x = s_x + A_WIDTH + 0.5;
8540 arrow[1].x = s_x + A_WIDTH + 0.5;
8541 arrow[1].y = d_y - h;
8543 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8544 arrow[2].y = d_y - h;
8549 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8550 arrow[5].y = d_y - h;
8552 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8553 arrow[4].y = d_y - h;
8555 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8558 else if( d_y == s_y ) {
8559 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8562 arrow[0].y = s_y + A_WIDTH + 0.5;
8564 arrow[1].x = d_x - w;
8565 arrow[1].y = s_y + A_WIDTH + 0.5;
8567 arrow[2].x = d_x - w;
8568 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8573 arrow[5].x = d_x - w;
8574 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8576 arrow[4].x = d_x - w;
8577 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8580 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8583 /* [AS] Needed a lot of paper for this! :-) */
8584 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8585 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8587 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8589 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8594 arrow[0].x = Round(x - j);
8595 arrow[0].y = Round(y + j*dx);
8597 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8598 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8601 x = (double) d_x - k;
8602 y = (double) d_y - k*dy;
8605 x = (double) d_x + k;
8606 y = (double) d_y + k*dy;
8609 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8611 arrow[6].x = Round(x - j);
8612 arrow[6].y = Round(y + j*dx);
8614 arrow[2].x = Round(arrow[6].x + 2*j);
8615 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8617 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8618 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8623 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8624 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8627 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8628 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8629 // Polygon( hdc, arrow, 7 );
8633 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8636 hor = 64*s_col + 32; vert = 64*s_row + 32;
8637 for(i=0; i<= 64; i++) {
8638 damage[0][vert+6>>6][hor+6>>6] = True;
8639 damage[0][vert-6>>6][hor+6>>6] = True;
8640 damage[0][vert+6>>6][hor-6>>6] = True;
8641 damage[0][vert-6>>6][hor-6>>6] = True;
8642 hor += d_col - s_col; vert += d_row - s_row;
8646 /* [AS] Draw an arrow between two squares */
8648 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8650 int s_x, s_y, d_x, d_y;
8652 if( s_col == d_col && s_row == d_row ) {
8656 /* Get source and destination points */
8657 SquareToPos( s_row, s_col, &s_x, &s_y);
8658 SquareToPos( d_row, d_col, &d_x, &d_y);
8661 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8663 else if( d_y < s_y ) {
8664 d_y += squareSize / 2 + squareSize / 4;
8667 d_y += squareSize / 2;
8671 d_x += squareSize / 2 - squareSize / 4;
8673 else if( d_x < s_x ) {
8674 d_x += squareSize / 2 + squareSize / 4;
8677 d_x += squareSize / 2;
8680 s_x += squareSize / 2;
8681 s_y += squareSize / 2;
8684 A_WIDTH = squareSize / 14.; //[HGM] make float
8686 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8687 ArrowDamage(s_col, s_row, d_col, d_row);
8691 IsDrawArrowEnabled ()
8693 return appData.highlightMoveWithArrow && squareSize >= 32;
8697 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8699 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8700 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8704 UpdateLogos (int displ)
8706 return; // no logos in XBoard yet