2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
245 int main P((int argc, char **argv));
246 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
247 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
248 RETSIGTYPE CmailSigHandler P((int sig));
249 RETSIGTYPE IntSigHandler P((int sig));
250 RETSIGTYPE TermSizeSigHandler P((int sig));
251 void CreateGCs P((int redo));
252 void CreateAnyPieces P((void));
253 void CreateXIMPieces P((void));
254 void CreateXPMPieces P((void));
255 void CreateXPMBoard P((char *s, int n));
256 void CreatePieces P((void));
257 void CreatePieceMenus P((void));
258 Widget CreateMenuBar P((Menu *mb, int boardWidth));
259 Widget CreateButtonBar P ((MenuItem *mi));
261 char *InsertPxlSize P((char *pattern, int targetPxlSize));
262 XFontSet CreateFontSet P((char *base_fnt_lst));
264 char *FindFont P((char *pattern, int targetPxlSize));
266 void PieceMenuPopup P((Widget w, XEvent *event,
267 String *params, Cardinal *num_params));
268 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
269 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
270 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
271 u_int wreq, u_int hreq));
272 void CreateGrid P((void));
273 int EventToSquare P((int x, int limit));
274 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
275 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
276 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
277 void HandleUserMove P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AnimateUserMove P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void HandlePV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void SelectPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void StopPV P((Widget w, XEvent * event,
286 String * params, Cardinal * nParams));
287 void WhiteClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void BlackClock P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void DrawPositionProc P((Widget w, XEvent *event,
292 String *prms, Cardinal *nprms));
293 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
295 void CommentClick P((Widget w, XEvent * event,
296 String * params, Cardinal * nParams));
297 void CommentPopUp P((char *title, char *label));
298 void CommentPopDown P((void));
299 void ICSInputBoxPopUp P((void));
300 void ICSInputBoxPopDown P((void));
301 void FileNamePopUp P((char *label, char *def, char *filter,
302 FileProc proc, char *openMode));
303 void FileNamePopDown P((void));
304 void FileNameCallback P((Widget w, XtPointer client_data,
305 XtPointer call_data));
306 void FileNameAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionReplyAction P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionProc P((Widget w, XEvent *event,
311 String *prms, Cardinal *nprms));
312 void AskQuestionPopDown P((void));
313 void PromotionPopDown P((void));
314 void PromotionCallback P((Widget w, XtPointer client_data,
315 XtPointer call_data));
316 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
317 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
318 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
321 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
323 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
325 void LoadPositionProc P((Widget w, XEvent *event,
326 String *prms, Cardinal *nprms));
327 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
329 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
331 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
333 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
335 void PastePositionProc P((Widget w, XEvent *event, String *prms,
337 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
340 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
341 void SavePositionProc P((Widget w, XEvent *event,
342 String *prms, Cardinal *nprms));
343 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
344 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
346 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
347 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
350 void MachineWhiteProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeModeProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void AnalyzeFileProc P((Widget w, XEvent *event,
355 String *prms, Cardinal *nprms));
356 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
358 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void IcsClientProc P((Widget w, XEvent *event, String *prms,
362 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void EditPositionProc P((Widget w, XEvent *event,
364 String *prms, Cardinal *nprms));
365 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void EditCommentProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void IcsInputBoxProc P((Widget w, XEvent *event,
369 String *prms, Cardinal *nprms));
370 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void StopObservingProc P((Widget w, XEvent *event, String *prms,
387 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
389 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 Boolean TempBackwardActive = False;
395 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
396 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
401 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
403 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
404 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
406 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
408 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
410 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
412 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
413 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
415 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
416 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
418 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
420 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
422 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
424 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
425 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
427 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
429 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
431 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
433 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
436 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
438 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
440 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
442 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void DisplayMove P((int moveNumber));
453 void DisplayTitle P((char *title));
454 void ICSInitScript P((void));
455 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
456 void ErrorPopUp P((char *title, char *text, int modal));
457 void ErrorPopDown P((void));
458 static char *ExpandPathName P((char *path));
459 static void CreateAnimVars P((void));
460 static void DragPieceMove P((int x, int y));
461 static void DrawDragPiece P((void));
462 char *ModeToWidgetName P((GameMode mode));
463 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
477 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
478 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
479 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
480 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
481 void GameListOptionsPopDown P(());
482 void GenericPopDown P(());
483 void update_ics_width P(());
484 int get_term_width P(());
485 int CopyMemoProc P(());
486 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
487 Boolean IsDrawArrowEnabled P(());
490 * XBoard depends on Xt R4 or higher
492 int xtVersion = XtSpecificationRelease;
497 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
498 jailSquareColor, highlightSquareColor, premoveHighlightColor;
499 Pixel lowTimeWarningColor;
500 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
501 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
502 wjPieceGC, bjPieceGC, prelineGC, countGC;
503 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
504 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
505 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
506 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
507 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
508 ICSInputShell, fileNameShell, askQuestionShell;
509 Widget historyShell, evalGraphShell, gameListShell;
510 int hOffset; // [HGM] dual
511 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
512 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
513 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
515 XFontSet fontSet, clockFontSet;
518 XFontStruct *clockFontStruct;
520 Font coordFontID, countFontID;
521 XFontStruct *coordFontStruct, *countFontStruct;
522 XtAppContext appContext;
524 char *oldICSInteractionTitle;
528 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
530 Position commentX = -1, commentY = -1;
531 Dimension commentW, commentH;
532 typedef unsigned int BoardSize;
534 Boolean chessProgram;
536 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
537 int squareSize, smallLayout = 0, tinyLayout = 0,
538 marginW, marginH, // [HGM] for run-time resizing
539 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
540 ICSInputBoxUp = False, askQuestionUp = False,
541 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
542 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
543 Dimension textHeight;
544 Pixel timerForegroundPixel, timerBackgroundPixel;
545 Pixel buttonForegroundPixel, buttonBackgroundPixel;
546 char *chessDir, *programName, *programVersion,
547 *gameCopyFilename, *gamePasteFilename;
548 Boolean alwaysOnTop = False;
549 Boolean saveSettingsOnExit;
550 char *settingsFileName;
551 char *icsTextMenuString;
553 char *firstChessProgramNames;
554 char *secondChessProgramNames;
556 WindowPlacement wpMain;
557 WindowPlacement wpConsole;
558 WindowPlacement wpComment;
559 WindowPlacement wpMoveHistory;
560 WindowPlacement wpEvalGraph;
561 WindowPlacement wpEngineOutput;
562 WindowPlacement wpGameList;
563 WindowPlacement wpTags;
565 extern Widget shells[];
566 extern Boolean shellUp[];
570 Pixmap pieceBitmap[2][(int)BlackPawn];
571 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
572 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
573 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
574 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
575 Pixmap xpmBoardBitmap[2];
576 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
577 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
578 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
579 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
580 XImage *ximLightSquare, *ximDarkSquare;
583 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
584 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
586 #define White(piece) ((int)(piece) < (int)BlackPawn)
588 /* Variables for doing smooth animation. This whole thing
589 would be much easier if the board was double-buffered,
590 but that would require a fairly major rewrite. */
595 GC blitGC, pieceGC, outlineGC;
596 XPoint startSquare, prevFrame, mouseDelta;
600 int startBoardX, startBoardY;
603 /* There can be two pieces being animated at once: a player
604 can begin dragging a piece before the remote opponent has moved. */
606 static AnimState game, player;
608 /* Bitmaps for use as masks when drawing XPM pieces.
609 Need one for each black and white piece. */
610 static Pixmap xpmMask[BlackKing + 1];
612 /* This magic number is the number of intermediate frames used
613 in each half of the animation. For short moves it's reduced
614 by 1. The total number of frames will be factor * 2 + 1. */
617 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
619 MenuItem fileMenu[] = {
620 {N_("New Game Ctrl+N"), "New Game", ResetProc},
621 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
622 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
623 {"----", NULL, NothingProc},
624 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
625 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
626 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
627 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
628 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
629 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
630 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
631 {"----", NULL, NothingProc},
632 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
633 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
634 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
635 {"----", NULL, NothingProc},
636 {N_("Mail Move"), "Mail Move", MailMoveProc},
637 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
638 {"----", NULL, NothingProc},
639 {N_("Quit Ctr+Q"), "Exit", QuitProc},
643 MenuItem editMenu[] = {
644 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
645 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
646 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
647 {"----", NULL, NothingProc},
648 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
649 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
650 {"----", NULL, NothingProc},
651 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
652 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
653 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
654 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
655 {N_("Edit Book"), "Edit Book", EditBookProc},
656 {"----", NULL, NothingProc},
657 {N_("Revert Home"), "Revert", RevertProc},
658 {N_("Annotate"), "Annotate", AnnotateProc},
659 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
660 {"----", NULL, NothingProc},
661 {N_("Backward Alt+Left"), "Backward", BackwardProc},
662 {N_("Forward Alt+Right"), "Forward", ForwardProc},
663 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
664 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
668 MenuItem viewMenu[] = {
669 {N_("Flip View F2"), "Flip View", FlipViewProc},
670 {"----", NULL, NothingProc},
671 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
672 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
673 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
674 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
675 {N_("ICS text menu"), "ICStex", IcsTextProc},
676 {"----", NULL, NothingProc},
677 {N_("Tags"), "Show Tags", EditTagsProc},
678 {N_("Comments"), "Show Comments", EditCommentProc},
679 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
680 {"----", NULL, NothingProc},
681 {N_("Board..."), "Board Options", BoardOptionsProc},
682 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
686 MenuItem modeMenu[] = {
687 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
688 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
689 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
690 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
691 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
692 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
693 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
694 {N_("Training"), "Training", TrainingProc},
695 {N_("ICS Client"), "ICS Client", IcsClientProc},
696 {"----", NULL, NothingProc},
697 {N_("Machine Match"), "Machine Match", MatchProc},
698 {N_("Pause Pause"), "Pause", PauseProc},
702 MenuItem actionMenu[] = {
703 {N_("Accept F3"), "Accept", AcceptProc},
704 {N_("Decline F4"), "Decline", DeclineProc},
705 {N_("Rematch F12"), "Rematch", RematchProc},
706 {"----", NULL, NothingProc},
707 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
708 {N_("Draw F6"), "Draw", DrawProc},
709 {N_("Adjourn F7"), "Adjourn", AdjournProc},
710 {N_("Abort F8"),"Abort", AbortProc},
711 {N_("Resign F9"), "Resign", ResignProc},
712 {"----", NULL, NothingProc},
713 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
714 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
715 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
716 {"----", NULL, NothingProc},
717 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
718 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
719 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
723 MenuItem engineMenu[] = {
724 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
725 {"----", NULL, NothingProc},
726 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
727 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
728 {"----", NULL, NothingProc},
729 {N_("Hint"), "Hint", HintProc},
730 {N_("Book"), "Book", BookProc},
731 {"----", NULL, NothingProc},
732 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
733 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
737 MenuItem optionsMenu[] = {
738 #define OPTIONSDIALOG
740 {N_("General ..."), "General", OptionsProc},
742 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
743 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
744 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
745 {N_("ICS ..."), "ICS", IcsOptionsProc},
746 {N_("Match ..."), "Match", MatchOptionsProc},
747 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
748 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
749 // {N_(" ..."), "", OptionsProc},
750 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
751 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
752 {"----", NULL, NothingProc},
753 #ifndef OPTIONSDIALOG
754 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
755 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
756 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
757 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
758 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
759 {N_("Blindfold"), "Blindfold", BlindfoldProc},
760 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
762 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
764 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
765 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
766 {N_("Move Sound"), "Move Sound", MoveSoundProc},
767 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
768 {N_("One-Click Moving"), "OneClick", OneClickProc},
769 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
770 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
771 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
772 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
773 // {N_("Premove"), "Premove", PremoveProc},
774 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
775 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
776 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
777 {"----", NULL, NothingProc},
779 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
780 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
784 MenuItem helpMenu[] = {
785 {N_("Info XBoard"), "Info XBoard", InfoProc},
786 {N_("Man XBoard F1"), "Man XBoard", ManProc},
787 {"----", NULL, NothingProc},
788 {N_("About XBoard"), "About XBoard", AboutProc},
793 {N_("File"), "File", fileMenu},
794 {N_("Edit"), "Edit", editMenu},
795 {N_("View"), "View", viewMenu},
796 {N_("Mode"), "Mode", modeMenu},
797 {N_("Action"), "Action", actionMenu},
798 {N_("Engine"), "Engine", engineMenu},
799 {N_("Options"), "Options", optionsMenu},
800 {N_("Help"), "Help", helpMenu},
804 #define PAUSE_BUTTON "P"
805 MenuItem buttonBar[] = {
806 {"<<", "<<", ToStartProc},
807 {"<", "<", BackwardProc},
808 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
809 {">", ">", ForwardProc},
810 {">>", ">>", ToEndProc},
814 #define PIECE_MENU_SIZE 18
815 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
816 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
817 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
818 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
819 N_("Empty square"), N_("Clear board") },
820 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
821 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
822 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
823 N_("Empty square"), N_("Clear board") }
825 /* must be in same order as pieceMenuStrings! */
826 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
827 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
828 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
829 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
830 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
831 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
832 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
833 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
834 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
837 #define DROP_MENU_SIZE 6
838 String dropMenuStrings[DROP_MENU_SIZE] = {
839 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
841 /* must be in same order as dropMenuStrings! */
842 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
843 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
844 WhiteRook, WhiteQueen
852 DropMenuEnables dmEnables[] = {
870 { XtNborderWidth, 0 },
871 { XtNdefaultDistance, 0 },
875 { XtNborderWidth, 0 },
876 { XtNresizable, (XtArgVal) True },
880 { XtNborderWidth, 0 },
886 { XtNjustify, (XtArgVal) XtJustifyRight },
887 { XtNlabel, (XtArgVal) "..." },
888 { XtNresizable, (XtArgVal) True },
889 { XtNresize, (XtArgVal) False }
892 Arg messageArgs[] = {
893 { XtNjustify, (XtArgVal) XtJustifyLeft },
894 { XtNlabel, (XtArgVal) "..." },
895 { XtNresizable, (XtArgVal) True },
896 { XtNresize, (XtArgVal) False }
900 { XtNborderWidth, 0 },
901 { XtNjustify, (XtArgVal) XtJustifyLeft }
904 XtResource clientResources[] = {
905 { "flashCount", "flashCount", XtRInt, sizeof(int),
906 XtOffset(AppDataPtr, flashCount), XtRImmediate,
907 (XtPointer) FLASH_COUNT },
910 XrmOptionDescRec shellOptions[] = {
911 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
912 { "-flash", "flashCount", XrmoptionNoArg, "3" },
913 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
916 XtActionsRec boardActions[] = {
917 { "DrawPosition", DrawPositionProc },
918 { "HandleUserMove", HandleUserMove },
919 { "AnimateUserMove", AnimateUserMove },
920 { "HandlePV", HandlePV },
921 { "SelectPV", SelectPV },
922 { "StopPV", StopPV },
923 { "FileNameAction", FileNameAction },
924 { "AskQuestionProc", AskQuestionProc },
925 { "AskQuestionReplyAction", AskQuestionReplyAction },
926 { "PieceMenuPopup", PieceMenuPopup },
927 { "WhiteClock", WhiteClock },
928 { "BlackClock", BlackClock },
929 { "ResetProc", ResetProc },
930 { "NewVariantProc", NewVariantProc },
931 { "LoadGameProc", LoadGameProc },
932 { "LoadNextGameProc", LoadNextGameProc },
933 { "LoadPrevGameProc", LoadPrevGameProc },
934 { "LoadSelectedProc", LoadSelectedProc },
935 { "SetFilterProc", SetFilterProc },
936 { "ReloadGameProc", ReloadGameProc },
937 { "LoadPositionProc", LoadPositionProc },
938 { "LoadNextPositionProc", LoadNextPositionProc },
939 { "LoadPrevPositionProc", LoadPrevPositionProc },
940 { "ReloadPositionProc", ReloadPositionProc },
941 { "CopyPositionProc", CopyPositionProc },
942 { "PastePositionProc", PastePositionProc },
943 { "CopyGameProc", CopyGameProc },
944 { "CopyGameListProc", CopyGameListProc },
945 { "PasteGameProc", PasteGameProc },
946 { "SaveGameProc", SaveGameProc },
947 { "SavePositionProc", SavePositionProc },
948 { "MailMoveProc", MailMoveProc },
949 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
950 { "QuitProc", QuitProc },
951 { "MachineWhiteProc", MachineWhiteProc },
952 { "MachineBlackProc", MachineBlackProc },
953 { "AnalysisModeProc", AnalyzeModeProc },
954 { "AnalyzeFileProc", AnalyzeFileProc },
955 { "TwoMachinesProc", TwoMachinesProc },
956 { "IcsClientProc", IcsClientProc },
957 { "EditGameProc", EditGameProc },
958 { "EditPositionProc", EditPositionProc },
959 { "TrainingProc", EditPositionProc },
960 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
961 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
962 { "ShowGameListProc", ShowGameListProc },
963 { "ShowMoveListProc", HistoryShowProc},
964 { "EditTagsProc", EditTagsProc },
965 { "EditBookProc", EditBookProc },
966 { "EditCommentProc", EditCommentProc },
967 { "IcsInputBoxProc", IcsInputBoxProc },
968 { "PauseProc", PauseProc },
969 { "AcceptProc", AcceptProc },
970 { "DeclineProc", DeclineProc },
971 { "RematchProc", RematchProc },
972 { "CallFlagProc", CallFlagProc },
973 { "DrawProc", DrawProc },
974 { "AdjournProc", AdjournProc },
975 { "AbortProc", AbortProc },
976 { "ResignProc", ResignProc },
977 { "AdjuWhiteProc", AdjuWhiteProc },
978 { "AdjuBlackProc", AdjuBlackProc },
979 { "AdjuDrawProc", AdjuDrawProc },
980 { "TypeInProc", TypeInProc },
981 { "EnterKeyProc", EnterKeyProc },
982 { "UpKeyProc", UpKeyProc },
983 { "DownKeyProc", DownKeyProc },
984 { "StopObservingProc", StopObservingProc },
985 { "StopExaminingProc", StopExaminingProc },
986 { "UploadProc", UploadProc },
987 { "BackwardProc", BackwardProc },
988 { "ForwardProc", ForwardProc },
989 { "TempBackwardProc", TempBackwardProc },
990 { "TempForwardProc", TempForwardProc },
991 { "ToStartProc", ToStartProc },
992 { "ToEndProc", ToEndProc },
993 { "RevertProc", RevertProc },
994 { "AnnotateProc", AnnotateProc },
995 { "TruncateGameProc", TruncateGameProc },
996 { "MoveNowProc", MoveNowProc },
997 { "RetractMoveProc", RetractMoveProc },
998 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
999 { "UciMenuProc", (XtActionProc) UciMenuProc },
1000 { "TimeControlProc", (XtActionProc) TimeControlProc },
1001 { "FlipViewProc", FlipViewProc },
1002 { "PonderNextMoveProc", PonderNextMoveProc },
1003 #ifndef OPTIONSDIALOG
1004 { "AlwaysQueenProc", AlwaysQueenProc },
1005 { "AnimateDraggingProc", AnimateDraggingProc },
1006 { "AnimateMovingProc", AnimateMovingProc },
1007 { "AutoflagProc", AutoflagProc },
1008 { "AutoflipProc", AutoflipProc },
1009 { "BlindfoldProc", BlindfoldProc },
1010 { "FlashMovesProc", FlashMovesProc },
1012 { "HighlightDraggingProc", HighlightDraggingProc },
1014 { "HighlightLastMoveProc", HighlightLastMoveProc },
1015 // { "IcsAlarmProc", IcsAlarmProc },
1016 { "MoveSoundProc", MoveSoundProc },
1017 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1018 { "PopupExitMessageProc", PopupExitMessageProc },
1019 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1020 // { "PremoveProc", PremoveProc },
1021 { "ShowCoordsProc", ShowCoordsProc },
1022 { "ShowThinkingProc", ShowThinkingProc },
1023 { "HideThinkingProc", HideThinkingProc },
1024 { "TestLegalityProc", TestLegalityProc },
1026 { "SaveSettingsProc", SaveSettingsProc },
1027 { "SaveOnExitProc", SaveOnExitProc },
1028 { "InfoProc", InfoProc },
1029 { "ManProc", ManProc },
1030 { "HintProc", HintProc },
1031 { "BookProc", BookProc },
1032 { "AboutGameProc", AboutGameProc },
1033 { "AboutProc", AboutProc },
1034 { "DebugProc", DebugProc },
1035 { "NothingProc", NothingProc },
1036 { "CommentClick", (XtActionProc) CommentClick },
1037 { "CommentPopDown", (XtActionProc) CommentPopDown },
1038 { "TagsPopDown", (XtActionProc) TagsPopDown },
1039 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1040 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1041 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1042 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1043 { "GameListPopDown", (XtActionProc) GameListPopDown },
1044 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1045 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1046 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1047 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1048 { "GenericPopDown", (XtActionProc) GenericPopDown },
1049 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1050 { "SelectMove", (XtActionProc) SelectMove },
1053 char globalTranslations[] =
1054 ":<Key>F9: ResignProc() \n \
1055 :Ctrl<Key>n: ResetProc() \n \
1056 :Meta<Key>V: NewVariantProc() \n \
1057 :Ctrl<Key>o: LoadGameProc() \n \
1058 :Meta<Key>Next: LoadNextGameProc() \n \
1059 :Meta<Key>Prior: LoadPrevGameProc() \n \
1060 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1061 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1062 :Ctrl<Key>s: SaveGameProc() \n \
1063 :Ctrl<Key>c: CopyGameProc() \n \
1064 :Ctrl<Key>v: PasteGameProc() \n \
1065 :Ctrl<Key>O: LoadPositionProc() \n \
1066 :Shift<Key>Next: LoadNextPositionProc() \n \
1067 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1068 :Ctrl<Key>S: SavePositionProc() \n \
1069 :Ctrl<Key>C: CopyPositionProc() \n \
1070 :Ctrl<Key>V: PastePositionProc() \n \
1071 :Ctrl<Key>q: QuitProc() \n \
1072 :Ctrl<Key>w: MachineWhiteProc() \n \
1073 :Ctrl<Key>b: MachineBlackProc() \n \
1074 :Ctrl<Key>t: TwoMachinesProc() \n \
1075 :Ctrl<Key>a: AnalysisModeProc() \n \
1076 :Ctrl<Key>g: AnalyzeFileProc() \n \
1077 :Ctrl<Key>e: EditGameProc() \n \
1078 :Ctrl<Key>E: EditPositionProc() \n \
1079 :Meta<Key>O: EngineOutputProc() \n \
1080 :Meta<Key>E: EvalGraphProc() \n \
1081 :Meta<Key>G: ShowGameListProc() \n \
1082 :Meta<Key>H: ShowMoveListProc() \n \
1083 :<Key>Pause: PauseProc() \n \
1084 :<Key>F3: AcceptProc() \n \
1085 :<Key>F4: DeclineProc() \n \
1086 :<Key>F12: RematchProc() \n \
1087 :<Key>F5: CallFlagProc() \n \
1088 :<Key>F6: DrawProc() \n \
1089 :<Key>F7: AdjournProc() \n \
1090 :<Key>F8: AbortProc() \n \
1091 :<Key>F10: StopObservingProc() \n \
1092 :<Key>F11: StopExaminingProc() \n \
1093 :Meta Ctrl<Key>F12: DebugProc() \n \
1094 :Meta<Key>End: ToEndProc() \n \
1095 :Meta<Key>Right: ForwardProc() \n \
1096 :Meta<Key>Home: ToStartProc() \n \
1097 :Meta<Key>Left: BackwardProc() \n \
1098 :<Key>Left: BackwardProc() \n \
1099 :<Key>Right: ForwardProc() \n \
1100 :<Key>Home: RevertProc() \n \
1101 :<Key>End: TruncateGameProc() \n \
1102 :Ctrl<Key>m: MoveNowProc() \n \
1103 :Ctrl<Key>x: RetractMoveProc() \n \
1104 :Meta<Key>J: EngineMenuProc() \n \
1105 :Meta<Key>U: UciMenuProc() \n \
1106 :Meta<Key>T: TimeControlProc() \n \
1107 :Ctrl<Key>P: PonderNextMoveProc() \n "
1108 #ifndef OPTIONSDIALOG
1110 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1111 :Ctrl<Key>F: AutoflagProc() \n \
1112 :Ctrl<Key>A: AnimateMovingProc() \n \
1113 :Ctrl<Key>L: TestLegalityProc() \n \
1114 :Ctrl<Key>H: HideThinkingProc() \n "
1117 :<Key>F1: ManProc() \n \
1118 :<Key>F2: FlipViewProc() \n \
1119 :<KeyDown>Return: TempBackwardProc() \n \
1120 :<KeyUp>Return: TempForwardProc() \n";
1122 char boardTranslations[] =
1123 "<Btn1Down>: HandleUserMove(0) \n \
1124 Shift<Btn1Up>: HandleUserMove(1) \n \
1125 <Btn1Up>: HandleUserMove(0) \n \
1126 <Btn1Motion>: AnimateUserMove() \n \
1127 <Btn3Motion>: HandlePV() \n \
1128 <Btn3Up>: PieceMenuPopup(menuB) \n \
1129 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1130 PieceMenuPopup(menuB) \n \
1131 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1132 PieceMenuPopup(menuW) \n \
1133 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1134 PieceMenuPopup(menuW) \n \
1135 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1136 PieceMenuPopup(menuB) \n";
1138 char whiteTranslations[] =
1139 "Shift<BtnDown>: WhiteClock(1)\n \
1140 <BtnDown>: WhiteClock(0)\n";
1141 char blackTranslations[] =
1142 "Shift<BtnDown>: BlackClock(1)\n \
1143 <BtnDown>: BlackClock(0)\n";
1145 char ICSInputTranslations[] =
1146 "<Key>Up: UpKeyProc() \n "
1147 "<Key>Down: DownKeyProc() \n "
1148 "<Key>Return: EnterKeyProc() \n";
1150 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1151 // as the widget is destroyed before the up-click can call extend-end
1152 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1154 String xboardResources[] = {
1155 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1156 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1157 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1162 /* Max possible square size */
1163 #define MAXSQSIZE 256
1165 static int xpm_avail[MAXSQSIZE];
1167 #ifdef HAVE_DIR_STRUCT
1169 /* Extract piece size from filename */
1171 xpm_getsize (char *name, int len, char *ext)
1179 if ((p=strchr(name, '.')) == NULL ||
1180 StrCaseCmp(p+1, ext) != 0)
1186 while (*p && isdigit(*p))
1193 /* Setup xpm_avail */
1195 xpm_getavail (char *dirname, char *ext)
1201 for (i=0; i<MAXSQSIZE; ++i)
1204 if (appData.debugMode)
1205 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1207 dir = opendir(dirname);
1210 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1211 programName, dirname);
1215 while ((ent=readdir(dir)) != NULL) {
1216 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1217 if (i > 0 && i < MAXSQSIZE)
1227 xpm_print_avail (FILE *fp, char *ext)
1231 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1232 for (i=1; i<MAXSQSIZE; ++i) {
1238 /* Return XPM piecesize closest to size */
1240 xpm_closest_to (char *dirname, int size, char *ext)
1243 int sm_diff = MAXSQSIZE;
1247 xpm_getavail(dirname, ext);
1249 if (appData.debugMode)
1250 xpm_print_avail(stderr, ext);
1252 for (i=1; i<MAXSQSIZE; ++i) {
1255 diff = (diff<0) ? -diff : diff;
1256 if (diff < sm_diff) {
1264 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1270 #else /* !HAVE_DIR_STRUCT */
1271 /* If we are on a system without a DIR struct, we can't
1272 read the directory, so we can't collect a list of
1273 filenames, etc., so we can't do any size-fitting. */
1275 xpm_closest_to (char *dirname, int size, char *ext)
1277 fprintf(stderr, _("\
1278 Warning: No DIR structure found on this system --\n\
1279 Unable to autosize for XPM/XIM pieces.\n\
1280 Please report this error to %s.\n\
1281 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1284 #endif /* HAVE_DIR_STRUCT */
1286 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1287 "magenta", "cyan", "white" };
1291 TextColors textColors[(int)NColorClasses];
1293 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1295 parse_color (char *str, int which)
1297 char *p, buf[100], *d;
1300 if (strlen(str) > 99) /* watch bounds on buf */
1305 for (i=0; i<which; ++i) {
1312 /* Could be looking at something like:
1314 .. in which case we want to stop on a comma also */
1315 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1319 return -1; /* Use default for empty field */
1322 if (which == 2 || isdigit(*p))
1325 while (*p && isalpha(*p))
1330 for (i=0; i<8; ++i) {
1331 if (!StrCaseCmp(buf, cnames[i]))
1332 return which? (i+40) : (i+30);
1334 if (!StrCaseCmp(buf, "default")) return -1;
1336 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1341 parse_cpair (ColorClass cc, char *str)
1343 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1344 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1349 /* bg and attr are optional */
1350 textColors[(int)cc].bg = parse_color(str, 1);
1351 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1352 textColors[(int)cc].attr = 0;
1358 /* Arrange to catch delete-window events */
1359 Atom wm_delete_window;
1361 CatchDeleteWindow (Widget w, String procname)
1364 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1365 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1366 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1373 XtSetArg(args[0], XtNiconic, False);
1374 XtSetValues(shellWidget, args, 1);
1376 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1379 //---------------------------------------------------------------------------------------------------------
1380 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1383 #define CW_USEDEFAULT (1<<31)
1384 #define ICS_TEXT_MENU_SIZE 90
1385 #define DEBUG_FILE "xboard.debug"
1386 #define SetCurrentDirectory chdir
1387 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1391 // these two must some day move to frontend.h, when they are implemented
1392 Boolean GameListIsUp();
1394 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1397 // front-end part of option handling
1399 // [HGM] This platform-dependent table provides the location for storing the color info
1400 extern char *crWhite, * crBlack;
1404 &appData.whitePieceColor,
1405 &appData.blackPieceColor,
1406 &appData.lightSquareColor,
1407 &appData.darkSquareColor,
1408 &appData.highlightSquareColor,
1409 &appData.premoveHighlightColor,
1410 &appData.lowTimeWarningColor,
1421 // [HGM] font: keep a font for each square size, even non-stndard ones
1422 #define NUM_SIZES 18
1423 #define MAX_SIZE 130
1424 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1425 char *fontTable[NUM_FONTS][MAX_SIZE];
1428 ParseFont (char *name, int number)
1429 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1431 if(sscanf(name, "size%d:", &size)) {
1432 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1433 // defer processing it until we know if it matches our board size
1434 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1435 fontTable[number][size] = strdup(strchr(name, ':')+1);
1436 fontValid[number][size] = True;
1441 case 0: // CLOCK_FONT
1442 appData.clockFont = strdup(name);
1444 case 1: // MESSAGE_FONT
1445 appData.font = strdup(name);
1447 case 2: // COORD_FONT
1448 appData.coordFont = strdup(name);
1453 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1458 { // only 2 fonts currently
1459 appData.clockFont = CLOCK_FONT_NAME;
1460 appData.coordFont = COORD_FONT_NAME;
1461 appData.font = DEFAULT_FONT_NAME;
1466 { // no-op, until we identify the code for this already in XBoard and move it here
1470 ParseColor (int n, char *name)
1471 { // in XBoard, just copy the color-name string
1472 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1476 ParseTextAttribs (ColorClass cc, char *s)
1478 (&appData.colorShout)[cc] = strdup(s);
1482 ParseBoardSize (void *addr, char *name)
1484 appData.boardSize = strdup(name);
1489 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1493 SetCommPortDefaults ()
1494 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1497 // [HGM] args: these three cases taken out to stay in front-end
1499 SaveFontArg (FILE *f, ArgDescriptor *ad)
1502 int i, n = (int)(intptr_t)ad->argLoc;
1504 case 0: // CLOCK_FONT
1505 name = appData.clockFont;
1507 case 1: // MESSAGE_FONT
1508 name = appData.font;
1510 case 2: // COORD_FONT
1511 name = appData.coordFont;
1516 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1517 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1518 fontTable[n][squareSize] = strdup(name);
1519 fontValid[n][squareSize] = True;
1522 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1523 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1528 { // nothing to do, as the sounds are at all times represented by their text-string names already
1532 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1533 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1534 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1538 SaveColor (FILE *f, ArgDescriptor *ad)
1539 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1540 if(colorVariable[(int)(intptr_t)ad->argLoc])
1541 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1545 SaveBoardSize (FILE *f, char *name, void *addr)
1546 { // wrapper to shield back-end from BoardSize & sizeInfo
1547 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1551 ParseCommPortSettings (char *s)
1552 { // no such option in XBoard (yet)
1555 extern Widget engineOutputShell;
1558 GetActualPlacement (Widget wg, WindowPlacement *wp)
1568 XtSetArg(args[i], XtNx, &x); i++;
1569 XtSetArg(args[i], XtNy, &y); i++;
1570 XtSetArg(args[i], XtNwidth, &w); i++;
1571 XtSetArg(args[i], XtNheight, &h); i++;
1572 XtGetValues(wg, args, i);
1581 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1582 // In XBoard this will have to wait until awareness of window parameters is implemented
1583 GetActualPlacement(shellWidget, &wpMain);
1584 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1585 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1586 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1587 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1588 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1589 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1593 PrintCommPortSettings (FILE *f, char *name)
1594 { // This option does not exist in XBoard
1598 MySearchPath (char *installDir, char *name, char *fullname)
1599 { // just append installDir and name. Perhaps ExpandPath should be used here?
1600 name = ExpandPathName(name);
1601 if(name && name[0] == '/')
1602 safeStrCpy(fullname, name, MSG_SIZ );
1604 sprintf(fullname, "%s%c%s", installDir, '/', name);
1610 MyGetFullPathName (char *name, char *fullname)
1611 { // should use ExpandPath?
1612 name = ExpandPathName(name);
1613 safeStrCpy(fullname, name, MSG_SIZ );
1618 EnsureOnScreen (int *x, int *y, int minX, int minY)
1625 { // [HGM] args: allows testing if main window is realized from back-end
1626 return xBoardWindow != 0;
1630 PopUpStartupDialog ()
1631 { // start menu not implemented in XBoard
1635 ConvertToLine (int argc, char **argv)
1637 static char line[128*1024], buf[1024];
1641 for(i=1; i<argc; i++)
1643 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1644 && argv[i][0] != '{' )
1645 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1647 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1648 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1651 line[strlen(line)-1] = NULLCHAR;
1655 //--------------------------------------------------------------------------------------------
1657 extern Boolean twoBoards, partnerUp;
1660 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1662 #define BoardSize int
1664 InitDrawingSizes (BoardSize boardSize, int flags)
1665 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1666 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1668 XtGeometryResult gres;
1670 static Dimension oldWidth, oldHeight;
1671 static VariantClass oldVariant;
1672 static int oldDual = -1, oldMono = -1;
1674 if(!formWidget) return;
1676 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1677 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1678 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1680 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1682 * Enable shell resizing.
1684 shellArgs[0].value = (XtArgVal) &w;
1685 shellArgs[1].value = (XtArgVal) &h;
1686 XtGetValues(shellWidget, shellArgs, 2);
1688 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1689 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1690 XtSetValues(shellWidget, &shellArgs[2], 4);
1692 XtSetArg(args[0], XtNdefaultDistance, &sep);
1693 XtGetValues(formWidget, args, 1);
1695 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1697 hOffset = boardWidth + 10;
1698 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1699 secondSegments[i] = gridSegments[i];
1700 secondSegments[i].x1 += hOffset;
1701 secondSegments[i].x2 += hOffset;
1704 XtSetArg(args[0], XtNwidth, boardWidth);
1705 XtSetArg(args[1], XtNheight, boardHeight);
1706 XtSetValues(boardWidget, args, 2);
1708 timerWidth = (boardWidth - sep) / 2;
1709 XtSetArg(args[0], XtNwidth, timerWidth);
1710 XtSetValues(whiteTimerWidget, args, 1);
1711 XtSetValues(blackTimerWidget, args, 1);
1713 XawFormDoLayout(formWidget, False);
1715 if (appData.titleInWindow) {
1717 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1718 XtSetArg(args[i], XtNheight, &h); i++;
1719 XtGetValues(titleWidget, args, i);
1721 w = boardWidth - 2*bor;
1723 XtSetArg(args[0], XtNwidth, &w);
1724 XtGetValues(menuBarWidget, args, 1);
1725 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1728 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1729 if (gres != XtGeometryYes && appData.debugMode) {
1731 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1732 programName, gres, w, h, wr, hr);
1736 XawFormDoLayout(formWidget, True);
1739 * Inhibit shell resizing.
1741 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1742 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1743 shellArgs[4].value = shellArgs[2].value = w;
1744 shellArgs[5].value = shellArgs[3].value = h;
1745 XtSetValues(shellWidget, &shellArgs[0], 6);
1748 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1751 if(gameInfo.variant != oldVariant) { // and only if variant changed
1754 for(i=0; i<4; i++) {
1756 for(p=0; p<=(int)WhiteKing; p++)
1757 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1758 if(gameInfo.variant == VariantShogi) {
1759 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1760 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1761 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1762 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1763 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1766 if(gameInfo.variant == VariantGothic) {
1767 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1770 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1771 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1772 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1775 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1776 for(p=0; p<=(int)WhiteKing; p++)
1777 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1778 if(gameInfo.variant == VariantShogi) {
1779 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1780 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1781 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1782 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1783 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1786 if(gameInfo.variant == VariantGothic) {
1787 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1790 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1791 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1792 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1797 for(i=0; i<2; i++) {
1799 for(p=0; p<=(int)WhiteKing; p++)
1800 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1801 if(gameInfo.variant == VariantShogi) {
1802 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1803 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1804 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1805 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1806 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1809 if(gameInfo.variant == VariantGothic) {
1810 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1813 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1814 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1815 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1821 if(appData.monoMode != oldMono)
1824 oldMono = appData.monoMode;
1829 ParseIcsTextColors ()
1830 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1831 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1832 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1833 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1834 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1835 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1836 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1837 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1838 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1839 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1840 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1842 if (appData.colorize) {
1844 _("%s: can't parse color names; disabling colorization\n"),
1847 appData.colorize = FALSE;
1853 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1854 XrmValue vFrom, vTo;
1855 int forceMono = False;
1857 if (!appData.monoMode) {
1858 vFrom.addr = (caddr_t) appData.lightSquareColor;
1859 vFrom.size = strlen(appData.lightSquareColor);
1860 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1861 if (vTo.addr == NULL) {
1862 appData.monoMode = True;
1865 lightSquareColor = *(Pixel *) vTo.addr;
1868 if (!appData.monoMode) {
1869 vFrom.addr = (caddr_t) appData.darkSquareColor;
1870 vFrom.size = strlen(appData.darkSquareColor);
1871 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1872 if (vTo.addr == NULL) {
1873 appData.monoMode = True;
1876 darkSquareColor = *(Pixel *) vTo.addr;
1879 if (!appData.monoMode) {
1880 vFrom.addr = (caddr_t) appData.whitePieceColor;
1881 vFrom.size = strlen(appData.whitePieceColor);
1882 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1883 if (vTo.addr == NULL) {
1884 appData.monoMode = True;
1887 whitePieceColor = *(Pixel *) vTo.addr;
1890 if (!appData.monoMode) {
1891 vFrom.addr = (caddr_t) appData.blackPieceColor;
1892 vFrom.size = strlen(appData.blackPieceColor);
1893 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1894 if (vTo.addr == NULL) {
1895 appData.monoMode = True;
1898 blackPieceColor = *(Pixel *) vTo.addr;
1902 if (!appData.monoMode) {
1903 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1904 vFrom.size = strlen(appData.highlightSquareColor);
1905 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1906 if (vTo.addr == NULL) {
1907 appData.monoMode = True;
1910 highlightSquareColor = *(Pixel *) vTo.addr;
1914 if (!appData.monoMode) {
1915 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1916 vFrom.size = strlen(appData.premoveHighlightColor);
1917 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1918 if (vTo.addr == NULL) {
1919 appData.monoMode = True;
1922 premoveHighlightColor = *(Pixel *) vTo.addr;
1930 { // [HGM] taken out of main
1932 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1933 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1934 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1936 if (appData.bitmapDirectory[0] != NULLCHAR) {
1940 CreateXPMBoard(appData.liteBackTextureFile, 1);
1941 CreateXPMBoard(appData.darkBackTextureFile, 0);
1945 /* Create regular pieces */
1946 if (!useImages) CreatePieces();
1951 main (int argc, char **argv)
1953 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1954 XSetWindowAttributes window_attributes;
1956 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1957 XrmValue vFrom, vTo;
1958 XtGeometryResult gres;
1961 int forceMono = False;
1963 srandom(time(0)); // [HGM] book: make random truly random
1965 setbuf(stdout, NULL);
1966 setbuf(stderr, NULL);
1969 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1970 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1974 programName = strrchr(argv[0], '/');
1975 if (programName == NULL)
1976 programName = argv[0];
1981 XtSetLanguageProc(NULL, NULL, NULL);
1982 bindtextdomain(PACKAGE, LOCALEDIR);
1983 textdomain(PACKAGE);
1987 XtAppInitialize(&appContext, "XBoard", shellOptions,
1988 XtNumber(shellOptions),
1989 &argc, argv, xboardResources, NULL, 0);
1990 appData.boardSize = "";
1991 InitAppData(ConvertToLine(argc, argv));
1993 if (p == NULL) p = "/tmp";
1994 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1995 gameCopyFilename = (char*) malloc(i);
1996 gamePasteFilename = (char*) malloc(i);
1997 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1998 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2000 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2001 clientResources, XtNumber(clientResources),
2004 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2005 static char buf[MSG_SIZ];
2006 EscapeExpand(buf, appData.firstInitString);
2007 appData.firstInitString = strdup(buf);
2008 EscapeExpand(buf, appData.secondInitString);
2009 appData.secondInitString = strdup(buf);
2010 EscapeExpand(buf, appData.firstComputerString);
2011 appData.firstComputerString = strdup(buf);
2012 EscapeExpand(buf, appData.secondComputerString);
2013 appData.secondComputerString = strdup(buf);
2016 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2019 if (chdir(chessDir) != 0) {
2020 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2026 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2027 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2028 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2029 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2032 setbuf(debugFP, NULL);
2036 if (appData.debugMode) {
2037 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2041 /* [HGM,HR] make sure board size is acceptable */
2042 if(appData.NrFiles > BOARD_FILES ||
2043 appData.NrRanks > BOARD_RANKS )
2044 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2047 /* This feature does not work; animation needs a rewrite */
2048 appData.highlightDragging = FALSE;
2052 xDisplay = XtDisplay(shellWidget);
2053 xScreen = DefaultScreen(xDisplay);
2054 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2056 gameInfo.variant = StringToVariant(appData.variant);
2057 InitPosition(FALSE);
2060 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2062 if (isdigit(appData.boardSize[0])) {
2063 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2064 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2065 &fontPxlSize, &smallLayout, &tinyLayout);
2067 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2068 programName, appData.boardSize);
2072 /* Find some defaults; use the nearest known size */
2073 SizeDefaults *szd, *nearest;
2074 int distance = 99999;
2075 nearest = szd = sizeDefaults;
2076 while (szd->name != NULL) {
2077 if (abs(szd->squareSize - squareSize) < distance) {
2079 distance = abs(szd->squareSize - squareSize);
2080 if (distance == 0) break;
2084 if (i < 2) lineGap = nearest->lineGap;
2085 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2086 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2087 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2088 if (i < 6) smallLayout = nearest->smallLayout;
2089 if (i < 7) tinyLayout = nearest->tinyLayout;
2092 SizeDefaults *szd = sizeDefaults;
2093 if (*appData.boardSize == NULLCHAR) {
2094 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2095 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2098 if (szd->name == NULL) szd--;
2099 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2101 while (szd->name != NULL &&
2102 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2103 if (szd->name == NULL) {
2104 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2105 programName, appData.boardSize);
2109 squareSize = szd->squareSize;
2110 lineGap = szd->lineGap;
2111 clockFontPxlSize = szd->clockFontPxlSize;
2112 coordFontPxlSize = szd->coordFontPxlSize;
2113 fontPxlSize = szd->fontPxlSize;
2114 smallLayout = szd->smallLayout;
2115 tinyLayout = szd->tinyLayout;
2116 // [HGM] font: use defaults from settings file if available and not overruled
2118 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2119 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2120 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2121 appData.font = fontTable[MESSAGE_FONT][squareSize];
2122 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2123 appData.coordFont = fontTable[COORD_FONT][squareSize];
2125 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2126 if (strlen(appData.pixmapDirectory) > 0) {
2127 p = ExpandPathName(appData.pixmapDirectory);
2129 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2130 appData.pixmapDirectory);
2133 if (appData.debugMode) {
2134 fprintf(stderr, _("\
2135 XBoard square size (hint): %d\n\
2136 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2138 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2139 if (appData.debugMode) {
2140 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2143 defaultLineGap = lineGap;
2144 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2146 /* [HR] height treated separately (hacked) */
2147 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2148 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2149 if (appData.showJail == 1) {
2150 /* Jail on top and bottom */
2151 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2152 XtSetArg(boardArgs[2], XtNheight,
2153 boardHeight + 2*(lineGap + squareSize));
2154 } else if (appData.showJail == 2) {
2156 XtSetArg(boardArgs[1], XtNwidth,
2157 boardWidth + 2*(lineGap + squareSize));
2158 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2161 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2162 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2166 * Determine what fonts to use.
2169 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2170 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2171 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2172 fontSet = CreateFontSet(appData.font);
2173 clockFontSet = CreateFontSet(appData.clockFont);
2175 /* For the coordFont, use the 0th font of the fontset. */
2176 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2177 XFontStruct **font_struct_list;
2178 char **font_name_list;
2179 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2180 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2181 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2184 appData.font = FindFont(appData.font, fontPxlSize);
2185 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2186 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2187 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2188 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2189 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2190 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2192 countFontID = coordFontID; // [HGM] holdings
2193 countFontStruct = coordFontStruct;
2195 xdb = XtDatabase(xDisplay);
2197 XrmPutLineResource(&xdb, "*international: True");
2198 vTo.size = sizeof(XFontSet);
2199 vTo.addr = (XtPointer) &fontSet;
2200 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2202 XrmPutStringResource(&xdb, "*font", appData.font);
2206 * Detect if there are not enough colors available and adapt.
2208 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2209 appData.monoMode = True;
2212 forceMono = MakeColors();
2215 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2217 appData.monoMode = True;
2220 if (appData.lowTimeWarning && !appData.monoMode) {
2221 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2222 vFrom.size = strlen(appData.lowTimeWarningColor);
2223 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2224 if (vTo.addr == NULL)
2225 appData.monoMode = True;
2227 lowTimeWarningColor = *(Pixel *) vTo.addr;
2230 if (appData.monoMode && appData.debugMode) {
2231 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2232 (unsigned long) XWhitePixel(xDisplay, xScreen),
2233 (unsigned long) XBlackPixel(xDisplay, xScreen));
2236 ParseIcsTextColors();
2237 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2238 textColors[ColorNone].attr = 0;
2240 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2246 layoutName = "tinyLayout";
2247 } else if (smallLayout) {
2248 layoutName = "smallLayout";
2250 layoutName = "normalLayout";
2252 /* Outer layoutWidget is there only to provide a name for use in
2253 resources that depend on the layout style */
2255 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2256 layoutArgs, XtNumber(layoutArgs));
2258 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2259 formArgs, XtNumber(formArgs));
2260 XtSetArg(args[0], XtNdefaultDistance, &sep);
2261 XtGetValues(formWidget, args, 1);
2264 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2265 XtSetArg(args[0], XtNtop, XtChainTop);
2266 XtSetArg(args[1], XtNbottom, XtChainTop);
2267 XtSetArg(args[2], XtNright, XtChainLeft);
2268 XtSetValues(menuBarWidget, args, 3);
2270 widgetList[j++] = whiteTimerWidget =
2271 XtCreateWidget("whiteTime", labelWidgetClass,
2272 formWidget, timerArgs, XtNumber(timerArgs));
2274 XtSetArg(args[0], XtNfontSet, clockFontSet);
2276 XtSetArg(args[0], XtNfont, clockFontStruct);
2278 XtSetArg(args[1], XtNtop, XtChainTop);
2279 XtSetArg(args[2], XtNbottom, XtChainTop);
2280 XtSetValues(whiteTimerWidget, args, 3);
2282 widgetList[j++] = blackTimerWidget =
2283 XtCreateWidget("blackTime", labelWidgetClass,
2284 formWidget, timerArgs, XtNumber(timerArgs));
2286 XtSetArg(args[0], XtNfontSet, clockFontSet);
2288 XtSetArg(args[0], XtNfont, clockFontStruct);
2290 XtSetArg(args[1], XtNtop, XtChainTop);
2291 XtSetArg(args[2], XtNbottom, XtChainTop);
2292 XtSetValues(blackTimerWidget, args, 3);
2294 if (appData.titleInWindow) {
2295 widgetList[j++] = titleWidget =
2296 XtCreateWidget("title", labelWidgetClass, formWidget,
2297 titleArgs, XtNumber(titleArgs));
2298 XtSetArg(args[0], XtNtop, XtChainTop);
2299 XtSetArg(args[1], XtNbottom, XtChainTop);
2300 XtSetValues(titleWidget, args, 2);
2303 if (appData.showButtonBar) {
2304 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2305 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2306 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2307 XtSetArg(args[2], XtNtop, XtChainTop);
2308 XtSetArg(args[3], XtNbottom, XtChainTop);
2309 XtSetValues(buttonBarWidget, args, 4);
2312 widgetList[j++] = messageWidget =
2313 XtCreateWidget("message", labelWidgetClass, formWidget,
2314 messageArgs, XtNumber(messageArgs));
2315 XtSetArg(args[0], XtNtop, XtChainTop);
2316 XtSetArg(args[1], XtNbottom, XtChainTop);
2317 XtSetValues(messageWidget, args, 2);
2319 widgetList[j++] = boardWidget =
2320 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2321 XtNumber(boardArgs));
2323 XtManageChildren(widgetList, j);
2325 timerWidth = (boardWidth - sep) / 2;
2326 XtSetArg(args[0], XtNwidth, timerWidth);
2327 XtSetValues(whiteTimerWidget, args, 1);
2328 XtSetValues(blackTimerWidget, args, 1);
2330 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2331 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2332 XtGetValues(whiteTimerWidget, args, 2);
2334 if (appData.showButtonBar) {
2335 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2336 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2337 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2341 * formWidget uses these constraints but they are stored
2345 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2346 XtSetValues(menuBarWidget, args, i);
2347 if (appData.titleInWindow) {
2350 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2351 XtSetValues(whiteTimerWidget, args, i);
2353 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2354 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2355 XtSetValues(blackTimerWidget, args, i);
2357 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2358 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2359 XtSetValues(titleWidget, args, i);
2361 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2362 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2363 XtSetValues(messageWidget, args, i);
2364 if (appData.showButtonBar) {
2366 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2367 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2368 XtSetValues(buttonBarWidget, args, i);
2372 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2373 XtSetValues(whiteTimerWidget, args, i);
2375 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2376 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2377 XtSetValues(blackTimerWidget, args, i);
2379 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2380 XtSetValues(titleWidget, args, i);
2382 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2383 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2384 XtSetValues(messageWidget, args, i);
2385 if (appData.showButtonBar) {
2387 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2388 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2389 XtSetValues(buttonBarWidget, args, i);
2394 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2395 XtSetValues(whiteTimerWidget, args, i);
2397 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2398 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2399 XtSetValues(blackTimerWidget, args, i);
2401 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2402 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2403 XtSetValues(messageWidget, args, i);
2404 if (appData.showButtonBar) {
2406 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2407 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2408 XtSetValues(buttonBarWidget, args, i);
2412 XtSetArg(args[0], XtNfromVert, messageWidget);
2413 XtSetArg(args[1], XtNtop, XtChainTop);
2414 XtSetArg(args[2], XtNbottom, XtChainBottom);
2415 XtSetArg(args[3], XtNleft, XtChainLeft);
2416 XtSetArg(args[4], XtNright, XtChainRight);
2417 XtSetValues(boardWidget, args, 5);
2419 XtRealizeWidget(shellWidget);
2422 XtSetArg(args[0], XtNx, wpMain.x);
2423 XtSetArg(args[1], XtNy, wpMain.y);
2424 XtSetValues(shellWidget, args, 2);
2428 * Correct the width of the message and title widgets.
2429 * It is not known why some systems need the extra fudge term.
2430 * The value "2" is probably larger than needed.
2432 XawFormDoLayout(formWidget, False);
2434 #define WIDTH_FUDGE 2
2436 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2437 XtSetArg(args[i], XtNheight, &h); i++;
2438 XtGetValues(messageWidget, args, i);
2439 if (appData.showButtonBar) {
2441 XtSetArg(args[i], XtNwidth, &w); i++;
2442 XtGetValues(buttonBarWidget, args, i);
2443 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2445 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2448 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2449 if (gres != XtGeometryYes && appData.debugMode) {
2450 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2451 programName, gres, w, h, wr, hr);
2454 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2455 /* The size used for the child widget in layout lags one resize behind
2456 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2458 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2459 if (gres != XtGeometryYes && appData.debugMode) {
2460 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2461 programName, gres, w, h, wr, hr);
2463 textHeight = hr; // [HGM] save height for use in generic popup
2465 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2466 XtSetArg(args[1], XtNright, XtChainRight);
2467 XtSetValues(messageWidget, args, 2);
2469 if (appData.titleInWindow) {
2471 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2472 XtSetArg(args[i], XtNheight, &h); i++;
2473 XtGetValues(titleWidget, args, i);
2475 w = boardWidth - 2*bor;
2477 XtSetArg(args[0], XtNwidth, &w);
2478 XtGetValues(menuBarWidget, args, 1);
2479 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2482 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2483 if (gres != XtGeometryYes && appData.debugMode) {
2485 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2486 programName, gres, w, h, wr, hr);
2489 XawFormDoLayout(formWidget, True);
2491 xBoardWindow = XtWindow(boardWidget);
2493 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2494 // not need to go into InitDrawingSizes().
2498 * Create X checkmark bitmap and initialize option menu checks.
2500 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2501 checkmark_bits, checkmark_width, checkmark_height);
2502 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2503 #ifndef OPTIONSDIALOG
2504 if (appData.alwaysPromoteToQueen) {
2505 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2508 if (appData.animateDragging) {
2509 XtSetValues(XtNameToWidget(menuBarWidget,
2510 "menuOptions.Animate Dragging"),
2513 if (appData.animate) {
2514 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2517 if (appData.autoCallFlag) {
2518 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2521 if (appData.autoFlipView) {
2522 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2525 if (appData.blindfold) {
2526 XtSetValues(XtNameToWidget(menuBarWidget,
2527 "menuOptions.Blindfold"), args, 1);
2529 if (appData.flashCount > 0) {
2530 XtSetValues(XtNameToWidget(menuBarWidget,
2531 "menuOptions.Flash Moves"),
2535 if (appData.highlightDragging) {
2536 XtSetValues(XtNameToWidget(menuBarWidget,
2537 "menuOptions.Highlight Dragging"),
2541 if (appData.highlightLastMove) {
2542 XtSetValues(XtNameToWidget(menuBarWidget,
2543 "menuOptions.Highlight Last Move"),
2546 if (appData.highlightMoveWithArrow) {
2547 XtSetValues(XtNameToWidget(menuBarWidget,
2548 "menuOptions.Arrow"),
2551 // if (appData.icsAlarm) {
2552 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2555 if (appData.ringBellAfterMoves) {
2556 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2559 if (appData.oneClick) {
2560 XtSetValues(XtNameToWidget(menuBarWidget,
2561 "menuOptions.OneClick"), args, 1);
2563 if (appData.periodicUpdates) {
2564 XtSetValues(XtNameToWidget(menuBarWidget,
2565 "menuOptions.Periodic Updates"), args, 1);
2567 if (appData.ponderNextMove) {
2568 XtSetValues(XtNameToWidget(menuBarWidget,
2569 "menuOptions.Ponder Next Move"), args, 1);
2571 if (appData.popupExitMessage) {
2572 XtSetValues(XtNameToWidget(menuBarWidget,
2573 "menuOptions.Popup Exit Message"), args, 1);
2575 if (appData.popupMoveErrors) {
2576 XtSetValues(XtNameToWidget(menuBarWidget,
2577 "menuOptions.Popup Move Errors"), args, 1);
2579 // if (appData.premove) {
2580 // XtSetValues(XtNameToWidget(menuBarWidget,
2581 // "menuOptions.Premove"), args, 1);
2583 if (appData.showCoords) {
2584 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2587 if (appData.hideThinkingFromHuman) {
2588 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2591 if (appData.testLegality) {
2592 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2596 if (saveSettingsOnExit) {
2597 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2604 ReadBitmap(&wIconPixmap, "icon_white.bm",
2605 icon_white_bits, icon_white_width, icon_white_height);
2606 ReadBitmap(&bIconPixmap, "icon_black.bm",
2607 icon_black_bits, icon_black_width, icon_black_height);
2608 iconPixmap = wIconPixmap;
2610 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2611 XtSetValues(shellWidget, args, i);
2614 * Create a cursor for the board widget.
2616 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2617 XChangeWindowAttributes(xDisplay, xBoardWindow,
2618 CWCursor, &window_attributes);
2621 * Inhibit shell resizing.
2623 shellArgs[0].value = (XtArgVal) &w;
2624 shellArgs[1].value = (XtArgVal) &h;
2625 XtGetValues(shellWidget, shellArgs, 2);
2626 shellArgs[4].value = shellArgs[2].value = w;
2627 shellArgs[5].value = shellArgs[3].value = h;
2628 XtSetValues(shellWidget, &shellArgs[2], 4);
2629 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2630 marginH = h - boardHeight;
2632 CatchDeleteWindow(shellWidget, "QuitProc");
2640 if (appData.animate || appData.animateDragging)
2643 XtAugmentTranslations(formWidget,
2644 XtParseTranslationTable(globalTranslations));
2645 XtAugmentTranslations(boardWidget,
2646 XtParseTranslationTable(boardTranslations));
2647 XtAugmentTranslations(whiteTimerWidget,
2648 XtParseTranslationTable(whiteTranslations));
2649 XtAugmentTranslations(blackTimerWidget,
2650 XtParseTranslationTable(blackTranslations));
2652 /* Why is the following needed on some versions of X instead
2653 * of a translation? */
2654 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2655 (XtEventHandler) EventProc, NULL);
2657 XtAddEventHandler(formWidget, KeyPressMask, False,
2658 (XtEventHandler) MoveTypeInProc, NULL);
2660 /* [AS] Restore layout */
2661 if( wpMoveHistory.visible ) {
2665 if( wpEvalGraph.visible )
2670 if( wpEngineOutput.visible ) {
2671 EngineOutputPopUp();
2676 if (errorExitStatus == -1) {
2677 if (appData.icsActive) {
2678 /* We now wait until we see "login:" from the ICS before
2679 sending the logon script (problems with timestamp otherwise) */
2680 /*ICSInitScript();*/
2681 if (appData.icsInputBox) ICSInputBoxPopUp();
2685 signal(SIGWINCH, TermSizeSigHandler);
2687 signal(SIGINT, IntSigHandler);
2688 signal(SIGTERM, IntSigHandler);
2689 if (*appData.cmailGameName != NULLCHAR) {
2690 signal(SIGUSR1, CmailSigHandler);
2693 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2695 // XtSetKeyboardFocus(shellWidget, formWidget);
2696 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2698 XtAppMainLoop(appContext);
2699 if (appData.debugMode) fclose(debugFP); // [DM] debug
2703 static Boolean noEcho;
2708 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2709 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2711 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2712 unlink(gameCopyFilename);
2713 unlink(gamePasteFilename);
2714 if(noEcho) EchoOn();
2718 TermSizeSigHandler (int sig)
2724 IntSigHandler (int sig)
2730 CmailSigHandler (int sig)
2735 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2737 /* Activate call-back function CmailSigHandlerCallBack() */
2738 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2740 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2744 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2747 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2749 /**** end signal code ****/
2755 /* try to open the icsLogon script, either in the location given
2756 * or in the users HOME directory
2763 f = fopen(appData.icsLogon, "r");
2766 homedir = getenv("HOME");
2767 if (homedir != NULL)
2769 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2770 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2771 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2772 f = fopen(buf, "r");
2777 ProcessICSInitScript(f);
2779 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2798 GreyRevert (Boolean grey)
2801 if (!menuBarWidget) return;
2802 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2804 DisplayError("menuEdit.Revert", 0);
2806 XtSetSensitive(w, !grey);
2808 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2810 DisplayError("menuEdit.Annotate", 0);
2812 XtSetSensitive(w, !grey);
2817 SetMenuEnables (Enables *enab)
2820 if (!menuBarWidget) return;
2821 while (enab->name != NULL) {
2822 w = XtNameToWidget(menuBarWidget, enab->name);
2824 DisplayError(enab->name, 0);
2826 XtSetSensitive(w, enab->value);
2832 Enables icsEnables[] = {
2833 { "menuFile.Mail Move", False },
2834 { "menuFile.Reload CMail Message", False },
2835 { "menuMode.Machine Black", False },
2836 { "menuMode.Machine White", False },
2837 { "menuMode.Analysis Mode", False },
2838 { "menuMode.Analyze File", False },
2839 { "menuMode.Two Machines", False },
2840 { "menuMode.Machine Match", False },
2842 { "menuEngine.Hint", False },
2843 { "menuEngine.Book", False },
2844 { "menuEngine.Move Now", False },
2845 #ifndef OPTIONSDIALOG
2846 { "menuOptions.Periodic Updates", False },
2847 { "menuOptions.Hide Thinking", False },
2848 { "menuOptions.Ponder Next Move", False },
2851 { "menuEngine.Engine #1 Settings", False },
2852 { "menuEngine.Engine #2 Settings", False },
2853 { "menuEngine.Load Engine", False },
2854 { "menuEdit.Annotate", False },
2855 { "menuOptions.Match", False },
2859 Enables ncpEnables[] = {
2860 { "menuFile.Mail Move", False },
2861 { "menuFile.Reload CMail Message", False },
2862 { "menuMode.Machine White", False },
2863 { "menuMode.Machine Black", False },
2864 { "menuMode.Analysis Mode", False },
2865 { "menuMode.Analyze File", False },
2866 { "menuMode.Two Machines", False },
2867 { "menuMode.Machine Match", False },
2868 { "menuMode.ICS Client", False },
2869 { "menuView.ICStex", False },
2870 { "menuView.ICS Input Box", False },
2871 { "Action", False },
2872 { "menuEdit.Revert", False },
2873 { "menuEdit.Annotate", False },
2874 { "menuEngine.Engine #1 Settings", False },
2875 { "menuEngine.Engine #2 Settings", False },
2876 { "menuEngine.Move Now", False },
2877 { "menuEngine.Retract Move", False },
2878 { "menuOptions.ICS", False },
2879 #ifndef OPTIONSDIALOG
2880 { "menuOptions.Auto Flag", False },
2881 { "menuOptions.Auto Flip View", False },
2882 // { "menuOptions.ICS Alarm", False },
2883 { "menuOptions.Move Sound", False },
2884 { "menuOptions.Hide Thinking", False },
2885 { "menuOptions.Periodic Updates", False },
2886 { "menuOptions.Ponder Next Move", False },
2888 { "menuEngine.Hint", False },
2889 { "menuEngine.Book", False },
2893 Enables gnuEnables[] = {
2894 { "menuMode.ICS Client", False },
2895 { "menuView.ICStex", False },
2896 { "menuView.ICS Input Box", False },
2897 { "menuAction.Accept", False },
2898 { "menuAction.Decline", False },
2899 { "menuAction.Rematch", False },
2900 { "menuAction.Adjourn", False },
2901 { "menuAction.Stop Examining", False },
2902 { "menuAction.Stop Observing", False },
2903 { "menuAction.Upload to Examine", False },
2904 { "menuEdit.Revert", False },
2905 { "menuEdit.Annotate", False },
2906 { "menuOptions.ICS", False },
2908 /* The next two options rely on SetCmailMode being called *after* */
2909 /* SetGNUMode so that when GNU is being used to give hints these */
2910 /* menu options are still available */
2912 { "menuFile.Mail Move", False },
2913 { "menuFile.Reload CMail Message", False },
2914 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2915 { "menuMode.Machine White", True },
2916 { "menuMode.Machine Black", True },
2917 { "menuMode.Analysis Mode", True },
2918 { "menuMode.Analyze File", True },
2919 { "menuMode.Two Machines", True },
2920 { "menuMode.Machine Match", True },
2921 { "menuEngine.Engine #1 Settings", True },
2922 { "menuEngine.Engine #2 Settings", True },
2923 { "menuEngine.Hint", True },
2924 { "menuEngine.Book", True },
2925 { "menuEngine.Move Now", True },
2926 { "menuEngine.Retract Move", True },
2931 Enables cmailEnables[] = {
2933 { "menuAction.Call Flag", False },
2934 { "menuAction.Draw", True },
2935 { "menuAction.Adjourn", False },
2936 { "menuAction.Abort", False },
2937 { "menuAction.Stop Observing", False },
2938 { "menuAction.Stop Examining", False },
2939 { "menuFile.Mail Move", True },
2940 { "menuFile.Reload CMail Message", True },
2944 Enables trainingOnEnables[] = {
2945 { "menuMode.Edit Comment", False },
2946 { "menuMode.Pause", False },
2947 { "menuEdit.Forward", False },
2948 { "menuEdit.Backward", False },
2949 { "menuEdit.Forward to End", False },
2950 { "menuEdit.Back to Start", False },
2951 { "menuEngine.Move Now", False },
2952 { "menuEdit.Truncate Game", False },
2956 Enables trainingOffEnables[] = {
2957 { "menuMode.Edit Comment", True },
2958 { "menuMode.Pause", True },
2959 { "menuEdit.Forward", True },
2960 { "menuEdit.Backward", True },
2961 { "menuEdit.Forward to End", True },
2962 { "menuEdit.Back to Start", True },
2963 { "menuEngine.Move Now", True },
2964 { "menuEdit.Truncate Game", True },
2968 Enables machineThinkingEnables[] = {
2969 { "menuFile.Load Game", False },
2970 // { "menuFile.Load Next Game", False },
2971 // { "menuFile.Load Previous Game", False },
2972 // { "menuFile.Reload Same Game", False },
2973 { "menuEdit.Paste Game", False },
2974 { "menuFile.Load Position", False },
2975 // { "menuFile.Load Next Position", False },
2976 // { "menuFile.Load Previous Position", False },
2977 // { "menuFile.Reload Same Position", False },
2978 { "menuEdit.Paste Position", False },
2979 { "menuMode.Machine White", False },
2980 { "menuMode.Machine Black", False },
2981 { "menuMode.Two Machines", False },
2982 // { "menuMode.Machine Match", False },
2983 { "menuEngine.Retract Move", False },
2987 Enables userThinkingEnables[] = {
2988 { "menuFile.Load Game", True },
2989 // { "menuFile.Load Next Game", True },
2990 // { "menuFile.Load Previous Game", True },
2991 // { "menuFile.Reload Same Game", True },
2992 { "menuEdit.Paste Game", True },
2993 { "menuFile.Load Position", True },
2994 // { "menuFile.Load Next Position", True },
2995 // { "menuFile.Load Previous Position", True },
2996 // { "menuFile.Reload Same Position", True },
2997 { "menuEdit.Paste Position", True },
2998 { "menuMode.Machine White", True },
2999 { "menuMode.Machine Black", True },
3000 { "menuMode.Two Machines", True },
3001 // { "menuMode.Machine Match", True },
3002 { "menuEngine.Retract Move", True },
3009 SetMenuEnables(icsEnables);
3012 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3013 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3014 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3022 SetMenuEnables(ncpEnables);
3028 SetMenuEnables(gnuEnables);
3034 SetMenuEnables(cmailEnables);
3038 SetTrainingModeOn ()
3040 SetMenuEnables(trainingOnEnables);
3041 if (appData.showButtonBar) {
3042 XtSetSensitive(buttonBarWidget, False);
3048 SetTrainingModeOff ()
3050 SetMenuEnables(trainingOffEnables);
3051 if (appData.showButtonBar) {
3052 XtSetSensitive(buttonBarWidget, True);
3057 SetUserThinkingEnables ()
3059 if (appData.noChessProgram) return;
3060 SetMenuEnables(userThinkingEnables);
3064 SetMachineThinkingEnables ()
3066 if (appData.noChessProgram) return;
3067 SetMenuEnables(machineThinkingEnables);
3069 case MachinePlaysBlack:
3070 case MachinePlaysWhite:
3071 case TwoMachinesPlay:
3072 XtSetSensitive(XtNameToWidget(menuBarWidget,
3073 ModeToWidgetName(gameMode)), True);
3080 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3081 #define HISTORY_SIZE 64
3082 static char *history[HISTORY_SIZE];
3083 int histIn = 0, histP = 0;
3086 SaveInHistory (char *cmd)
3088 if (history[histIn] != NULL) {
3089 free(history[histIn]);
3090 history[histIn] = NULL;
3092 if (*cmd == NULLCHAR) return;
3093 history[histIn] = StrSave(cmd);
3094 histIn = (histIn + 1) % HISTORY_SIZE;
3095 if (history[histIn] != NULL) {
3096 free(history[histIn]);
3097 history[histIn] = NULL;
3103 PrevInHistory (char *cmd)
3106 if (histP == histIn) {
3107 if (history[histIn] != NULL) free(history[histIn]);
3108 history[histIn] = StrSave(cmd);
3110 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3111 if (newhp == histIn || history[newhp] == NULL) return NULL;
3113 return history[histP];
3119 if (histP == histIn) return NULL;
3120 histP = (histP + 1) % HISTORY_SIZE;
3121 return history[histP];
3123 // end of borrowed code
3125 #define Abs(n) ((n)<0 ? -(n) : (n))
3129 InsertPxlSize (char *pattern, int targetPxlSize)
3131 char *base_fnt_lst, strInt[12], *p, *q;
3132 int alternatives, i, len, strIntLen;
3135 * Replace the "*" (if present) in the pixel-size slot of each
3136 * alternative with the targetPxlSize.
3140 while ((p = strchr(p, ',')) != NULL) {
3144 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3145 strIntLen = strlen(strInt);
3146 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3150 while (alternatives--) {
3151 char *comma = strchr(p, ',');
3152 for (i=0; i<14; i++) {
3153 char *hyphen = strchr(p, '-');
3155 if (comma && hyphen > comma) break;
3156 len = hyphen + 1 - p;
3157 if (i == 7 && *p == '*' && len == 2) {
3159 memcpy(q, strInt, strIntLen);
3169 len = comma + 1 - p;
3176 return base_fnt_lst;
3180 CreateFontSet (char *base_fnt_lst)
3183 char **missing_list;
3187 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3188 &missing_list, &missing_count, &def_string);
3189 if (appData.debugMode) {
3191 XFontStruct **font_struct_list;
3192 char **font_name_list;
3193 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3195 fprintf(debugFP, " got list %s, locale %s\n",
3196 XBaseFontNameListOfFontSet(fntSet),
3197 XLocaleOfFontSet(fntSet));
3198 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3199 for (i = 0; i < count; i++) {
3200 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3203 for (i = 0; i < missing_count; i++) {
3204 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3207 if (fntSet == NULL) {
3208 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3213 #else // not ENABLE_NLS
3215 * Find a font that matches "pattern" that is as close as
3216 * possible to the targetPxlSize. Prefer fonts that are k
3217 * pixels smaller to fonts that are k pixels larger. The
3218 * pattern must be in the X Consortium standard format,
3219 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3220 * The return value should be freed with XtFree when no
3224 FindFont (char *pattern, int targetPxlSize)
3226 char **fonts, *p, *best, *scalable, *scalableTail;
3227 int i, j, nfonts, minerr, err, pxlSize;
3229 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3231 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3232 programName, pattern);
3239 for (i=0; i<nfonts; i++) {
3242 if (*p != '-') continue;
3244 if (*p == NULLCHAR) break;
3245 if (*p++ == '-') j++;
3247 if (j < 7) continue;
3250 scalable = fonts[i];
3253 err = pxlSize - targetPxlSize;
3254 if (Abs(err) < Abs(minerr) ||
3255 (minerr > 0 && err < 0 && -err == minerr)) {
3261 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3262 /* If the error is too big and there is a scalable font,
3263 use the scalable font. */
3264 int headlen = scalableTail - scalable;
3265 p = (char *) XtMalloc(strlen(scalable) + 10);
3266 while (isdigit(*scalableTail)) scalableTail++;
3267 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3269 p = (char *) XtMalloc(strlen(best) + 2);
3270 safeStrCpy(p, best, strlen(best)+1 );
3272 if (appData.debugMode) {
3273 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3274 pattern, targetPxlSize, p);
3276 XFreeFontNames(fonts);
3283 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3284 // must be called before all non-first callse to CreateGCs()
3285 XtReleaseGC(shellWidget, highlineGC);
3286 XtReleaseGC(shellWidget, lightSquareGC);
3287 XtReleaseGC(shellWidget, darkSquareGC);
3288 XtReleaseGC(shellWidget, lineGC);
3289 if (appData.monoMode) {
3290 if (DefaultDepth(xDisplay, xScreen) == 1) {
3291 XtReleaseGC(shellWidget, wbPieceGC);
3293 XtReleaseGC(shellWidget, bwPieceGC);
3296 XtReleaseGC(shellWidget, prelineGC);
3297 XtReleaseGC(shellWidget, jailSquareGC);
3298 XtReleaseGC(shellWidget, wdPieceGC);
3299 XtReleaseGC(shellWidget, wlPieceGC);
3300 XtReleaseGC(shellWidget, wjPieceGC);
3301 XtReleaseGC(shellWidget, bdPieceGC);
3302 XtReleaseGC(shellWidget, blPieceGC);
3303 XtReleaseGC(shellWidget, bjPieceGC);
3308 CreateGCs (int redo)
3310 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3311 | GCBackground | GCFunction | GCPlaneMask;
3312 XGCValues gc_values;
3315 gc_values.plane_mask = AllPlanes;
3316 gc_values.line_width = lineGap;
3317 gc_values.line_style = LineSolid;
3318 gc_values.function = GXcopy;
3321 DeleteGCs(); // called a second time; clean up old GCs first
3322 } else { // [HGM] grid and font GCs created on first call only
3323 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3324 gc_values.background = XWhitePixel(xDisplay, xScreen);
3325 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3326 XSetFont(xDisplay, coordGC, coordFontID);
3328 // [HGM] make font for holdings counts (white on black)
3329 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3330 gc_values.background = XBlackPixel(xDisplay, xScreen);
3331 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3332 XSetFont(xDisplay, countGC, countFontID);
3334 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3335 gc_values.background = XBlackPixel(xDisplay, xScreen);
3336 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3338 if (appData.monoMode) {
3339 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3340 gc_values.background = XWhitePixel(xDisplay, xScreen);
3341 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3343 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3344 gc_values.background = XBlackPixel(xDisplay, xScreen);
3345 lightSquareGC = wbPieceGC
3346 = XtGetGC(shellWidget, value_mask, &gc_values);
3348 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3349 gc_values.background = XWhitePixel(xDisplay, xScreen);
3350 darkSquareGC = bwPieceGC
3351 = XtGetGC(shellWidget, value_mask, &gc_values);
3353 if (DefaultDepth(xDisplay, xScreen) == 1) {
3354 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3355 gc_values.function = GXcopyInverted;
3356 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3357 gc_values.function = GXcopy;
3358 if (XBlackPixel(xDisplay, xScreen) == 1) {
3359 bwPieceGC = darkSquareGC;
3360 wbPieceGC = copyInvertedGC;
3362 bwPieceGC = copyInvertedGC;
3363 wbPieceGC = lightSquareGC;
3367 gc_values.foreground = highlightSquareColor;
3368 gc_values.background = highlightSquareColor;
3369 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3371 gc_values.foreground = premoveHighlightColor;
3372 gc_values.background = premoveHighlightColor;
3373 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3375 gc_values.foreground = lightSquareColor;
3376 gc_values.background = darkSquareColor;
3377 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3379 gc_values.foreground = darkSquareColor;
3380 gc_values.background = lightSquareColor;
3381 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3383 gc_values.foreground = jailSquareColor;
3384 gc_values.background = jailSquareColor;
3385 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3387 gc_values.foreground = whitePieceColor;
3388 gc_values.background = darkSquareColor;
3389 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3391 gc_values.foreground = whitePieceColor;
3392 gc_values.background = lightSquareColor;
3393 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3395 gc_values.foreground = whitePieceColor;
3396 gc_values.background = jailSquareColor;
3397 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3399 gc_values.foreground = blackPieceColor;
3400 gc_values.background = darkSquareColor;
3401 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3403 gc_values.foreground = blackPieceColor;
3404 gc_values.background = lightSquareColor;
3405 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3407 gc_values.foreground = blackPieceColor;
3408 gc_values.background = jailSquareColor;
3409 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3414 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3422 fp = fopen(filename, "rb");
3424 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3431 for (y=0; y<h; ++y) {
3432 for (x=0; x<h; ++x) {
3437 XPutPixel(xim, x, y, blackPieceColor);
3439 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3442 XPutPixel(xim, x, y, darkSquareColor);
3444 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3447 XPutPixel(xim, x, y, whitePieceColor);
3449 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3452 XPutPixel(xim, x, y, lightSquareColor);
3454 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3462 /* create Pixmap of piece */
3463 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3465 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3468 /* create Pixmap of clipmask
3469 Note: We assume the white/black pieces have the same
3470 outline, so we make only 6 masks. This is okay
3471 since the XPM clipmask routines do the same. */
3473 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3475 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3478 /* now create the 1-bit version */
3479 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3482 values.foreground = 1;
3483 values.background = 0;
3485 /* Don't use XtGetGC, not read only */
3486 maskGC = XCreateGC(xDisplay, *mask,
3487 GCForeground | GCBackground, &values);
3488 XCopyPlane(xDisplay, temp, *mask, maskGC,
3489 0, 0, squareSize, squareSize, 0, 0, 1);
3490 XFreePixmap(xDisplay, temp);
3495 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3503 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3508 /* The XSynchronize calls were copied from CreatePieces.
3509 Not sure if needed, but can't hurt */
3510 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3513 /* temp needed by loadXIM() */
3514 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3515 0, 0, ss, ss, AllPlanes, XYPixmap);
3517 if (strlen(appData.pixmapDirectory) == 0) {
3521 if (appData.monoMode) {
3522 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3526 fprintf(stderr, _("\nLoading XIMs...\n"));
3528 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3529 fprintf(stderr, "%d", piece+1);
3530 for (kind=0; kind<4; kind++) {
3531 fprintf(stderr, ".");
3532 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3533 ExpandPathName(appData.pixmapDirectory),
3534 piece <= (int) WhiteKing ? "" : "w",
3535 pieceBitmapNames[piece],
3537 ximPieceBitmap[kind][piece] =
3538 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3539 0, 0, ss, ss, AllPlanes, XYPixmap);
3540 if (appData.debugMode)
3541 fprintf(stderr, _("(File:%s:) "), buf);
3542 loadXIM(ximPieceBitmap[kind][piece],
3544 &(xpmPieceBitmap2[kind][piece]),
3545 &(ximMaskPm2[piece]));
3546 if(piece <= (int)WhiteKing)
3547 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3549 fprintf(stderr," ");
3551 /* Load light and dark squares */
3552 /* If the LSQ and DSQ pieces don't exist, we will
3553 draw them with solid squares. */
3554 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3555 if (access(buf, 0) != 0) {
3559 fprintf(stderr, _("light square "));
3561 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3562 0, 0, ss, ss, AllPlanes, XYPixmap);
3563 if (appData.debugMode)
3564 fprintf(stderr, _("(File:%s:) "), buf);
3566 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3567 fprintf(stderr, _("dark square "));
3568 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3569 ExpandPathName(appData.pixmapDirectory), ss);
3570 if (appData.debugMode)
3571 fprintf(stderr, _("(File:%s:) "), buf);
3573 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3574 0, 0, ss, ss, AllPlanes, XYPixmap);
3575 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3576 xpmJailSquare = xpmLightSquare;
3578 fprintf(stderr, _("Done.\n"));
3580 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3583 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3587 CreateXPMBoard (char *s, int kind)
3591 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3592 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3593 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3599 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3600 // thisroutine has to be called t free the old piece pixmaps
3602 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3603 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3605 XFreePixmap(xDisplay, xpmLightSquare);
3606 XFreePixmap(xDisplay, xpmDarkSquare);
3615 u_int ss = squareSize;
3617 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3618 XpmColorSymbol symbols[4];
3619 static int redo = False;
3621 if(redo) FreeXPMPieces(); else redo = 1;
3623 /* The XSynchronize calls were copied from CreatePieces.
3624 Not sure if needed, but can't hurt */
3625 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3627 /* Setup translations so piece colors match square colors */
3628 symbols[0].name = "light_piece";
3629 symbols[0].value = appData.whitePieceColor;
3630 symbols[1].name = "dark_piece";
3631 symbols[1].value = appData.blackPieceColor;
3632 symbols[2].name = "light_square";
3633 symbols[2].value = appData.lightSquareColor;
3634 symbols[3].name = "dark_square";
3635 symbols[3].value = appData.darkSquareColor;
3637 attr.valuemask = XpmColorSymbols;
3638 attr.colorsymbols = symbols;
3639 attr.numsymbols = 4;
3641 if (appData.monoMode) {
3642 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3646 if (strlen(appData.pixmapDirectory) == 0) {
3647 XpmPieces* pieces = builtInXpms;
3650 while (pieces->size != squareSize && pieces->size) pieces++;
3651 if (!pieces->size) {
3652 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3655 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3656 for (kind=0; kind<4; kind++) {
3658 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3659 pieces->xpm[piece][kind],
3660 &(xpmPieceBitmap2[kind][piece]),
3661 NULL, &attr)) != 0) {
3662 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3666 if(piece <= (int) WhiteKing)
3667 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3671 xpmJailSquare = xpmLightSquare;
3675 fprintf(stderr, _("\nLoading XPMs...\n"));
3678 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3679 fprintf(stderr, "%d ", piece+1);
3680 for (kind=0; kind<4; kind++) {
3681 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3682 ExpandPathName(appData.pixmapDirectory),
3683 piece > (int) WhiteKing ? "w" : "",
3684 pieceBitmapNames[piece],
3686 if (appData.debugMode) {
3687 fprintf(stderr, _("(File:%s:) "), buf);
3689 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3690 &(xpmPieceBitmap2[kind][piece]),
3691 NULL, &attr)) != 0) {
3692 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3693 // [HGM] missing: read of unorthodox piece failed; substitute King.
3694 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3695 ExpandPathName(appData.pixmapDirectory),
3697 if (appData.debugMode) {
3698 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3700 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3701 &(xpmPieceBitmap2[kind][piece]),
3705 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3710 if(piece <= (int) WhiteKing)
3711 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3714 /* Load light and dark squares */
3715 /* If the LSQ and DSQ pieces don't exist, we will
3716 draw them with solid squares. */
3717 fprintf(stderr, _("light square "));
3718 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3719 if (access(buf, 0) != 0) {
3723 if (appData.debugMode)
3724 fprintf(stderr, _("(File:%s:) "), buf);
3726 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3727 &xpmLightSquare, NULL, &attr)) != 0) {
3728 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3731 fprintf(stderr, _("dark square "));
3732 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3733 ExpandPathName(appData.pixmapDirectory), ss);
3734 if (appData.debugMode) {
3735 fprintf(stderr, _("(File:%s:) "), buf);
3737 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3738 &xpmDarkSquare, NULL, &attr)) != 0) {
3739 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3743 xpmJailSquare = xpmLightSquare;
3744 fprintf(stderr, _("Done.\n"));
3746 oldVariant = -1; // kludge to force re-makig of animation masks
3747 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3750 #endif /* HAVE_LIBXPM */
3753 /* No built-in bitmaps */
3758 u_int ss = squareSize;
3760 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3763 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3764 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3765 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3766 pieceBitmapNames[piece],
3767 ss, kind == SOLID ? 's' : 'o');
3768 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3769 if(piece <= (int)WhiteKing)
3770 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3774 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3778 /* With built-in bitmaps */
3782 BuiltInBits* bib = builtInBits;
3785 u_int ss = squareSize;
3787 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3790 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3792 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3793 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3794 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3795 pieceBitmapNames[piece],
3796 ss, kind == SOLID ? 's' : 'o');
3797 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3798 bib->bits[kind][piece], ss, ss);
3799 if(piece <= (int)WhiteKing)
3800 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3804 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3810 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3815 char msg[MSG_SIZ], fullname[MSG_SIZ];
3817 if (*appData.bitmapDirectory != NULLCHAR) {
3818 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3819 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3820 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3821 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3822 &w, &h, pm, &x_hot, &y_hot);
3823 fprintf(stderr, "load %s\n", name);
3824 if (errcode != BitmapSuccess) {
3826 case BitmapOpenFailed:
3827 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3829 case BitmapFileInvalid:
3830 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3832 case BitmapNoMemory:
3833 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3837 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3841 fprintf(stderr, _("%s: %s...using built-in\n"),
3843 } else if (w != wreq || h != hreq) {
3845 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3846 programName, fullname, w, h, wreq, hreq);
3852 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3862 if (lineGap == 0) return;
3864 /* [HR] Split this into 2 loops for non-square boards. */
3866 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3867 gridSegments[i].x1 = 0;
3868 gridSegments[i].x2 =
3869 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3870 gridSegments[i].y1 = gridSegments[i].y2
3871 = lineGap / 2 + (i * (squareSize + lineGap));
3874 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3875 gridSegments[j + i].y1 = 0;
3876 gridSegments[j + i].y2 =
3877 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3878 gridSegments[j + i].x1 = gridSegments[j + i].x2
3879 = lineGap / 2 + (j * (squareSize + lineGap));
3884 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3886 XtActionProc proc = (XtActionProc) addr;
3888 (proc)(NULL, NULL, NULL, NULL);
3892 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3899 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3902 XtSetArg(args[j], XtNleftMargin, 20); j++;
3903 XtSetArg(args[j], XtNrightMargin, 20); j++;
3905 while (mi->string != NULL) {
3906 if (strcmp(mi->string, "----") == 0) {
3907 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3910 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3911 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3913 XtAddCallback(entry, XtNcallback,
3914 (XtCallbackProc) MenuBarSelect,
3915 (caddr_t) mi->proc);
3922 CreateMenuBar (Menu *mb, int boardWidth)
3924 int i, j, nr = 0, wtot = 0, widths[10];
3927 char menuName[MSG_SIZ];
3932 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3933 XtSetArg(args[j], XtNvSpace, 0); j++;
3934 XtSetArg(args[j], XtNborderWidth, 0); j++;
3935 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3936 formWidget, args, j);
3938 while (mb->name != NULL) {
3939 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3940 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3942 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3943 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3944 XtSetArg(args[j], XtNborderWidth, 0); j++;
3945 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3947 CreateMenuBarPopup(menuBar, menuName, mb);
3949 XtSetArg(args[j], XtNwidth, &w); j++;
3950 XtGetValues(mb->subMenu, args, j);
3951 wtot += mb->textWidth = widths[nr++] = w;
3954 while(wtot > boardWidth - 40) {
3956 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3960 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3962 XtSetArg(args[j], XtNwidth, widths[i]); j++;
3963 XtSetValues(ma[i].subMenu, args, j);
3969 CreateButtonBar (MenuItem *mi)
3972 Widget button, buttonBar;
3976 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3978 XtSetArg(args[j], XtNhSpace, 0); j++;
3980 XtSetArg(args[j], XtNborderWidth, 0); j++;
3981 XtSetArg(args[j], XtNvSpace, 0); j++;
3982 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3983 formWidget, args, j);
3985 while (mi->string != NULL) {
3988 XtSetArg(args[j], XtNinternalWidth, 2); j++;
3989 XtSetArg(args[j], XtNborderWidth, 0); j++;
3991 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3992 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3993 buttonBar, args, j);
3994 XtAddCallback(button, XtNcallback,
3995 (XtCallbackProc) MenuBarSelect,
3996 (caddr_t) mi->proc);
4003 CreatePieceMenu (char *name, int color)
4008 ChessSquare selection;
4010 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4011 boardWidget, args, 0);
4013 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4014 String item = pieceMenuStrings[color][i];
4016 if (strcmp(item, "----") == 0) {
4017 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4020 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4021 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4023 selection = pieceMenuTranslation[color][i];
4024 XtAddCallback(entry, XtNcallback,
4025 (XtCallbackProc) PieceMenuSelect,
4026 (caddr_t) selection);
4027 if (selection == WhitePawn || selection == BlackPawn) {
4028 XtSetArg(args[0], XtNpopupOnEntry, entry);
4029 XtSetValues(menu, args, 1);
4042 ChessSquare selection;
4044 whitePieceMenu = CreatePieceMenu("menuW", 0);
4045 blackPieceMenu = CreatePieceMenu("menuB", 1);
4047 XtRegisterGrabAction(PieceMenuPopup, True,
4048 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4049 GrabModeAsync, GrabModeAsync);
4051 XtSetArg(args[0], XtNlabel, _("Drop"));
4052 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4053 boardWidget, args, 1);
4054 for (i = 0; i < DROP_MENU_SIZE; i++) {
4055 String item = dropMenuStrings[i];
4057 if (strcmp(item, "----") == 0) {
4058 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4061 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4062 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4064 selection = dropMenuTranslation[i];
4065 XtAddCallback(entry, XtNcallback,
4066 (XtCallbackProc) DropMenuSelect,
4067 (caddr_t) selection);
4081 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4082 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4083 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4084 dmEnables[i].piece);
4085 XtSetSensitive(entry, p != NULL || !appData.testLegality
4086 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4087 && !appData.icsActive));
4089 while (p && *p++ == dmEnables[i].piece) count++;
4090 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4092 XtSetArg(args[j], XtNlabel, label); j++;
4093 XtSetValues(entry, args, j);
4098 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4100 String whichMenu; int menuNr = -2;
4101 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4102 if (event->type == ButtonRelease)
4103 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4104 else if (event->type == ButtonPress)
4105 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4107 case 0: whichMenu = params[0]; break;
4108 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4110 case -1: if (errorUp) ErrorPopDown();
4113 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4117 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4119 if (pmFromX < 0 || pmFromY < 0) return;
4120 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4124 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4126 if (pmFromX < 0 || pmFromY < 0) return;
4127 DropMenuEvent(piece, pmFromX, pmFromY);
4131 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4133 shiftKey = prms[0][0] & 1;
4138 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4140 shiftKey = prms[0][0] & 1;
4146 * If the user selects on a border boundary, return -1; if off the board,
4147 * return -2. Otherwise map the event coordinate to the square.
4150 EventToSquare (int x, int limit)
4157 if ((x % (squareSize + lineGap)) >= squareSize)
4159 x /= (squareSize + lineGap);
4166 do_flash_delay (unsigned long msec)
4172 drawHighlight (int file, int rank, GC gc)
4176 if (lineGap == 0) return;
4179 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4180 (squareSize + lineGap);
4181 y = lineGap/2 + rank * (squareSize + lineGap);
4183 x = lineGap/2 + file * (squareSize + lineGap);
4184 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4185 (squareSize + lineGap);
4188 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4189 squareSize+lineGap, squareSize+lineGap);
4192 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4193 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4196 SetHighlights (int fromX, int fromY, int toX, int toY)
4198 if (hi1X != fromX || hi1Y != fromY) {
4199 if (hi1X >= 0 && hi1Y >= 0) {
4200 drawHighlight(hi1X, hi1Y, lineGC);
4202 } // [HGM] first erase both, then draw new!
4203 if (hi2X != toX || hi2Y != toY) {
4204 if (hi2X >= 0 && hi2Y >= 0) {
4205 drawHighlight(hi2X, hi2Y, lineGC);
4208 if (hi1X != fromX || hi1Y != fromY) {
4209 if (fromX >= 0 && fromY >= 0) {
4210 drawHighlight(fromX, fromY, highlineGC);
4213 if (hi2X != toX || hi2Y != toY) {
4214 if (toX >= 0 && toY >= 0) {
4215 drawHighlight(toX, toY, highlineGC);
4218 if(toX<0) // clearing the highlights must have damaged arrow
4219 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4229 SetHighlights(-1, -1, -1, -1);
4234 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4236 if (pm1X != fromX || pm1Y != fromY) {
4237 if (pm1X >= 0 && pm1Y >= 0) {
4238 drawHighlight(pm1X, pm1Y, lineGC);
4240 if (fromX >= 0 && fromY >= 0) {
4241 drawHighlight(fromX, fromY, prelineGC);
4244 if (pm2X != toX || pm2Y != toY) {
4245 if (pm2X >= 0 && pm2Y >= 0) {
4246 drawHighlight(pm2X, pm2Y, lineGC);
4248 if (toX >= 0 && toY >= 0) {
4249 drawHighlight(toX, toY, prelineGC);
4259 ClearPremoveHighlights ()
4261 SetPremoveHighlights(-1, -1, -1, -1);
4265 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4267 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4268 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4270 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4271 if(textureW[kind] < W*squareSize)
4272 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4274 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4275 if(textureH[kind] < H*squareSize)
4276 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4278 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4283 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4284 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4286 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4287 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4288 squareSize, squareSize, x*fac, y*fac);
4290 if (useImages && useImageSqs) {
4294 pm = xpmLightSquare;
4299 case 2: /* neutral */
4304 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4305 squareSize, squareSize, x*fac, y*fac);
4315 case 2: /* neutral */
4320 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4325 I split out the routines to draw a piece so that I could
4326 make a generic flash routine.
4329 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4331 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4332 switch (square_color) {
4334 case 2: /* neutral */
4336 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4337 ? *pieceToOutline(piece)
4338 : *pieceToSolid(piece),
4339 dest, bwPieceGC, 0, 0,
4340 squareSize, squareSize, x, y);
4343 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4344 ? *pieceToSolid(piece)
4345 : *pieceToOutline(piece),
4346 dest, wbPieceGC, 0, 0,
4347 squareSize, squareSize, x, y);
4353 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4355 switch (square_color) {
4357 case 2: /* neutral */
4359 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4360 ? *pieceToOutline(piece)
4361 : *pieceToSolid(piece),
4362 dest, bwPieceGC, 0, 0,
4363 squareSize, squareSize, x, y, 1);
4366 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4367 ? *pieceToSolid(piece)
4368 : *pieceToOutline(piece),
4369 dest, wbPieceGC, 0, 0,
4370 squareSize, squareSize, x, y, 1);
4376 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4378 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4379 switch (square_color) {
4381 XCopyPlane(xDisplay, *pieceToSolid(piece),
4382 dest, (int) piece < (int) BlackPawn
4383 ? wlPieceGC : blPieceGC, 0, 0,
4384 squareSize, squareSize, x, y, 1);
4387 XCopyPlane(xDisplay, *pieceToSolid(piece),
4388 dest, (int) piece < (int) BlackPawn
4389 ? wdPieceGC : bdPieceGC, 0, 0,
4390 squareSize, squareSize, x, y, 1);
4392 case 2: /* neutral */
4394 XCopyPlane(xDisplay, *pieceToSolid(piece),
4395 dest, (int) piece < (int) BlackPawn
4396 ? wjPieceGC : bjPieceGC, 0, 0,
4397 squareSize, squareSize, x, y, 1);
4403 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4405 int kind, p = piece;
4407 switch (square_color) {
4409 case 2: /* neutral */
4411 if ((int)piece < (int) BlackPawn) {
4419 if ((int)piece < (int) BlackPawn) {
4427 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4428 if(useTexture & square_color+1) {
4429 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4430 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4431 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4432 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4433 XSetClipMask(xDisplay, wlPieceGC, None);
4434 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4436 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4437 dest, wlPieceGC, 0, 0,
4438 squareSize, squareSize, x, y);
4441 typedef void (*DrawFunc)();
4446 if (appData.monoMode) {
4447 if (DefaultDepth(xDisplay, xScreen) == 1) {
4448 return monoDrawPiece_1bit;
4450 return monoDrawPiece;
4454 return colorDrawPieceImage;
4456 return colorDrawPiece;
4460 /* [HR] determine square color depending on chess variant. */
4462 SquareColor (int row, int column)
4466 if (gameInfo.variant == VariantXiangqi) {
4467 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4469 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4471 } else if (row <= 4) {
4477 square_color = ((column + row) % 2) == 1;
4480 /* [hgm] holdings: next line makes all holdings squares light */
4481 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4483 return square_color;
4487 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4489 int square_color, x, y, direction, font_ascent, font_descent;
4492 XCharStruct overall;
4496 /* Calculate delay in milliseconds (2-delays per complete flash) */
4497 flash_delay = 500 / appData.flashRate;
4500 x = lineGap + ((BOARD_WIDTH-1)-column) *
4501 (squareSize + lineGap);
4502 y = lineGap + row * (squareSize + lineGap);
4504 x = lineGap + column * (squareSize + lineGap);
4505 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4506 (squareSize + lineGap);
4509 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4511 square_color = SquareColor(row, column);
4513 if ( // [HGM] holdings: blank out area between board and holdings
4514 column == BOARD_LEFT-1 || column == BOARD_RGHT
4515 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4516 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4517 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4519 // [HGM] print piece counts next to holdings
4520 string[1] = NULLCHAR;
4521 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4522 string[0] = '0' + piece;
4523 XTextExtents(countFontStruct, string, 1, &direction,
4524 &font_ascent, &font_descent, &overall);
4525 if (appData.monoMode) {
4526 XDrawImageString(xDisplay, xBoardWindow, countGC,
4527 x + squareSize - overall.width - 2,
4528 y + font_ascent + 1, string, 1);
4530 XDrawString(xDisplay, xBoardWindow, countGC,
4531 x + squareSize - overall.width - 2,
4532 y + font_ascent + 1, string, 1);
4535 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4536 string[0] = '0' + piece;
4537 XTextExtents(countFontStruct, string, 1, &direction,
4538 &font_ascent, &font_descent, &overall);
4539 if (appData.monoMode) {
4540 XDrawImageString(xDisplay, xBoardWindow, countGC,
4541 x + 2, y + font_ascent + 1, string, 1);
4543 XDrawString(xDisplay, xBoardWindow, countGC,
4544 x + 2, y + font_ascent + 1, string, 1);
4548 if (piece == EmptySquare || appData.blindfold) {
4549 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4551 drawfunc = ChooseDrawFunc();
4553 if (do_flash && appData.flashCount > 0) {
4554 for (i=0; i<appData.flashCount; ++i) {
4555 drawfunc(piece, square_color, x, y, xBoardWindow);
4556 XSync(xDisplay, False);
4557 do_flash_delay(flash_delay);
4559 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4560 XSync(xDisplay, False);
4561 do_flash_delay(flash_delay);
4564 drawfunc(piece, square_color, x, y, xBoardWindow);
4568 string[1] = NULLCHAR;
4569 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4570 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4571 string[0] = 'a' + column - BOARD_LEFT;
4572 XTextExtents(coordFontStruct, string, 1, &direction,
4573 &font_ascent, &font_descent, &overall);
4574 if (appData.monoMode) {
4575 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4576 x + squareSize - overall.width - 2,
4577 y + squareSize - font_descent - 1, string, 1);
4579 XDrawString(xDisplay, xBoardWindow, coordGC,
4580 x + squareSize - overall.width - 2,
4581 y + squareSize - font_descent - 1, string, 1);
4584 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4585 string[0] = ONE + row;
4586 XTextExtents(coordFontStruct, string, 1, &direction,
4587 &font_ascent, &font_descent, &overall);
4588 if (appData.monoMode) {
4589 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4590 x + 2, y + font_ascent + 1, string, 1);
4592 XDrawString(xDisplay, xBoardWindow, coordGC,
4593 x + 2, y + font_ascent + 1, string, 1);
4596 if(!partnerUp && marker[row][column]) {
4597 if(appData.monoMode) {
4598 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4599 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4600 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4601 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4603 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4604 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4609 /* Why is this needed on some versions of X? */
4611 EventProc (Widget widget, caddr_t unused, XEvent *event)
4613 if (!XtIsRealized(widget))
4616 switch (event->type) {
4618 if (event->xexpose.count > 0) return; /* no clipping is done */
4619 XDrawPosition(widget, True, NULL);
4620 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4621 flipView = !flipView; partnerUp = !partnerUp;
4622 XDrawPosition(widget, True, NULL);
4623 flipView = !flipView; partnerUp = !partnerUp;
4627 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4635 DrawPosition (int fullRedraw, Board board)
4637 XDrawPosition(boardWidget, fullRedraw, board);
4640 /* Returns 1 if there are "too many" differences between b1 and b2
4641 (i.e. more than 1 move was made) */
4643 too_many_diffs (Board b1, Board b2)
4648 for (i=0; i<BOARD_HEIGHT; ++i) {
4649 for (j=0; j<BOARD_WIDTH; ++j) {
4650 if (b1[i][j] != b2[i][j]) {
4651 if (++c > 4) /* Castling causes 4 diffs */
4659 /* Matrix describing castling maneuvers */
4660 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4661 static int castling_matrix[4][5] = {
4662 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4663 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4664 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4665 { 7, 7, 4, 5, 6 } /* 0-0, black */
4668 /* Checks whether castling occurred. If it did, *rrow and *rcol
4669 are set to the destination (row,col) of the rook that moved.
4671 Returns 1 if castling occurred, 0 if not.
4673 Note: Only handles a max of 1 castling move, so be sure
4674 to call too_many_diffs() first.
4677 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4682 /* For each type of castling... */
4683 for (i=0; i<4; ++i) {
4684 r = castling_matrix[i];
4686 /* Check the 4 squares involved in the castling move */
4688 for (j=1; j<=4; ++j) {
4689 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4696 /* All 4 changed, so it must be a castling move */
4705 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4707 DrawSeekAxis (int x, int y, int xTo, int yTo)
4709 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4713 DrawSeekBackground (int left, int top, int right, int bottom)
4715 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4719 DrawSeekText (char *buf, int x, int y)
4721 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4725 DrawSeekDot (int x, int y, int colorNr)
4727 int square = colorNr & 0x80;
4730 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4732 XFillRectangle(xDisplay, xBoardWindow, color,
4733 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4735 XFillArc(xDisplay, xBoardWindow, color,
4736 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4739 static int damage[2][BOARD_RANKS][BOARD_FILES];
4742 * event handler for redrawing the board
4745 XDrawPosition (Widget w, int repaint, Board board)
4748 static int lastFlipView = 0;
4749 static int lastBoardValid[2] = {0, 0};
4750 static Board lastBoard[2];
4753 int nr = twoBoards*partnerUp;
4755 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4757 if (board == NULL) {
4758 if (!lastBoardValid[nr]) return;
4759 board = lastBoard[nr];
4761 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4762 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4763 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4768 * It would be simpler to clear the window with XClearWindow()
4769 * but this causes a very distracting flicker.
4772 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4774 if ( lineGap && IsDrawArrowEnabled())
4775 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4776 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4778 /* If too much changes (begin observing new game, etc.), don't
4780 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4782 /* Special check for castling so we don't flash both the king
4783 and the rook (just flash the king). */
4785 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4786 /* Draw rook with NO flashing. King will be drawn flashing later */
4787 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4788 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4792 /* First pass -- Draw (newly) empty squares and repair damage.
4793 This prevents you from having a piece show up twice while it
4794 is flashing on its new square */
4795 for (i = 0; i < BOARD_HEIGHT; i++)
4796 for (j = 0; j < BOARD_WIDTH; j++)
4797 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4798 || damage[nr][i][j]) {
4799 DrawSquare(i, j, board[i][j], 0);
4800 damage[nr][i][j] = False;
4803 /* Second pass -- Draw piece(s) in new position and flash them */
4804 for (i = 0; i < BOARD_HEIGHT; i++)
4805 for (j = 0; j < BOARD_WIDTH; j++)
4806 if (board[i][j] != lastBoard[nr][i][j]) {
4807 DrawSquare(i, j, board[i][j], do_flash);
4811 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4812 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4813 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4815 for (i = 0; i < BOARD_HEIGHT; i++)
4816 for (j = 0; j < BOARD_WIDTH; j++) {
4817 DrawSquare(i, j, board[i][j], 0);
4818 damage[nr][i][j] = False;
4822 CopyBoard(lastBoard[nr], board);
4823 lastBoardValid[nr] = 1;
4824 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4825 lastFlipView = flipView;
4827 /* Draw highlights */
4828 if (pm1X >= 0 && pm1Y >= 0) {
4829 drawHighlight(pm1X, pm1Y, prelineGC);
4831 if (pm2X >= 0 && pm2Y >= 0) {
4832 drawHighlight(pm2X, pm2Y, prelineGC);
4834 if (hi1X >= 0 && hi1Y >= 0) {
4835 drawHighlight(hi1X, hi1Y, highlineGC);
4837 if (hi2X >= 0 && hi2Y >= 0) {
4838 drawHighlight(hi2X, hi2Y, highlineGC);
4840 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4842 /* If piece being dragged around board, must redraw that too */
4845 XSync(xDisplay, False);
4850 * event handler for redrawing the board
4853 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4855 XDrawPosition(w, True, NULL);
4860 * event handler for parsing user moves
4862 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4863 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4864 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4865 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4866 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4867 // and at the end FinishMove() to perform the move after optional promotion popups.
4868 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4870 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4872 if (w != boardWidget || errorExitStatus != -1) return;
4873 if(nprms) shiftKey = !strcmp(prms[0], "1");
4876 if (event->type == ButtonPress) {
4877 XtPopdown(promotionShell);
4878 XtDestroyWidget(promotionShell);
4879 promotionUp = False;
4887 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4888 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4889 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4893 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4895 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4896 DragPieceMove(event->xmotion.x, event->xmotion.y);
4900 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4901 { // [HGM] pv: walk PV
4902 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4905 static int savedIndex; /* gross that this is global */
4908 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4911 XawTextPosition index, dummy;
4914 XawTextGetSelectionPos(w, &index, &dummy);
4915 XtSetArg(arg, XtNstring, &val);
4916 XtGetValues(w, &arg, 1);
4917 ReplaceComment(savedIndex, val);
4918 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4919 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4923 EditCommentPopUp (int index, char *title, char *text)
4926 if (text == NULL) text = "";
4927 NewCommentPopup(title, text, index);
4936 extern Option boxOptions[];
4946 edit = boxOptions[0].handle;
4948 XtSetArg(args[j], XtNstring, &val); j++;
4949 XtGetValues(edit, args, j);
4951 SendMultiLineToICS(val);
4952 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4953 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4957 ICSInputBoxPopDown ()
4963 CommentPopUp (char *title, char *text)
4965 savedIndex = currentMove; // [HGM] vari
4966 NewCommentPopup(title, text, currentMove);
4975 static char *openName;
4981 (void) (*fileProc)(openFP, 0, openName);
4985 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
4987 fileProc = proc; /* I can't see a way not */
4988 fileOpenMode = openMode; /* to use globals here */
4989 { // [HGM] use file-selector dialog stolen from Ghostview
4990 int index; // this is not supported yet
4991 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
4992 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
4993 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
4994 ScheduleDelayedEvent(&DelayedLoad, 50);
5001 if (!filenameUp) return;
5002 XtPopdown(fileNameShell);
5003 XtDestroyWidget(fileNameShell);
5009 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5014 XtSetArg(args[0], XtNlabel, &name);
5015 XtGetValues(w, args, 1);
5017 if (strcmp(name, _("cancel")) == 0) {
5022 FileNameAction(w, NULL, NULL, NULL);
5026 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5034 name = XawDialogGetValueString(w = XtParent(w));
5036 if ((name != NULL) && (*name != NULLCHAR)) {
5037 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5038 XtPopdown(w = XtParent(XtParent(w)));
5042 p = strrchr(buf, ' ');
5049 fullname = ExpandPathName(buf);
5051 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5054 f = fopen(fullname, fileOpenMode);
5056 DisplayError(_("Failed to open file"), errno);
5058 (void) (*fileProc)(f, index, buf);
5065 XtPopdown(w = XtParent(XtParent(w)));
5075 Widget dialog, layout;
5077 Dimension bw_width, pw_width;
5079 char *PromoChars = "wglcqrbnkac+=\0";
5082 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5083 XtGetValues(boardWidget, args, j);
5086 XtSetArg(args[j], XtNresizable, True); j++;
5087 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5089 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5090 shellWidget, args, j);
5092 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5093 layoutArgs, XtNumber(layoutArgs));
5096 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5097 XtSetArg(args[j], XtNborderWidth, 0); j++;
5098 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5101 if(gameInfo.variant != VariantShogi) {
5102 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5103 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5104 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5105 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5106 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5108 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5109 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5110 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5111 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5113 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5114 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5115 gameInfo.variant == VariantGiveaway) {
5116 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5118 if(gameInfo.variant == VariantCapablanca ||
5119 gameInfo.variant == VariantGothic ||
5120 gameInfo.variant == VariantCapaRandom) {
5121 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5122 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5124 } else // [HGM] shogi
5126 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5127 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5129 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5131 XtRealizeWidget(promotionShell);
5132 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5135 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5136 XtGetValues(promotionShell, args, j);
5138 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5139 lineGap + squareSize/3 +
5140 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5141 0 : 6*(squareSize + lineGap)), &x, &y);
5144 XtSetArg(args[j], XtNx, x); j++;
5145 XtSetArg(args[j], XtNy, y); j++;
5146 XtSetValues(promotionShell, args, j);
5148 XtPopup(promotionShell, XtGrabNone);
5156 if (!promotionUp) return;
5157 XtPopdown(promotionShell);
5158 XtDestroyWidget(promotionShell);
5159 promotionUp = False;
5163 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5165 int promoChar = * (const char *) client_data;
5169 if (fromX == -1) return;
5176 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5178 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5179 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5185 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5188 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5190 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5197 if (!errorUp) return;
5199 XtPopdown(errorShell);
5200 XtDestroyWidget(errorShell);
5201 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5205 ErrorPopUp (char *title, char *label, int modal)
5208 Widget dialog, layout;
5212 Dimension bw_width, pw_width;
5213 Dimension pw_height;
5217 XtSetArg(args[i], XtNresizable, True); i++;
5218 XtSetArg(args[i], XtNtitle, title); i++;
5220 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5221 shellWidget, args, i);
5223 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5224 layoutArgs, XtNumber(layoutArgs));
5227 XtSetArg(args[i], XtNlabel, label); i++;
5228 XtSetArg(args[i], XtNborderWidth, 0); i++;
5229 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5232 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5234 XtRealizeWidget(errorShell);
5235 CatchDeleteWindow(errorShell, "ErrorPopDown");
5238 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5239 XtGetValues(boardWidget, args, i);
5241 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5242 XtSetArg(args[i], XtNheight, &pw_height); i++;
5243 XtGetValues(errorShell, args, i);
5246 /* This code seems to tickle an X bug if it is executed too soon
5247 after xboard starts up. The coordinates get transformed as if
5248 the main window was positioned at (0, 0).
5250 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5251 0 - pw_height + squareSize / 3, &x, &y);
5253 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5254 RootWindowOfScreen(XtScreen(boardWidget)),
5255 (bw_width - pw_width) / 2,
5256 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5260 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5263 XtSetArg(args[i], XtNx, x); i++;
5264 XtSetArg(args[i], XtNy, y); i++;
5265 XtSetValues(errorShell, args, i);
5268 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5271 /* Disable all user input other than deleting the window */
5272 static int frozen = 0;
5278 /* Grab by a widget that doesn't accept input */
5279 XtAddGrab(messageWidget, TRUE, FALSE);
5283 /* Undo a FreezeUI */
5287 if (!frozen) return;
5288 XtRemoveGrab(messageWidget);
5293 ModeToWidgetName (GameMode mode)
5296 case BeginningOfGame:
5297 if (appData.icsActive)
5298 return "menuMode.ICS Client";
5299 else if (appData.noChessProgram ||
5300 *appData.cmailGameName != NULLCHAR)
5301 return "menuMode.Edit Game";
5303 return "menuMode.Machine Black";
5304 case MachinePlaysBlack:
5305 return "menuMode.Machine Black";
5306 case MachinePlaysWhite:
5307 return "menuMode.Machine White";
5309 return "menuMode.Analysis Mode";
5311 return "menuMode.Analyze File";
5312 case TwoMachinesPlay:
5313 return "menuMode.Two Machines";
5315 return "menuMode.Edit Game";
5316 case PlayFromGameFile:
5317 return "menuFile.Load Game";
5319 return "menuMode.Edit Position";
5321 return "menuMode.Training";
5322 case IcsPlayingWhite:
5323 case IcsPlayingBlack:
5327 return "menuMode.ICS Client";
5338 static int oldPausing = FALSE;
5339 static GameMode oldmode = (GameMode) -1;
5342 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5344 if (pausing != oldPausing) {
5345 oldPausing = pausing;
5347 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5349 XtSetArg(args[0], XtNleftBitmap, None);
5351 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5354 if (appData.showButtonBar) {
5355 /* Always toggle, don't set. Previous code messes up when
5356 invoked while the button is pressed, as releasing it
5357 toggles the state again. */
5360 XtSetArg(args[0], XtNbackground, &oldbg);
5361 XtSetArg(args[1], XtNforeground, &oldfg);
5362 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5364 XtSetArg(args[0], XtNbackground, oldfg);
5365 XtSetArg(args[1], XtNforeground, oldbg);
5367 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5371 wname = ModeToWidgetName(oldmode);
5372 if (wname != NULL) {
5373 XtSetArg(args[0], XtNleftBitmap, None);
5374 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5376 wname = ModeToWidgetName(gameMode);
5377 if (wname != NULL) {
5378 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5379 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5382 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5383 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5385 /* Maybe all the enables should be handled here, not just this one */
5386 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5387 gameMode == Training || gameMode == PlayFromGameFile);
5392 * Button/menu procedures
5395 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5401 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5403 cmailMsgLoaded = FALSE;
5404 if (gameNumber == 0) {
5405 int error = GameListBuild(f);
5407 DisplayError(_("Cannot build game list"), error);
5408 } else if (!ListEmpty(&gameList) &&
5409 ((ListGame *) gameList.tailPred)->number > 1) {
5410 GameListPopUp(f, title);
5416 return LoadGame(f, gameNumber, title, FALSE);
5420 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5422 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5425 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5429 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5435 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5441 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5447 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5453 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5459 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5465 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5467 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5470 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5474 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5476 FileNamePopUp(_("Save game file name?"),
5477 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5478 appData.oldSaveStyle ? ".game" : ".pgn",
5483 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5485 FileNamePopUp(_("Save position file name?"),
5486 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5487 appData.oldSaveStyle ? ".pos" : ".fen",
5492 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5494 ReloadCmailMsgEvent(FALSE);
5498 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5503 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5504 char *selected_fen_position=NULL;
5507 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5508 Atom *type_return, XtPointer *value_return,
5509 unsigned long *length_return, int *format_return)
5511 char *selection_tmp;
5513 if (!selected_fen_position) return False; /* should never happen */
5514 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5515 /* note: since no XtSelectionDoneProc was registered, Xt will
5516 * automatically call XtFree on the value returned. So have to
5517 * make a copy of it allocated with XtMalloc */
5518 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5519 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5521 *value_return=selection_tmp;
5522 *length_return=strlen(selection_tmp);
5523 *type_return=*target;
5524 *format_return = 8; /* bits per byte */
5526 } else if (*target == XA_TARGETS(xDisplay)) {
5527 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5528 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5529 targets_tmp[1] = XA_STRING;
5530 *value_return = targets_tmp;
5531 *type_return = XA_ATOM;
5534 // This code leads to a read of value_return out of bounds on 64-bit systems.
5535 // Other code which I have seen always sets *format_return to 32 independent of
5536 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5537 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5538 *format_return = 8 * sizeof(Atom);
5539 if (*format_return > 32) {
5540 *length_return *= *format_return / 32;
5541 *format_return = 32;
5544 *format_return = 32;
5552 /* note: when called from menu all parameters are NULL, so no clue what the
5553 * Widget which was clicked on was, or what the click event was
5556 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5559 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5560 * have a notion of a position that is selected but not copied.
5561 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5563 if(gameMode == EditPosition) EditPositionDone(TRUE);
5564 if (selected_fen_position) free(selected_fen_position);
5565 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5566 if (!selected_fen_position) return;
5567 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5569 SendPositionSelection,
5570 NULL/* lose_ownership_proc */ ,
5571 NULL/* transfer_done_proc */);
5572 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5574 SendPositionSelection,
5575 NULL/* lose_ownership_proc */ ,
5576 NULL/* transfer_done_proc */);
5579 /* function called when the data to Paste is ready */
5581 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5582 Atom *type, XtPointer value, unsigned long *len, int *format)
5585 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5586 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5587 EditPositionPasteFEN(fenstr);
5591 /* called when Paste Position button is pressed,
5592 * all parameters will be NULL */
5593 void PastePositionProc(w, event, prms, nprms)
5599 XtGetSelectionValue(menuBarWidget,
5600 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5601 /* (XtSelectionCallbackProc) */ PastePositionCB,
5602 NULL, /* client_data passed to PastePositionCB */
5604 /* better to use the time field from the event that triggered the
5605 * call to this function, but that isn't trivial to get
5613 SendGameSelection (Widget w, Atom *selection, Atom *target,
5614 Atom *type_return, XtPointer *value_return,
5615 unsigned long *length_return, int *format_return)
5617 char *selection_tmp;
5619 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5620 FILE* f = fopen(gameCopyFilename, "r");
5623 if (f == NULL) return False;
5627 selection_tmp = XtMalloc(len + 1);
5628 count = fread(selection_tmp, 1, len, f);
5631 XtFree(selection_tmp);
5634 selection_tmp[len] = NULLCHAR;
5635 *value_return = selection_tmp;
5636 *length_return = len;
5637 *type_return = *target;
5638 *format_return = 8; /* bits per byte */
5640 } else if (*target == XA_TARGETS(xDisplay)) {
5641 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5642 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5643 targets_tmp[1] = XA_STRING;
5644 *value_return = targets_tmp;
5645 *type_return = XA_ATOM;
5648 // This code leads to a read of value_return out of bounds on 64-bit systems.
5649 // Other code which I have seen always sets *format_return to 32 independent of
5650 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5651 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5652 *format_return = 8 * sizeof(Atom);
5653 if (*format_return > 32) {
5654 *length_return *= *format_return / 32;
5655 *format_return = 32;
5658 *format_return = 32;
5670 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5671 * have a notion of a game that is selected but not copied.
5672 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5674 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5677 NULL/* lose_ownership_proc */ ,
5678 NULL/* transfer_done_proc */);
5679 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5682 NULL/* lose_ownership_proc */ ,
5683 NULL/* transfer_done_proc */);
5686 /* note: when called from menu all parameters are NULL, so no clue what the
5687 * Widget which was clicked on was, or what the click event was
5690 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5694 ret = SaveGameToFile(gameCopyFilename, FALSE);
5701 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5703 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5707 /* function called when the data to Paste is ready */
5709 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5710 Atom *type, XtPointer value, unsigned long *len, int *format)
5713 if (value == NULL || *len == 0) {
5714 return; /* nothing had been selected to copy */
5716 f = fopen(gamePasteFilename, "w");
5718 DisplayError(_("Can't open temp file"), errno);
5721 fwrite(value, 1, *len, f);
5724 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5727 /* called when Paste Game button is pressed,
5728 * all parameters will be NULL */
5730 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5732 XtGetSelectionValue(menuBarWidget,
5733 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5734 /* (XtSelectionCallbackProc) */ PasteGameCB,
5735 NULL, /* client_data passed to PasteGameCB */
5737 /* better to use the time field from the event that triggered the
5738 * call to this function, but that isn't trivial to get
5749 SaveGameProc(NULL, NULL, NULL, NULL);
5754 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5760 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5766 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5768 MachineBlackEvent();
5772 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5774 MachineWhiteEvent();
5778 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5782 if (!first.analysisSupport) {
5783 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5784 DisplayError(buf, 0);
5787 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5788 if (appData.icsActive) {
5789 if (gameMode != IcsObserving) {
5790 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5791 DisplayError(buf, 0);
5793 if (appData.icsEngineAnalyze) {
5794 if (appData.debugMode)
5795 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5801 /* if enable, use want disable icsEngineAnalyze */
5802 if (appData.icsEngineAnalyze) {
5807 appData.icsEngineAnalyze = TRUE;
5808 if (appData.debugMode)
5809 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5811 #ifndef OPTIONSDIALOG
5812 if (!appData.showThinking)
5813 ShowThinkingProc(w,event,prms,nprms);
5820 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5822 if (!first.analysisSupport) {
5824 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5825 DisplayError(buf, 0);
5828 // Reset(FALSE, TRUE);
5829 #ifndef OPTIONSDIALOG
5830 if (!appData.showThinking)
5831 ShowThinkingProc(w,event,prms,nprms);
5834 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5835 AnalysisPeriodicEvent(1);
5839 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5845 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5851 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5857 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5863 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5865 EditPositionEvent();
5869 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5875 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5879 if (PopDown(1)) { // popdown succesful
5881 XtSetArg(args[j], XtNleftBitmap, None); j++;
5882 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5883 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5884 } else // was not up
5889 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5891 if (!PopDown(4)) ICSInputBoxPopUp();
5895 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5901 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5907 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5913 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5919 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5925 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5931 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5937 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5943 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5945 UserAdjudicationEvent(+1);
5949 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5951 UserAdjudicationEvent(-1);
5955 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5957 UserAdjudicationEvent(0);
5961 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5963 if (shellUp[4] == True)
5968 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5969 { // [HGM] input: let up-arrow recall previous line from history
5976 if (!shellUp[4]) return;
5977 edit = boxOptions[0].handle;
5979 XtSetArg(args[j], XtNstring, &val); j++;
5980 XtGetValues(edit, args, j);
5981 val = PrevInHistory(val);
5982 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5983 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5985 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
5986 XawTextReplace(edit, 0, 0, &t);
5987 XawTextSetInsertionPoint(edit, 9999);
5992 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5993 { // [HGM] input: let down-arrow recall next line from history
5998 if (!shellUp[4]) return;
5999 edit = boxOptions[0].handle;
6000 val = NextInHistory();
6001 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6002 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6004 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6005 XawTextReplace(edit, 0, 0, &t);
6006 XawTextSetInsertionPoint(edit, 9999);
6011 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6013 StopObservingEvent();
6017 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6019 StopExaminingEvent();
6023 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6030 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6037 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6043 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6045 if (!TempBackwardActive) {
6046 TempBackwardActive = True;
6052 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6054 /* Check to see if triggered by a key release event for a repeating key.
6055 * If so the next queued event will be a key press of the same key at the same time */
6056 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6058 XPeekEvent(xDisplay, &next);
6059 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6060 next.xkey.keycode == event->xkey.keycode)
6064 TempBackwardActive = False;
6068 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6074 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6080 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6086 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6092 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6094 TruncateGameEvent();
6098 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6104 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6110 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6112 flipView = !flipView;
6113 DrawPosition(True, NULL);
6117 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6121 PonderNextMoveEvent(!appData.ponderNextMove);
6122 #ifndef OPTIONSDIALOG
6123 if (appData.ponderNextMove) {
6124 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6126 XtSetArg(args[0], XtNleftBitmap, None);
6128 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6133 #ifndef OPTIONSDIALOG
6135 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6139 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6141 if (appData.alwaysPromoteToQueen) {
6142 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6144 XtSetArg(args[0], XtNleftBitmap, None);
6146 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6151 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6155 appData.animateDragging = !appData.animateDragging;
6157 if (appData.animateDragging) {
6158 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6161 XtSetArg(args[0], XtNleftBitmap, None);
6163 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6168 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6172 appData.animate = !appData.animate;
6174 if (appData.animate) {
6175 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6178 XtSetArg(args[0], XtNleftBitmap, None);
6180 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6185 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6189 appData.autoCallFlag = !appData.autoCallFlag;
6191 if (appData.autoCallFlag) {
6192 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6194 XtSetArg(args[0], XtNleftBitmap, None);
6196 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6201 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6205 appData.autoFlipView = !appData.autoFlipView;
6207 if (appData.autoFlipView) {
6208 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6210 XtSetArg(args[0], XtNleftBitmap, None);
6212 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6217 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6221 appData.blindfold = !appData.blindfold;
6223 if (appData.blindfold) {
6224 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6226 XtSetArg(args[0], XtNleftBitmap, None);
6228 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6231 DrawPosition(True, NULL);
6235 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6239 appData.testLegality = !appData.testLegality;
6241 if (appData.testLegality) {
6242 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6244 XtSetArg(args[0], XtNleftBitmap, None);
6246 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6252 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6256 if (appData.flashCount == 0) {
6257 appData.flashCount = 3;
6259 appData.flashCount = -appData.flashCount;
6262 if (appData.flashCount > 0) {
6263 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6265 XtSetArg(args[0], XtNleftBitmap, None);
6267 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6273 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6277 appData.highlightDragging = !appData.highlightDragging;
6279 if (appData.highlightDragging) {
6280 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6282 XtSetArg(args[0], XtNleftBitmap, None);
6284 XtSetValues(XtNameToWidget(menuBarWidget,
6285 "menuOptions.Highlight Dragging"), args, 1);
6290 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6294 appData.highlightLastMove = !appData.highlightLastMove;
6296 if (appData.highlightLastMove) {
6297 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6299 XtSetArg(args[0], XtNleftBitmap, None);
6301 XtSetValues(XtNameToWidget(menuBarWidget,
6302 "menuOptions.Highlight Last Move"), args, 1);
6306 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6310 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6312 if (appData.highlightMoveWithArrow) {
6313 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6315 XtSetArg(args[0], XtNleftBitmap, None);
6317 XtSetValues(XtNameToWidget(menuBarWidget,
6318 "menuOptions.Arrow"), args, 1);
6323 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6327 appData.icsAlarm = !appData.icsAlarm;
6329 if (appData.icsAlarm) {
6330 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6332 XtSetArg(args[0], XtNleftBitmap, None);
6334 XtSetValues(XtNameToWidget(menuBarWidget,
6335 "menuOptions.ICS Alarm"), args, 1);
6340 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6344 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6346 if (appData.ringBellAfterMoves) {
6347 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6349 XtSetArg(args[0], XtNleftBitmap, None);
6351 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6356 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6360 appData.oneClick = !appData.oneClick;
6362 if (appData.oneClick) {
6363 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6365 XtSetArg(args[0], XtNleftBitmap, None);
6367 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6372 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6376 PeriodicUpdatesEvent(!appData.periodicUpdates);
6378 if (appData.periodicUpdates) {
6379 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6381 XtSetArg(args[0], XtNleftBitmap, None);
6383 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6388 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6392 appData.popupExitMessage = !appData.popupExitMessage;
6394 if (appData.popupExitMessage) {
6395 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6397 XtSetArg(args[0], XtNleftBitmap, None);
6399 XtSetValues(XtNameToWidget(menuBarWidget,
6400 "menuOptions.Popup Exit Message"), args, 1);
6404 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6408 appData.popupMoveErrors = !appData.popupMoveErrors;
6410 if (appData.popupMoveErrors) {
6411 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6413 XtSetArg(args[0], XtNleftBitmap, None);
6415 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6421 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6425 appData.premove = !appData.premove;
6427 if (appData.premove) {
6428 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6430 XtSetArg(args[0], XtNleftBitmap, None);
6432 XtSetValues(XtNameToWidget(menuBarWidget,
6433 "menuOptions.Premove"), args, 1);
6438 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6442 appData.showCoords = !appData.showCoords;
6444 if (appData.showCoords) {
6445 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6447 XtSetArg(args[0], XtNleftBitmap, None);
6449 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6452 DrawPosition(True, NULL);
6456 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6458 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6459 ShowThinkingEvent();
6463 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6467 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6468 ShowThinkingEvent();
6470 if (appData.hideThinkingFromHuman) {
6471 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6473 XtSetArg(args[0], XtNleftBitmap, None);
6475 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6481 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6485 saveSettingsOnExit = !saveSettingsOnExit;
6487 if (saveSettingsOnExit) {
6488 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6490 XtSetArg(args[0], XtNleftBitmap, None);
6492 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6497 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6499 SaveSettings(settingsFileName);
6503 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6506 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6512 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6516 if (nprms && *nprms > 0)
6520 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6525 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6531 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6537 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6541 char *zippy = _(" (with Zippy code)");
6545 snprintf(buf, sizeof(buf),
6547 "Copyright 1991 Digital Equipment Corporation\n"
6548 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6549 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6550 "%s is free software and carries NO WARRANTY;"
6551 "see the file COPYING for more information."),
6552 programVersion, zippy, PACKAGE);
6553 ErrorPopUp(_("About XBoard"), buf, FALSE);
6557 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6559 appData.debugMode = !appData.debugMode;
6563 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6569 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6575 DisplayMessage (char *message, char *extMessage)
6577 /* display a message in the message widget */
6586 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6591 message = extMessage;
6595 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6597 /* need to test if messageWidget already exists, since this function
6598 can also be called during the startup, if for example a Xresource
6599 is not set up correctly */
6602 XtSetArg(arg, XtNlabel, message);
6603 XtSetValues(messageWidget, &arg, 1);
6610 DisplayTitle (char *text)
6614 char title[MSG_SIZ];
6617 if (text == NULL) text = "";
6619 if (appData.titleInWindow) {
6621 XtSetArg(args[i], XtNlabel, text); i++;
6622 XtSetValues(titleWidget, args, i);
6625 if (*text != NULLCHAR) {
6626 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6627 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6628 } else if (appData.icsActive) {
6629 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6630 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6631 } else if (appData.cmailGameName[0] != NULLCHAR) {
6632 snprintf(icon, sizeof(icon), "%s", "CMail");
6633 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6635 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6636 } else if (gameInfo.variant == VariantGothic) {
6637 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6638 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6641 } else if (gameInfo.variant == VariantFalcon) {
6642 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6643 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6645 } else if (appData.noChessProgram) {
6646 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6647 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6649 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6650 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6653 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6654 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6655 XtSetValues(shellWidget, args, i);
6656 XSync(xDisplay, False);
6661 DisplayError (String message, int error)
6666 if (appData.debugMode || appData.matchMode) {
6667 fprintf(stderr, "%s: %s\n", programName, message);
6670 if (appData.debugMode || appData.matchMode) {
6671 fprintf(stderr, "%s: %s: %s\n",
6672 programName, message, strerror(error));
6674 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6677 ErrorPopUp(_("Error"), message, FALSE);
6682 DisplayMoveError (String message)
6686 DrawPosition(FALSE, NULL);
6687 if (appData.debugMode || appData.matchMode) {
6688 fprintf(stderr, "%s: %s\n", programName, message);
6690 if (appData.popupMoveErrors) {
6691 ErrorPopUp(_("Error"), message, FALSE);
6693 DisplayMessage(message, "");
6699 DisplayFatalError (String message, int error, int status)
6703 errorExitStatus = status;
6705 fprintf(stderr, "%s: %s\n", programName, message);
6707 fprintf(stderr, "%s: %s: %s\n",
6708 programName, message, strerror(error));
6709 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6712 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6713 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6720 DisplayInformation (String message)
6723 ErrorPopUp(_("Information"), message, TRUE);
6727 DisplayNote (String message)
6730 ErrorPopUp(_("Note"), message, FALSE);
6734 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6740 DisplayIcsInteractionTitle (String message)
6742 if (oldICSInteractionTitle == NULL) {
6743 /* Magic to find the old window title, adapted from vim */
6744 char *wina = getenv("WINDOWID");
6746 Window win = (Window) atoi(wina);
6747 Window root, parent, *children;
6748 unsigned int nchildren;
6749 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6751 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6752 if (!XQueryTree(xDisplay, win, &root, &parent,
6753 &children, &nchildren)) break;
6754 if (children) XFree((void *)children);
6755 if (parent == root || parent == 0) break;
6758 XSetErrorHandler(oldHandler);
6760 if (oldICSInteractionTitle == NULL) {
6761 oldICSInteractionTitle = "xterm";
6764 printf("\033]0;%s\007", message);
6768 char pendingReplyPrefix[MSG_SIZ];
6769 ProcRef pendingReplyPR;
6772 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6775 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6779 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6783 AskQuestionPopDown ()
6785 if (!askQuestionUp) return;
6786 XtPopdown(askQuestionShell);
6787 XtDestroyWidget(askQuestionShell);
6788 askQuestionUp = False;
6792 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6798 reply = XawDialogGetValueString(w = XtParent(w));
6799 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6800 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6801 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6802 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6803 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6804 AskQuestionPopDown();
6806 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6810 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6815 XtSetArg(args[0], XtNlabel, &name);
6816 XtGetValues(w, args, 1);
6818 if (strcmp(name, _("cancel")) == 0) {
6819 AskQuestionPopDown();
6821 AskQuestionReplyAction(w, NULL, NULL, NULL);
6826 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6829 Widget popup, layout, dialog, edit;
6835 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6836 pendingReplyPR = pr;
6839 XtSetArg(args[i], XtNresizable, True); i++;
6840 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6841 askQuestionShell = popup =
6842 XtCreatePopupShell(title, transientShellWidgetClass,
6843 shellWidget, args, i);
6846 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6847 layoutArgs, XtNumber(layoutArgs));
6850 XtSetArg(args[i], XtNlabel, question); i++;
6851 XtSetArg(args[i], XtNvalue, ""); i++;
6852 XtSetArg(args[i], XtNborderWidth, 0); i++;
6853 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6856 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6857 (XtPointer) dialog);
6858 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6859 (XtPointer) dialog);
6861 XtRealizeWidget(popup);
6862 CatchDeleteWindow(popup, "AskQuestionPopDown");
6864 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6865 &x, &y, &win_x, &win_y, &mask);
6867 XtSetArg(args[0], XtNx, x - 10);
6868 XtSetArg(args[1], XtNy, y - 30);
6869 XtSetValues(popup, args, 2);
6871 XtPopup(popup, XtGrabExclusive);
6872 askQuestionUp = True;
6874 edit = XtNameToWidget(dialog, "*value");
6875 XtSetKeyboardFocus(popup, edit);
6880 PlaySound (char *name)
6882 if (*name == NULLCHAR) {
6884 } else if (strcmp(name, "$") == 0) {
6885 putc(BELLCHAR, stderr);
6888 char *prefix = "", *sep = "";
6889 if(appData.soundProgram[0] == NULLCHAR) return;
6890 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6891 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6899 PlaySound(appData.soundMove);
6905 PlaySound(appData.soundIcsWin);
6911 PlaySound(appData.soundIcsLoss);
6917 PlaySound(appData.soundIcsDraw);
6921 PlayIcsUnfinishedSound ()
6923 PlaySound(appData.soundIcsUnfinished);
6929 PlaySound(appData.soundIcsAlarm);
6935 PlaySound(appData.soundTell);
6941 system("stty echo");
6948 system("stty -echo");
6953 RunCommand (char *buf)
6959 Colorize (ColorClass cc, int continuation)
6962 int count, outCount, error;
6964 if (textColors[(int)cc].bg > 0) {
6965 if (textColors[(int)cc].fg > 0) {
6966 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
6967 textColors[(int)cc].fg, textColors[(int)cc].bg);
6969 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
6970 textColors[(int)cc].bg);
6973 if (textColors[(int)cc].fg > 0) {
6974 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
6975 textColors[(int)cc].fg);
6977 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
6980 count = strlen(buf);
6981 outCount = OutputToProcess(NoProc, buf, count, &error);
6982 if (outCount < count) {
6983 DisplayFatalError(_("Error writing to display"), error, 1);
6986 if (continuation) return;
6989 PlaySound(appData.soundShout);
6992 PlaySound(appData.soundSShout);
6995 PlaySound(appData.soundChannel1);
6998 PlaySound(appData.soundChannel);
7001 PlaySound(appData.soundKibitz);
7004 PlaySound(appData.soundTell);
7006 case ColorChallenge:
7007 PlaySound(appData.soundChallenge);
7010 PlaySound(appData.soundRequest);
7013 PlaySound(appData.soundSeek);
7025 return getpwuid(getuid())->pw_name;
7029 ExpandPathName (char *path)
7031 static char static_buf[4*MSG_SIZ];
7032 char *d, *s, buf[4*MSG_SIZ];
7038 while (*s && isspace(*s))
7047 if (*(s+1) == '/') {
7048 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7052 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7053 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7054 pwd = getpwnam(buf);
7057 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7061 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7062 strcat(d, strchr(s+1, '/'));
7066 safeStrCpy(d, s, 4*MSG_SIZ );
7074 static char host_name[MSG_SIZ];
7076 #if HAVE_GETHOSTNAME
7077 gethostname(host_name, MSG_SIZ);
7079 #else /* not HAVE_GETHOSTNAME */
7080 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7081 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7083 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7085 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7086 #endif /* not HAVE_GETHOSTNAME */
7089 XtIntervalId delayedEventTimerXID = 0;
7090 DelayedEventCallback delayedEventCallback = 0;
7095 delayedEventTimerXID = 0;
7096 delayedEventCallback();
7100 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7102 if(delayedEventTimerXID && delayedEventCallback == cb)
7103 // [HGM] alive: replace, rather than add or flush identical event
7104 XtRemoveTimeOut(delayedEventTimerXID);
7105 delayedEventCallback = cb;
7106 delayedEventTimerXID =
7107 XtAppAddTimeOut(appContext, millisec,
7108 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7111 DelayedEventCallback
7114 if (delayedEventTimerXID) {
7115 return delayedEventCallback;
7122 CancelDelayedEvent ()
7124 if (delayedEventTimerXID) {
7125 XtRemoveTimeOut(delayedEventTimerXID);
7126 delayedEventTimerXID = 0;
7130 XtIntervalId loadGameTimerXID = 0;
7133 LoadGameTimerRunning ()
7135 return loadGameTimerXID != 0;
7139 StopLoadGameTimer ()
7141 if (loadGameTimerXID != 0) {
7142 XtRemoveTimeOut(loadGameTimerXID);
7143 loadGameTimerXID = 0;
7151 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7153 loadGameTimerXID = 0;
7158 StartLoadGameTimer (long millisec)
7161 XtAppAddTimeOut(appContext, millisec,
7162 (XtTimerCallbackProc) LoadGameTimerCallback,
7166 XtIntervalId analysisClockXID = 0;
7169 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7171 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7172 || appData.icsEngineAnalyze) { // [DM]
7173 AnalysisPeriodicEvent(0);
7174 StartAnalysisClock();
7179 StartAnalysisClock ()
7182 XtAppAddTimeOut(appContext, 2000,
7183 (XtTimerCallbackProc) AnalysisClockCallback,
7187 XtIntervalId clockTimerXID = 0;
7190 ClockTimerRunning ()
7192 return clockTimerXID != 0;
7198 if (clockTimerXID != 0) {
7199 XtRemoveTimeOut(clockTimerXID);
7208 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7215 StartClockTimer (long millisec)
7218 XtAppAddTimeOut(appContext, millisec,
7219 (XtTimerCallbackProc) ClockTimerCallback,
7224 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7229 /* check for low time warning */
7230 Pixel foregroundOrWarningColor = timerForegroundPixel;
7233 appData.lowTimeWarning &&
7234 (timer / 1000) < appData.icsAlarmTime)
7235 foregroundOrWarningColor = lowTimeWarningColor;
7237 if (appData.clockMode) {
7238 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7239 XtSetArg(args[0], XtNlabel, buf);
7241 snprintf(buf, MSG_SIZ, "%s ", color);
7242 XtSetArg(args[0], XtNlabel, buf);
7247 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7248 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7250 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7251 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7254 XtSetValues(w, args, 3);
7258 DisplayWhiteClock (long timeRemaining, int highlight)
7262 if(appData.noGUI) return;
7263 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7264 if (highlight && iconPixmap == bIconPixmap) {
7265 iconPixmap = wIconPixmap;
7266 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7267 XtSetValues(shellWidget, args, 1);
7272 DisplayBlackClock (long timeRemaining, int highlight)
7276 if(appData.noGUI) return;
7277 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7278 if (highlight && iconPixmap == wIconPixmap) {
7279 iconPixmap = bIconPixmap;
7280 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7281 XtSetValues(shellWidget, args, 1);
7300 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7304 int to_prog[2], from_prog[2];
7308 if (appData.debugMode) {
7309 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7312 /* We do NOT feed the cmdLine to the shell; we just
7313 parse it into blank-separated arguments in the
7314 most simple-minded way possible.
7317 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7320 while(*p == ' ') p++;
7322 if(*p == '"' || *p == '\'')
7323 p = strchr(++argv[i-1], *p);
7324 else p = strchr(p, ' ');
7325 if (p == NULL) break;
7330 SetUpChildIO(to_prog, from_prog);
7332 if ((pid = fork()) == 0) {
7334 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7335 close(to_prog[1]); // first close the unused pipe ends
7336 close(from_prog[0]);
7337 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7338 dup2(from_prog[1], 1);
7339 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7340 close(from_prog[1]); // and closing again loses one of the pipes!
7341 if(fileno(stderr) >= 2) // better safe than sorry...
7342 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7344 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7349 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7351 execvp(argv[0], argv);
7353 /* If we get here, exec failed */
7358 /* Parent process */
7360 close(from_prog[1]);
7362 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7365 cp->fdFrom = from_prog[0];
7366 cp->fdTo = to_prog[1];
7371 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7373 AlarmCallBack (int n)
7379 DestroyChildProcess (ProcRef pr, int signalType)
7381 ChildProc *cp = (ChildProc *) pr;
7383 if (cp->kind != CPReal) return;
7385 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7386 signal(SIGALRM, AlarmCallBack);
7388 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7389 kill(cp->pid, SIGKILL); // kill it forcefully
7390 wait((int *) 0); // and wait again
7394 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7396 /* Process is exiting either because of the kill or because of
7397 a quit command sent by the backend; either way, wait for it to die.
7406 InterruptChildProcess (ProcRef pr)
7408 ChildProc *cp = (ChildProc *) pr;
7410 if (cp->kind != CPReal) return;
7411 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7415 OpenTelnet (char *host, char *port, ProcRef *pr)
7417 char cmdLine[MSG_SIZ];
7419 if (port[0] == NULLCHAR) {
7420 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7422 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7424 return StartChildProcess(cmdLine, "", pr);
7428 OpenTCP (char *host, char *port, ProcRef *pr)
7431 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7432 #else /* !OMIT_SOCKETS */
7433 struct addrinfo hints;
7434 struct addrinfo *ais, *ai;
7439 memset(&hints, 0, sizeof(hints));
7440 hints.ai_family = AF_UNSPEC;
7441 hints.ai_socktype = SOCK_STREAM;
7443 error = getaddrinfo(host, port, &hints, &ais);
7445 /* a getaddrinfo error is not an errno, so can't return it */
7446 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7447 host, port, gai_strerror(error));
7451 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7452 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7456 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7469 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7475 #endif /* !OMIT_SOCKETS */
7481 OpenCommPort (char *name, ProcRef *pr)
7486 fd = open(name, 2, 0);
7487 if (fd < 0) return errno;
7489 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7500 OpenLoopback (ProcRef *pr)
7505 SetUpChildIO(to, from);
7507 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7510 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7518 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7520 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7524 #define INPUT_SOURCE_BUF_SIZE 8192
7533 char buf[INPUT_SOURCE_BUF_SIZE];
7538 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7540 InputSource *is = (InputSource *) closure;
7545 if (is->lineByLine) {
7546 count = read(is->fd, is->unused,
7547 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7549 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7552 is->unused += count;
7554 while (p < is->unused) {
7555 q = memchr(p, '\n', is->unused - p);
7556 if (q == NULL) break;
7558 (is->func)(is, is->closure, p, q - p, 0);
7562 while (p < is->unused) {
7567 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7572 (is->func)(is, is->closure, is->buf, count, error);
7577 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7580 ChildProc *cp = (ChildProc *) pr;
7582 is = (InputSource *) calloc(1, sizeof(InputSource));
7583 is->lineByLine = lineByLine;
7587 is->fd = fileno(stdin);
7589 is->kind = cp->kind;
7590 is->fd = cp->fdFrom;
7593 is->unused = is->buf;
7596 is->xid = XtAppAddInput(appContext, is->fd,
7597 (XtPointer) (XtInputReadMask),
7598 (XtInputCallbackProc) DoInputCallback,
7600 is->closure = closure;
7601 return (InputSourceRef) is;
7605 RemoveInputSource (InputSourceRef isr)
7607 InputSource *is = (InputSource *) isr;
7609 if (is->xid == 0) return;
7610 XtRemoveInput(is->xid);
7615 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7617 static int line = 0;
7618 ChildProc *cp = (ChildProc *) pr;
7623 if (appData.noJoin || !appData.useInternalWrap)
7624 outCount = fwrite(message, 1, count, stdout);
7627 int width = get_term_width();
7628 int len = wrap(NULL, message, count, width, &line);
7629 char *msg = malloc(len);
7633 outCount = fwrite(message, 1, count, stdout);
7636 dbgchk = wrap(msg, message, count, width, &line);
7637 if (dbgchk != len && appData.debugMode)
7638 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7639 outCount = fwrite(msg, 1, dbgchk, stdout);
7645 outCount = write(cp->fdTo, message, count);
7655 /* Output message to process, with "ms" milliseconds of delay
7656 between each character. This is needed when sending the logon
7657 script to ICC, which for some reason doesn't like the
7658 instantaneous send. */
7660 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7662 ChildProc *cp = (ChildProc *) pr;
7667 r = write(cp->fdTo, message++, 1);
7680 /**** Animation code by Hugh Fisher, DCS, ANU.
7682 Known problem: if a window overlapping the board is
7683 moved away while a piece is being animated underneath,
7684 the newly exposed area won't be updated properly.
7685 I can live with this.
7687 Known problem: if you look carefully at the animation
7688 of pieces in mono mode, they are being drawn as solid
7689 shapes without interior detail while moving. Fixing
7690 this would be a major complication for minimal return.
7693 /* Masks for XPM pieces. Black and white pieces can have
7694 different shapes, but in the interest of retaining my
7695 sanity pieces must have the same outline on both light
7696 and dark squares, and all pieces must use the same
7697 background square colors/images. */
7699 static int xpmDone = 0;
7702 CreateAnimMasks (int pieceDepth)
7708 unsigned long plane;
7711 /* Need a bitmap just to get a GC with right depth */
7712 buf = XCreatePixmap(xDisplay, xBoardWindow,
7714 values.foreground = 1;
7715 values.background = 0;
7716 /* Don't use XtGetGC, not read only */
7717 maskGC = XCreateGC(xDisplay, buf,
7718 GCForeground | GCBackground, &values);
7719 XFreePixmap(xDisplay, buf);
7721 buf = XCreatePixmap(xDisplay, xBoardWindow,
7722 squareSize, squareSize, pieceDepth);
7723 values.foreground = XBlackPixel(xDisplay, xScreen);
7724 values.background = XWhitePixel(xDisplay, xScreen);
7725 bufGC = XCreateGC(xDisplay, buf,
7726 GCForeground | GCBackground, &values);
7728 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7729 /* Begin with empty mask */
7730 if(!xpmDone) // [HGM] pieces: keep using existing
7731 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7732 squareSize, squareSize, 1);
7733 XSetFunction(xDisplay, maskGC, GXclear);
7734 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7735 0, 0, squareSize, squareSize);
7737 /* Take a copy of the piece */
7742 XSetFunction(xDisplay, bufGC, GXcopy);
7743 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7745 0, 0, squareSize, squareSize, 0, 0);
7747 /* XOR the background (light) over the piece */
7748 XSetFunction(xDisplay, bufGC, GXxor);
7750 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7751 0, 0, squareSize, squareSize, 0, 0);
7753 XSetForeground(xDisplay, bufGC, lightSquareColor);
7754 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7757 /* We now have an inverted piece image with the background
7758 erased. Construct mask by just selecting all the non-zero
7759 pixels - no need to reconstruct the original image. */
7760 XSetFunction(xDisplay, maskGC, GXor);
7762 /* Might be quicker to download an XImage and create bitmap
7763 data from it rather than this N copies per piece, but it
7764 only takes a fraction of a second and there is a much
7765 longer delay for loading the pieces. */
7766 for (n = 0; n < pieceDepth; n ++) {
7767 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7768 0, 0, squareSize, squareSize,
7774 XFreePixmap(xDisplay, buf);
7775 XFreeGC(xDisplay, bufGC);
7776 XFreeGC(xDisplay, maskGC);
7780 InitAnimState (AnimState *anim, XWindowAttributes *info)
7785 /* Each buffer is square size, same depth as window */
7786 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7787 squareSize, squareSize, info->depth);
7788 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7789 squareSize, squareSize, info->depth);
7791 /* Create a plain GC for blitting */
7792 mask = GCForeground | GCBackground | GCFunction |
7793 GCPlaneMask | GCGraphicsExposures;
7794 values.foreground = XBlackPixel(xDisplay, xScreen);
7795 values.background = XWhitePixel(xDisplay, xScreen);
7796 values.function = GXcopy;
7797 values.plane_mask = AllPlanes;
7798 values.graphics_exposures = False;
7799 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7801 /* Piece will be copied from an existing context at
7802 the start of each new animation/drag. */
7803 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7805 /* Outline will be a read-only copy of an existing */
7806 anim->outlineGC = None;
7812 XWindowAttributes info;
7814 if (xpmDone && gameInfo.variant == oldVariant) return;
7815 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7816 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7818 InitAnimState(&game, &info);
7819 InitAnimState(&player, &info);
7821 /* For XPM pieces, we need bitmaps to use as masks. */
7823 CreateAnimMasks(info.depth), xpmDone = 1;
7828 static Boolean frameWaiting;
7831 FrameAlarm (int sig)
7833 frameWaiting = False;
7834 /* In case System-V style signals. Needed?? */
7835 signal(SIGALRM, FrameAlarm);
7839 FrameDelay (int time)
7841 struct itimerval delay;
7843 XSync(xDisplay, False);
7846 frameWaiting = True;
7847 signal(SIGALRM, FrameAlarm);
7848 delay.it_interval.tv_sec =
7849 delay.it_value.tv_sec = time / 1000;
7850 delay.it_interval.tv_usec =
7851 delay.it_value.tv_usec = (time % 1000) * 1000;
7852 setitimer(ITIMER_REAL, &delay, NULL);
7853 while (frameWaiting) pause();
7854 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7855 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7856 setitimer(ITIMER_REAL, &delay, NULL);
7863 FrameDelay (int time)
7865 XSync(xDisplay, False);
7867 usleep(time * 1000);
7878 /* Convert board position to corner of screen rect and color */
7881 ScreenSquare (int column, int row, XPoint *pt, int *color)
7884 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7885 pt->y = lineGap + row * (squareSize + lineGap);
7887 pt->x = lineGap + column * (squareSize + lineGap);
7888 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7890 *color = SquareColor(row, column);
7893 /* Convert window coords to square */
7896 BoardSquare (int x, int y, int *column, int *row)
7898 *column = EventToSquare(x, BOARD_WIDTH);
7899 if (flipView && *column >= 0)
7900 *column = BOARD_WIDTH - 1 - *column;
7901 *row = EventToSquare(y, BOARD_HEIGHT);
7902 if (!flipView && *row >= 0)
7903 *row = BOARD_HEIGHT - 1 - *row;
7908 #undef Max /* just in case */
7910 #define Max(a, b) ((a) > (b) ? (a) : (b))
7911 #define Min(a, b) ((a) < (b) ? (a) : (b))
7914 SetRect (XRectangle *rect, int x, int y, int width, int height)
7918 rect->width = width;
7919 rect->height = height;
7922 /* Test if two frames overlap. If they do, return
7923 intersection rect within old and location of
7924 that rect within new. */
7927 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
7929 if (old->x > new->x + size || new->x > old->x + size ||
7930 old->y > new->y + size || new->y > old->y + size) {
7933 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7934 size - abs(old->x - new->x), size - abs(old->y - new->y));
7935 pt->x = Max(old->x - new->x, 0);
7936 pt->y = Max(old->y - new->y, 0);
7941 /* For two overlapping frames, return the rect(s)
7942 in the old that do not intersect with the new. */
7945 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
7949 /* If old = new (shouldn't happen) then nothing to draw */
7950 if (old->x == new->x && old->y == new->y) {
7954 /* Work out what bits overlap. Since we know the rects
7955 are the same size we don't need a full intersect calc. */
7957 /* Top or bottom edge? */
7958 if (new->y > old->y) {
7959 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
7961 } else if (old->y > new->y) {
7962 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
7963 size, old->y - new->y);
7966 /* Left or right edge - don't overlap any update calculated above. */
7967 if (new->x > old->x) {
7968 SetRect(&(update[count]), old->x, Max(new->y, old->y),
7969 new->x - old->x, size - abs(new->y - old->y));
7971 } else if (old->x > new->x) {
7972 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
7973 old->x - new->x, size - abs(new->y - old->y));
7980 /* Generate a series of frame coords from start->mid->finish.
7981 The movement rate doubles until the half way point is
7982 reached, then halves back down to the final destination,
7983 which gives a nice slow in/out effect. The algorithmn
7984 may seem to generate too many intermediates for short
7985 moves, but remember that the purpose is to attract the
7986 viewers attention to the piece about to be moved and
7987 then to where it ends up. Too few frames would be less
7991 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
7993 int fraction, n, count;
7997 /* Slow in, stepping 1/16th, then 1/8th, ... */
7999 for (n = 0; n < factor; n++)
8001 for (n = 0; n < factor; n++) {
8002 frames[count].x = start->x + (mid->x - start->x) / fraction;
8003 frames[count].y = start->y + (mid->y - start->y) / fraction;
8005 fraction = fraction / 2;
8009 frames[count] = *mid;
8012 /* Slow out, stepping 1/2, then 1/4, ... */
8014 for (n = 0; n < factor; n++) {
8015 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8016 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8018 fraction = fraction * 2;
8023 /* Draw a piece on the screen without disturbing what's there */
8026 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8030 /* Bitmap for piece being moved. */
8031 if (appData.monoMode) {
8032 *mask = *pieceToSolid(piece);
8033 } else if (useImages) {
8035 *mask = xpmMask[piece];
8037 *mask = ximMaskPm[piece];
8040 *mask = *pieceToSolid(piece);
8043 /* GC for piece being moved. Square color doesn't matter, but
8044 since it gets modified we make a copy of the original. */
8046 if (appData.monoMode)
8051 if (appData.monoMode)
8056 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8058 /* Outline only used in mono mode and is not modified */
8060 *outline = bwPieceGC;
8062 *outline = wbPieceGC;
8066 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8071 /* Draw solid rectangle which will be clipped to shape of piece */
8072 XFillRectangle(xDisplay, dest, clip,
8073 0, 0, squareSize, squareSize);
8074 if (appData.monoMode)
8075 /* Also draw outline in contrasting color for black
8076 on black / white on white cases */
8077 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8078 0, 0, squareSize, squareSize, 0, 0, 1);
8080 /* Copy the piece */
8085 if(appData.upsideDown && flipView) kind ^= 2;
8086 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8088 0, 0, squareSize, squareSize,
8093 /* Animate the movement of a single piece */
8096 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8100 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8101 /* The old buffer is initialised with the start square (empty) */
8102 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8103 anim->prevFrame = *start;
8105 /* The piece will be drawn using its own bitmap as a matte */
8106 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8107 XSetClipMask(xDisplay, anim->pieceGC, mask);
8111 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8113 XRectangle updates[4];
8118 /* Save what we are about to draw into the new buffer */
8119 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8120 frame->x, frame->y, squareSize, squareSize,
8123 /* Erase bits of the previous frame */
8124 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8125 /* Where the new frame overlapped the previous,
8126 the contents in newBuf are wrong. */
8127 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8128 overlap.x, overlap.y,
8129 overlap.width, overlap.height,
8131 /* Repaint the areas in the old that don't overlap new */
8132 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8133 for (i = 0; i < count; i++)
8134 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8135 updates[i].x - anim->prevFrame.x,
8136 updates[i].y - anim->prevFrame.y,
8137 updates[i].width, updates[i].height,
8138 updates[i].x, updates[i].y);
8140 /* Easy when no overlap */
8141 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8142 0, 0, squareSize, squareSize,
8143 anim->prevFrame.x, anim->prevFrame.y);
8146 /* Save this frame for next time round */
8147 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8148 0, 0, squareSize, squareSize,
8150 anim->prevFrame = *frame;
8152 /* Draw piece over original screen contents, not current,
8153 and copy entire rect. Wipes out overlapping piece images. */
8154 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8155 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8156 0, 0, squareSize, squareSize,
8157 frame->x, frame->y);
8161 EndAnimation (AnimState *anim, XPoint *finish)
8163 XRectangle updates[4];
8168 /* The main code will redraw the final square, so we
8169 only need to erase the bits that don't overlap. */
8170 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8171 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8172 for (i = 0; i < count; i++)
8173 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8174 updates[i].x - anim->prevFrame.x,
8175 updates[i].y - anim->prevFrame.y,
8176 updates[i].width, updates[i].height,
8177 updates[i].x, updates[i].y);
8179 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8180 0, 0, squareSize, squareSize,
8181 anim->prevFrame.x, anim->prevFrame.y);
8186 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8190 BeginAnimation(anim, piece, startColor, start);
8191 for (n = 0; n < nFrames; n++) {
8192 AnimationFrame(anim, &(frames[n]), piece);
8193 FrameDelay(appData.animSpeed);
8195 EndAnimation(anim, finish);
8199 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8202 ChessSquare piece = board[fromY][toY];
8203 board[fromY][toY] = EmptySquare;
8204 DrawPosition(FALSE, board);
8206 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8207 y = lineGap + toY * (squareSize + lineGap);
8209 x = lineGap + toX * (squareSize + lineGap);
8210 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8212 for(i=1; i<4*kFactor; i++) {
8213 int r = squareSize * 9 * i/(20*kFactor - 5);
8214 XFillArc(xDisplay, xBoardWindow, highlineGC,
8215 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8216 FrameDelay(appData.animSpeed);
8218 board[fromY][toY] = piece;
8221 /* Main control logic for deciding what to animate and how */
8224 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8228 XPoint start, finish, mid;
8229 XPoint frames[kFactor * 2 + 1];
8230 int nFrames, startColor, endColor;
8232 /* Are we animating? */
8233 if (!appData.animate || appData.blindfold)
8236 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8237 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8238 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8240 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8241 piece = board[fromY][fromX];
8242 if (piece >= EmptySquare) return;
8247 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8250 ScreenSquare(fromX, fromY, &start, &startColor);
8251 ScreenSquare(toX, toY, &finish, &endColor);
8254 /* Knight: make straight movement then diagonal */
8255 if (abs(toY - fromY) < abs(toX - fromX)) {
8256 mid.x = start.x + (finish.x - start.x) / 2;
8260 mid.y = start.y + (finish.y - start.y) / 2;
8263 mid.x = start.x + (finish.x - start.x) / 2;
8264 mid.y = start.y + (finish.y - start.y) / 2;
8267 /* Don't use as many frames for very short moves */
8268 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8269 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8271 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8272 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8273 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8275 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8276 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8279 /* Be sure end square is redrawn */
8280 damage[0][toY][toX] = True;
8284 DragPieceBegin (int x, int y, Boolean instantly)
8286 int boardX, boardY, color;
8289 /* Are we animating? */
8290 if (!appData.animateDragging || appData.blindfold)
8293 /* Figure out which square we start in and the
8294 mouse position relative to top left corner. */
8295 BoardSquare(x, y, &boardX, &boardY);
8296 player.startBoardX = boardX;
8297 player.startBoardY = boardY;
8298 ScreenSquare(boardX, boardY, &corner, &color);
8299 player.startSquare = corner;
8300 player.startColor = color;
8301 /* As soon as we start dragging, the piece will jump slightly to
8302 be centered over the mouse pointer. */
8303 player.mouseDelta.x = squareSize/2;
8304 player.mouseDelta.y = squareSize/2;
8305 /* Initialise animation */
8306 player.dragPiece = PieceForSquare(boardX, boardY);
8308 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8309 player.dragActive = True;
8310 BeginAnimation(&player, player.dragPiece, color, &corner);
8311 /* Mark this square as needing to be redrawn. Note that
8312 we don't remove the piece though, since logically (ie
8313 as seen by opponent) the move hasn't been made yet. */
8314 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8315 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8316 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8317 corner.x, corner.y, squareSize, squareSize,
8318 0, 0); // [HGM] zh: unstack in stead of grab
8319 if(gatingPiece != EmptySquare) {
8320 /* Kludge alert: When gating we want the introduced
8321 piece to appear on the from square. To generate an
8322 image of it, we draw it on the board, copy the image,
8323 and draw the original piece again. */
8324 ChessSquare piece = boards[currentMove][boardY][boardX];
8325 DrawSquare(boardY, boardX, gatingPiece, 0);
8326 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8327 corner.x, corner.y, squareSize, squareSize, 0, 0);
8328 DrawSquare(boardY, boardX, piece, 0);
8330 damage[0][boardY][boardX] = True;
8332 player.dragActive = False;
8337 ChangeDragPiece (ChessSquare piece)
8340 player.dragPiece = piece;
8341 /* The piece will be drawn using its own bitmap as a matte */
8342 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8343 XSetClipMask(xDisplay, player.pieceGC, mask);
8347 DragPieceMove (int x, int y)
8351 /* Are we animating? */
8352 if (!appData.animateDragging || appData.blindfold)
8356 if (! player.dragActive)
8358 /* Move piece, maintaining same relative position
8359 of mouse within square */
8360 corner.x = x - player.mouseDelta.x;
8361 corner.y = y - player.mouseDelta.y;
8362 AnimationFrame(&player, &corner, player.dragPiece);
8364 if (appData.highlightDragging) {
8366 BoardSquare(x, y, &boardX, &boardY);
8367 SetHighlights(fromX, fromY, boardX, boardY);
8373 DragPieceEnd (int x, int y)
8375 int boardX, boardY, color;
8378 /* Are we animating? */
8379 if (!appData.animateDragging || appData.blindfold)
8383 if (! player.dragActive)
8385 /* Last frame in sequence is square piece is
8386 placed on, which may not match mouse exactly. */
8387 BoardSquare(x, y, &boardX, &boardY);
8388 ScreenSquare(boardX, boardY, &corner, &color);
8389 EndAnimation(&player, &corner);
8391 /* Be sure end square is redrawn */
8392 damage[0][boardY][boardX] = True;
8394 /* This prevents weird things happening with fast successive
8395 clicks which on my Sun at least can cause motion events
8396 without corresponding press/release. */
8397 player.dragActive = False;
8400 /* Handle expose event while piece being dragged */
8405 if (!player.dragActive || appData.blindfold)
8408 /* What we're doing: logically, the move hasn't been made yet,
8409 so the piece is still in it's original square. But visually
8410 it's being dragged around the board. So we erase the square
8411 that the piece is on and draw it at the last known drag point. */
8412 BlankSquare(player.startSquare.x, player.startSquare.y,
8413 player.startColor, EmptySquare, xBoardWindow, 1);
8414 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8415 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8418 #include <sys/ioctl.h>
8422 int fd, default_width;
8425 default_width = 79; // this is FICS default anyway...
8427 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8429 if (!ioctl(fd, TIOCGSIZE, &win))
8430 default_width = win.ts_cols;
8431 #elif defined(TIOCGWINSZ)
8433 if (!ioctl(fd, TIOCGWINSZ, &win))
8434 default_width = win.ws_col;
8436 return default_width;
8442 static int old_width = 0;
8443 int new_width = get_term_width();
8445 if (old_width != new_width)
8446 ics_printf("set width %d\n", new_width);
8447 old_width = new_width;
8451 NotifyFrontendLogin ()
8456 /* [AS] Arrow highlighting support */
8458 static double A_WIDTH = 5; /* Width of arrow body */
8460 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8461 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8472 return (int) (x + 0.5);
8476 SquareToPos (int rank, int file, int *x, int *y)
8479 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8480 *y = lineGap + rank * (squareSize + lineGap);
8482 *x = lineGap + file * (squareSize + lineGap);
8483 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8487 /* Draw an arrow between two points using current settings */
8489 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8492 double dx, dy, j, k, x, y;
8495 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8497 arrow[0].x = s_x + A_WIDTH + 0.5;
8500 arrow[1].x = s_x + A_WIDTH + 0.5;
8501 arrow[1].y = d_y - h;
8503 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8504 arrow[2].y = d_y - h;
8509 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8510 arrow[5].y = d_y - h;
8512 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8513 arrow[4].y = d_y - h;
8515 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8518 else if( d_y == s_y ) {
8519 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8522 arrow[0].y = s_y + A_WIDTH + 0.5;
8524 arrow[1].x = d_x - w;
8525 arrow[1].y = s_y + A_WIDTH + 0.5;
8527 arrow[2].x = d_x - w;
8528 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8533 arrow[5].x = d_x - w;
8534 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8536 arrow[4].x = d_x - w;
8537 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8540 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8543 /* [AS] Needed a lot of paper for this! :-) */
8544 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8545 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8547 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8549 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8554 arrow[0].x = Round(x - j);
8555 arrow[0].y = Round(y + j*dx);
8557 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8558 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8561 x = (double) d_x - k;
8562 y = (double) d_y - k*dy;
8565 x = (double) d_x + k;
8566 y = (double) d_y + k*dy;
8569 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8571 arrow[6].x = Round(x - j);
8572 arrow[6].y = Round(y + j*dx);
8574 arrow[2].x = Round(arrow[6].x + 2*j);
8575 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8577 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8578 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8583 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8584 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8587 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8588 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8589 // Polygon( hdc, arrow, 7 );
8593 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8596 hor = 64*s_col + 32; vert = 64*s_row + 32;
8597 for(i=0; i<= 64; i++) {
8598 damage[0][vert+6>>6][hor+6>>6] = True;
8599 damage[0][vert-6>>6][hor+6>>6] = True;
8600 damage[0][vert+6>>6][hor-6>>6] = True;
8601 damage[0][vert-6>>6][hor-6>>6] = True;
8602 hor += d_col - s_col; vert += d_row - s_row;
8606 /* [AS] Draw an arrow between two squares */
8608 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8610 int s_x, s_y, d_x, d_y;
8612 if( s_col == d_col && s_row == d_row ) {
8616 /* Get source and destination points */
8617 SquareToPos( s_row, s_col, &s_x, &s_y);
8618 SquareToPos( d_row, d_col, &d_x, &d_y);
8621 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8623 else if( d_y < s_y ) {
8624 d_y += squareSize / 2 + squareSize / 4;
8627 d_y += squareSize / 2;
8631 d_x += squareSize / 2 - squareSize / 4;
8633 else if( d_x < s_x ) {
8634 d_x += squareSize / 2 + squareSize / 4;
8637 d_x += squareSize / 2;
8640 s_x += squareSize / 2;
8641 s_y += squareSize / 2;
8644 A_WIDTH = squareSize / 14.; //[HGM] make float
8646 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8647 ArrowDamage(s_col, s_row, d_col, d_row);
8651 IsDrawArrowEnabled ()
8653 return appData.highlightMoveWithArrow && squareSize >= 32;
8657 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8659 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8660 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8664 UpdateLogos (int displ)
8666 return; // no logos in XBoard yet