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 XFontSetExtents *fontSize;
2179 char **font_name_list;
2180 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2181 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2182 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2183 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
2184 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
2187 appData.font = FindFont(appData.font, fontPxlSize);
2188 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2189 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2190 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2191 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2192 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2193 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2195 countFontID = coordFontID; // [HGM] holdings
2196 countFontStruct = coordFontStruct;
2198 xdb = XtDatabase(xDisplay);
2200 XrmPutLineResource(&xdb, "*international: True");
2201 vTo.size = sizeof(XFontSet);
2202 vTo.addr = (XtPointer) &fontSet;
2203 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2205 XrmPutStringResource(&xdb, "*font", appData.font);
2209 * Detect if there are not enough colors available and adapt.
2211 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2212 appData.monoMode = True;
2215 forceMono = MakeColors();
2218 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2220 appData.monoMode = True;
2223 if (appData.lowTimeWarning && !appData.monoMode) {
2224 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2225 vFrom.size = strlen(appData.lowTimeWarningColor);
2226 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2227 if (vTo.addr == NULL)
2228 appData.monoMode = True;
2230 lowTimeWarningColor = *(Pixel *) vTo.addr;
2233 if (appData.monoMode && appData.debugMode) {
2234 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2235 (unsigned long) XWhitePixel(xDisplay, xScreen),
2236 (unsigned long) XBlackPixel(xDisplay, xScreen));
2239 ParseIcsTextColors();
2240 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2241 textColors[ColorNone].attr = 0;
2243 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2249 layoutName = "tinyLayout";
2250 } else if (smallLayout) {
2251 layoutName = "smallLayout";
2253 layoutName = "normalLayout";
2255 /* Outer layoutWidget is there only to provide a name for use in
2256 resources that depend on the layout style */
2258 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2259 layoutArgs, XtNumber(layoutArgs));
2261 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2262 formArgs, XtNumber(formArgs));
2263 XtSetArg(args[0], XtNdefaultDistance, &sep);
2264 XtGetValues(formWidget, args, 1);
2267 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2268 XtSetArg(args[0], XtNtop, XtChainTop);
2269 XtSetArg(args[1], XtNbottom, XtChainTop);
2270 XtSetArg(args[2], XtNright, XtChainLeft);
2271 XtSetValues(menuBarWidget, args, 3);
2273 widgetList[j++] = whiteTimerWidget =
2274 XtCreateWidget("whiteTime", labelWidgetClass,
2275 formWidget, timerArgs, XtNumber(timerArgs));
2277 XtSetArg(args[0], XtNfontSet, clockFontSet);
2279 XtSetArg(args[0], XtNfont, clockFontStruct);
2281 XtSetArg(args[1], XtNtop, XtChainTop);
2282 XtSetArg(args[2], XtNbottom, XtChainTop);
2283 XtSetValues(whiteTimerWidget, args, 3);
2285 widgetList[j++] = blackTimerWidget =
2286 XtCreateWidget("blackTime", labelWidgetClass,
2287 formWidget, timerArgs, XtNumber(timerArgs));
2289 XtSetArg(args[0], XtNfontSet, clockFontSet);
2291 XtSetArg(args[0], XtNfont, clockFontStruct);
2293 XtSetArg(args[1], XtNtop, XtChainTop);
2294 XtSetArg(args[2], XtNbottom, XtChainTop);
2295 XtSetValues(blackTimerWidget, args, 3);
2297 if (appData.titleInWindow) {
2298 widgetList[j++] = titleWidget =
2299 XtCreateWidget("title", labelWidgetClass, formWidget,
2300 titleArgs, XtNumber(titleArgs));
2301 XtSetArg(args[0], XtNtop, XtChainTop);
2302 XtSetArg(args[1], XtNbottom, XtChainTop);
2303 XtSetValues(titleWidget, args, 2);
2306 if (appData.showButtonBar) {
2307 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2308 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2309 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2310 XtSetArg(args[2], XtNtop, XtChainTop);
2311 XtSetArg(args[3], XtNbottom, XtChainTop);
2312 XtSetValues(buttonBarWidget, args, 4);
2315 widgetList[j++] = messageWidget =
2316 XtCreateWidget("message", labelWidgetClass, formWidget,
2317 messageArgs, XtNumber(messageArgs));
2318 XtSetArg(args[0], XtNtop, XtChainTop);
2319 XtSetArg(args[1], XtNbottom, XtChainTop);
2320 XtSetValues(messageWidget, args, 2);
2322 widgetList[j++] = boardWidget =
2323 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2324 XtNumber(boardArgs));
2326 XtManageChildren(widgetList, j);
2328 timerWidth = (boardWidth - sep) / 2;
2329 XtSetArg(args[0], XtNwidth, timerWidth);
2330 XtSetValues(whiteTimerWidget, args, 1);
2331 XtSetValues(blackTimerWidget, args, 1);
2333 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2334 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2335 XtGetValues(whiteTimerWidget, args, 2);
2337 if (appData.showButtonBar) {
2338 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2339 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2340 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2344 * formWidget uses these constraints but they are stored
2348 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2349 XtSetValues(menuBarWidget, args, i);
2350 if (appData.titleInWindow) {
2353 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2354 XtSetValues(whiteTimerWidget, args, i);
2356 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2357 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2358 XtSetValues(blackTimerWidget, args, i);
2360 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2361 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2362 XtSetValues(titleWidget, args, i);
2364 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2365 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2366 XtSetValues(messageWidget, args, i);
2367 if (appData.showButtonBar) {
2369 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2370 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2371 XtSetValues(buttonBarWidget, args, i);
2375 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2376 XtSetValues(whiteTimerWidget, args, i);
2378 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2379 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2380 XtSetValues(blackTimerWidget, args, i);
2382 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2383 XtSetValues(titleWidget, args, i);
2385 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2386 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2387 XtSetValues(messageWidget, args, i);
2388 if (appData.showButtonBar) {
2390 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2391 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2392 XtSetValues(buttonBarWidget, args, i);
2397 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2398 XtSetValues(whiteTimerWidget, args, i);
2400 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2401 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2402 XtSetValues(blackTimerWidget, args, i);
2404 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2405 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2406 XtSetValues(messageWidget, args, i);
2407 if (appData.showButtonBar) {
2409 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2410 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2411 XtSetValues(buttonBarWidget, args, i);
2415 XtSetArg(args[0], XtNfromVert, messageWidget);
2416 XtSetArg(args[1], XtNtop, XtChainTop);
2417 XtSetArg(args[2], XtNbottom, XtChainBottom);
2418 XtSetArg(args[3], XtNleft, XtChainLeft);
2419 XtSetArg(args[4], XtNright, XtChainRight);
2420 XtSetValues(boardWidget, args, 5);
2422 XtRealizeWidget(shellWidget);
2425 XtSetArg(args[0], XtNx, wpMain.x);
2426 XtSetArg(args[1], XtNy, wpMain.y);
2427 XtSetValues(shellWidget, args, 2);
2431 * Correct the width of the message and title widgets.
2432 * It is not known why some systems need the extra fudge term.
2433 * The value "2" is probably larger than needed.
2435 XawFormDoLayout(formWidget, False);
2437 #define WIDTH_FUDGE 2
2439 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2440 XtSetArg(args[i], XtNheight, &h); i++;
2441 XtGetValues(messageWidget, args, i);
2442 if (appData.showButtonBar) {
2444 XtSetArg(args[i], XtNwidth, &w); i++;
2445 XtGetValues(buttonBarWidget, args, i);
2446 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2448 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2451 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2452 if (gres != XtGeometryYes && appData.debugMode) {
2453 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2454 programName, gres, w, h, wr, hr);
2457 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2458 /* The size used for the child widget in layout lags one resize behind
2459 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2461 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2462 if (gres != XtGeometryYes && appData.debugMode) {
2463 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2464 programName, gres, w, h, wr, hr);
2467 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2468 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2469 XtSetArg(args[1], XtNright, XtChainRight);
2470 XtSetValues(messageWidget, args, 2);
2472 if (appData.titleInWindow) {
2474 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2475 XtSetArg(args[i], XtNheight, &h); i++;
2476 XtGetValues(titleWidget, args, i);
2478 w = boardWidth - 2*bor;
2480 XtSetArg(args[0], XtNwidth, &w);
2481 XtGetValues(menuBarWidget, args, 1);
2482 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2485 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2486 if (gres != XtGeometryYes && appData.debugMode) {
2488 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2489 programName, gres, w, h, wr, hr);
2492 XawFormDoLayout(formWidget, True);
2494 xBoardWindow = XtWindow(boardWidget);
2496 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2497 // not need to go into InitDrawingSizes().
2501 * Create X checkmark bitmap and initialize option menu checks.
2503 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2504 checkmark_bits, checkmark_width, checkmark_height);
2505 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2506 #ifndef OPTIONSDIALOG
2507 if (appData.alwaysPromoteToQueen) {
2508 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2511 if (appData.animateDragging) {
2512 XtSetValues(XtNameToWidget(menuBarWidget,
2513 "menuOptions.Animate Dragging"),
2516 if (appData.animate) {
2517 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2520 if (appData.autoCallFlag) {
2521 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2524 if (appData.autoFlipView) {
2525 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2528 if (appData.blindfold) {
2529 XtSetValues(XtNameToWidget(menuBarWidget,
2530 "menuOptions.Blindfold"), args, 1);
2532 if (appData.flashCount > 0) {
2533 XtSetValues(XtNameToWidget(menuBarWidget,
2534 "menuOptions.Flash Moves"),
2538 if (appData.highlightDragging) {
2539 XtSetValues(XtNameToWidget(menuBarWidget,
2540 "menuOptions.Highlight Dragging"),
2544 if (appData.highlightLastMove) {
2545 XtSetValues(XtNameToWidget(menuBarWidget,
2546 "menuOptions.Highlight Last Move"),
2549 if (appData.highlightMoveWithArrow) {
2550 XtSetValues(XtNameToWidget(menuBarWidget,
2551 "menuOptions.Arrow"),
2554 // if (appData.icsAlarm) {
2555 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2558 if (appData.ringBellAfterMoves) {
2559 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2562 if (appData.oneClick) {
2563 XtSetValues(XtNameToWidget(menuBarWidget,
2564 "menuOptions.OneClick"), args, 1);
2566 if (appData.periodicUpdates) {
2567 XtSetValues(XtNameToWidget(menuBarWidget,
2568 "menuOptions.Periodic Updates"), args, 1);
2570 if (appData.ponderNextMove) {
2571 XtSetValues(XtNameToWidget(menuBarWidget,
2572 "menuOptions.Ponder Next Move"), args, 1);
2574 if (appData.popupExitMessage) {
2575 XtSetValues(XtNameToWidget(menuBarWidget,
2576 "menuOptions.Popup Exit Message"), args, 1);
2578 if (appData.popupMoveErrors) {
2579 XtSetValues(XtNameToWidget(menuBarWidget,
2580 "menuOptions.Popup Move Errors"), args, 1);
2582 // if (appData.premove) {
2583 // XtSetValues(XtNameToWidget(menuBarWidget,
2584 // "menuOptions.Premove"), args, 1);
2586 if (appData.showCoords) {
2587 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2590 if (appData.hideThinkingFromHuman) {
2591 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2594 if (appData.testLegality) {
2595 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2599 if (saveSettingsOnExit) {
2600 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2607 ReadBitmap(&wIconPixmap, "icon_white.bm",
2608 icon_white_bits, icon_white_width, icon_white_height);
2609 ReadBitmap(&bIconPixmap, "icon_black.bm",
2610 icon_black_bits, icon_black_width, icon_black_height);
2611 iconPixmap = wIconPixmap;
2613 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2614 XtSetValues(shellWidget, args, i);
2617 * Create a cursor for the board widget.
2619 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2620 XChangeWindowAttributes(xDisplay, xBoardWindow,
2621 CWCursor, &window_attributes);
2624 * Inhibit shell resizing.
2626 shellArgs[0].value = (XtArgVal) &w;
2627 shellArgs[1].value = (XtArgVal) &h;
2628 XtGetValues(shellWidget, shellArgs, 2);
2629 shellArgs[4].value = shellArgs[2].value = w;
2630 shellArgs[5].value = shellArgs[3].value = h;
2631 XtSetValues(shellWidget, &shellArgs[2], 4);
2632 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2633 marginH = h - boardHeight;
2635 CatchDeleteWindow(shellWidget, "QuitProc");
2643 if (appData.animate || appData.animateDragging)
2646 XtAugmentTranslations(formWidget,
2647 XtParseTranslationTable(globalTranslations));
2648 XtAugmentTranslations(boardWidget,
2649 XtParseTranslationTable(boardTranslations));
2650 XtAugmentTranslations(whiteTimerWidget,
2651 XtParseTranslationTable(whiteTranslations));
2652 XtAugmentTranslations(blackTimerWidget,
2653 XtParseTranslationTable(blackTranslations));
2655 /* Why is the following needed on some versions of X instead
2656 * of a translation? */
2657 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2658 (XtEventHandler) EventProc, NULL);
2660 XtAddEventHandler(formWidget, KeyPressMask, False,
2661 (XtEventHandler) MoveTypeInProc, NULL);
2663 /* [AS] Restore layout */
2664 if( wpMoveHistory.visible ) {
2668 if( wpEvalGraph.visible )
2673 if( wpEngineOutput.visible ) {
2674 EngineOutputPopUp();
2679 if (errorExitStatus == -1) {
2680 if (appData.icsActive) {
2681 /* We now wait until we see "login:" from the ICS before
2682 sending the logon script (problems with timestamp otherwise) */
2683 /*ICSInitScript();*/
2684 if (appData.icsInputBox) ICSInputBoxPopUp();
2688 signal(SIGWINCH, TermSizeSigHandler);
2690 signal(SIGINT, IntSigHandler);
2691 signal(SIGTERM, IntSigHandler);
2692 if (*appData.cmailGameName != NULLCHAR) {
2693 signal(SIGUSR1, CmailSigHandler);
2696 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2698 // XtSetKeyboardFocus(shellWidget, formWidget);
2699 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2701 XtAppMainLoop(appContext);
2702 if (appData.debugMode) fclose(debugFP); // [DM] debug
2706 static Boolean noEcho;
2711 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2712 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2714 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2715 unlink(gameCopyFilename);
2716 unlink(gamePasteFilename);
2717 if(noEcho) EchoOn();
2721 TermSizeSigHandler (int sig)
2727 IntSigHandler (int sig)
2733 CmailSigHandler (int sig)
2738 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2740 /* Activate call-back function CmailSigHandlerCallBack() */
2741 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2743 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2747 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2750 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2752 /**** end signal code ****/
2758 /* try to open the icsLogon script, either in the location given
2759 * or in the users HOME directory
2766 f = fopen(appData.icsLogon, "r");
2769 homedir = getenv("HOME");
2770 if (homedir != NULL)
2772 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2773 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2774 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2775 f = fopen(buf, "r");
2780 ProcessICSInitScript(f);
2782 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2801 GreyRevert (Boolean grey)
2804 if (!menuBarWidget) return;
2805 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2807 DisplayError("menuEdit.Revert", 0);
2809 XtSetSensitive(w, !grey);
2811 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2813 DisplayError("menuEdit.Annotate", 0);
2815 XtSetSensitive(w, !grey);
2820 SetMenuEnables (Enables *enab)
2823 if (!menuBarWidget) return;
2824 while (enab->name != NULL) {
2825 w = XtNameToWidget(menuBarWidget, enab->name);
2827 DisplayError(enab->name, 0);
2829 XtSetSensitive(w, enab->value);
2835 Enables icsEnables[] = {
2836 { "menuFile.Mail Move", False },
2837 { "menuFile.Reload CMail Message", False },
2838 { "menuMode.Machine Black", False },
2839 { "menuMode.Machine White", False },
2840 { "menuMode.Analysis Mode", False },
2841 { "menuMode.Analyze File", False },
2842 { "menuMode.Two Machines", False },
2843 { "menuMode.Machine Match", False },
2845 { "menuEngine.Hint", False },
2846 { "menuEngine.Book", False },
2847 { "menuEngine.Move Now", False },
2848 #ifndef OPTIONSDIALOG
2849 { "menuOptions.Periodic Updates", False },
2850 { "menuOptions.Hide Thinking", False },
2851 { "menuOptions.Ponder Next Move", False },
2854 { "menuEngine.Engine #1 Settings", False },
2855 { "menuEngine.Engine #2 Settings", False },
2856 { "menuEngine.Load Engine", False },
2857 { "menuEdit.Annotate", False },
2858 { "menuOptions.Match", False },
2862 Enables ncpEnables[] = {
2863 { "menuFile.Mail Move", False },
2864 { "menuFile.Reload CMail Message", False },
2865 { "menuMode.Machine White", False },
2866 { "menuMode.Machine Black", False },
2867 { "menuMode.Analysis Mode", False },
2868 { "menuMode.Analyze File", False },
2869 { "menuMode.Two Machines", False },
2870 { "menuMode.Machine Match", False },
2871 { "menuMode.ICS Client", False },
2872 { "menuView.ICStex", False },
2873 { "menuView.ICS Input Box", False },
2874 { "Action", False },
2875 { "menuEdit.Revert", False },
2876 { "menuEdit.Annotate", False },
2877 { "menuEngine.Engine #1 Settings", False },
2878 { "menuEngine.Engine #2 Settings", False },
2879 { "menuEngine.Move Now", False },
2880 { "menuEngine.Retract Move", False },
2881 { "menuOptions.ICS", False },
2882 #ifndef OPTIONSDIALOG
2883 { "menuOptions.Auto Flag", False },
2884 { "menuOptions.Auto Flip View", False },
2885 // { "menuOptions.ICS Alarm", False },
2886 { "menuOptions.Move Sound", False },
2887 { "menuOptions.Hide Thinking", False },
2888 { "menuOptions.Periodic Updates", False },
2889 { "menuOptions.Ponder Next Move", False },
2891 { "menuEngine.Hint", False },
2892 { "menuEngine.Book", False },
2896 Enables gnuEnables[] = {
2897 { "menuMode.ICS Client", False },
2898 { "menuView.ICStex", False },
2899 { "menuView.ICS Input Box", False },
2900 { "menuAction.Accept", False },
2901 { "menuAction.Decline", False },
2902 { "menuAction.Rematch", False },
2903 { "menuAction.Adjourn", False },
2904 { "menuAction.Stop Examining", False },
2905 { "menuAction.Stop Observing", False },
2906 { "menuAction.Upload to Examine", False },
2907 { "menuEdit.Revert", False },
2908 { "menuEdit.Annotate", False },
2909 { "menuOptions.ICS", False },
2911 /* The next two options rely on SetCmailMode being called *after* */
2912 /* SetGNUMode so that when GNU is being used to give hints these */
2913 /* menu options are still available */
2915 { "menuFile.Mail Move", False },
2916 { "menuFile.Reload CMail Message", False },
2917 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2918 { "menuMode.Machine White", True },
2919 { "menuMode.Machine Black", True },
2920 { "menuMode.Analysis Mode", True },
2921 { "menuMode.Analyze File", True },
2922 { "menuMode.Two Machines", True },
2923 { "menuMode.Machine Match", True },
2924 { "menuEngine.Engine #1 Settings", True },
2925 { "menuEngine.Engine #2 Settings", True },
2926 { "menuEngine.Hint", True },
2927 { "menuEngine.Book", True },
2928 { "menuEngine.Move Now", True },
2929 { "menuEngine.Retract Move", True },
2934 Enables cmailEnables[] = {
2936 { "menuAction.Call Flag", False },
2937 { "menuAction.Draw", True },
2938 { "menuAction.Adjourn", False },
2939 { "menuAction.Abort", False },
2940 { "menuAction.Stop Observing", False },
2941 { "menuAction.Stop Examining", False },
2942 { "menuFile.Mail Move", True },
2943 { "menuFile.Reload CMail Message", True },
2947 Enables trainingOnEnables[] = {
2948 { "menuMode.Edit Comment", False },
2949 { "menuMode.Pause", False },
2950 { "menuEdit.Forward", False },
2951 { "menuEdit.Backward", False },
2952 { "menuEdit.Forward to End", False },
2953 { "menuEdit.Back to Start", False },
2954 { "menuEngine.Move Now", False },
2955 { "menuEdit.Truncate Game", False },
2959 Enables trainingOffEnables[] = {
2960 { "menuMode.Edit Comment", True },
2961 { "menuMode.Pause", True },
2962 { "menuEdit.Forward", True },
2963 { "menuEdit.Backward", True },
2964 { "menuEdit.Forward to End", True },
2965 { "menuEdit.Back to Start", True },
2966 { "menuEngine.Move Now", True },
2967 { "menuEdit.Truncate Game", True },
2971 Enables machineThinkingEnables[] = {
2972 { "menuFile.Load Game", False },
2973 // { "menuFile.Load Next Game", False },
2974 // { "menuFile.Load Previous Game", False },
2975 // { "menuFile.Reload Same Game", False },
2976 { "menuEdit.Paste Game", False },
2977 { "menuFile.Load Position", False },
2978 // { "menuFile.Load Next Position", False },
2979 // { "menuFile.Load Previous Position", False },
2980 // { "menuFile.Reload Same Position", False },
2981 { "menuEdit.Paste Position", False },
2982 { "menuMode.Machine White", False },
2983 { "menuMode.Machine Black", False },
2984 { "menuMode.Two Machines", False },
2985 // { "menuMode.Machine Match", False },
2986 { "menuEngine.Retract Move", False },
2990 Enables userThinkingEnables[] = {
2991 { "menuFile.Load Game", True },
2992 // { "menuFile.Load Next Game", True },
2993 // { "menuFile.Load Previous Game", True },
2994 // { "menuFile.Reload Same Game", True },
2995 { "menuEdit.Paste Game", True },
2996 { "menuFile.Load Position", True },
2997 // { "menuFile.Load Next Position", True },
2998 // { "menuFile.Load Previous Position", True },
2999 // { "menuFile.Reload Same Position", True },
3000 { "menuEdit.Paste Position", True },
3001 { "menuMode.Machine White", True },
3002 { "menuMode.Machine Black", True },
3003 { "menuMode.Two Machines", True },
3004 // { "menuMode.Machine Match", True },
3005 { "menuEngine.Retract Move", True },
3012 SetMenuEnables(icsEnables);
3015 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3016 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3017 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3025 SetMenuEnables(ncpEnables);
3031 SetMenuEnables(gnuEnables);
3037 SetMenuEnables(cmailEnables);
3041 SetTrainingModeOn ()
3043 SetMenuEnables(trainingOnEnables);
3044 if (appData.showButtonBar) {
3045 XtSetSensitive(buttonBarWidget, False);
3051 SetTrainingModeOff ()
3053 SetMenuEnables(trainingOffEnables);
3054 if (appData.showButtonBar) {
3055 XtSetSensitive(buttonBarWidget, True);
3060 SetUserThinkingEnables ()
3062 if (appData.noChessProgram) return;
3063 SetMenuEnables(userThinkingEnables);
3067 SetMachineThinkingEnables ()
3069 if (appData.noChessProgram) return;
3070 SetMenuEnables(machineThinkingEnables);
3072 case MachinePlaysBlack:
3073 case MachinePlaysWhite:
3074 case TwoMachinesPlay:
3075 XtSetSensitive(XtNameToWidget(menuBarWidget,
3076 ModeToWidgetName(gameMode)), True);
3083 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3084 #define HISTORY_SIZE 64
3085 static char *history[HISTORY_SIZE];
3086 int histIn = 0, histP = 0;
3089 SaveInHistory (char *cmd)
3091 if (history[histIn] != NULL) {
3092 free(history[histIn]);
3093 history[histIn] = NULL;
3095 if (*cmd == NULLCHAR) return;
3096 history[histIn] = StrSave(cmd);
3097 histIn = (histIn + 1) % HISTORY_SIZE;
3098 if (history[histIn] != NULL) {
3099 free(history[histIn]);
3100 history[histIn] = NULL;
3106 PrevInHistory (char *cmd)
3109 if (histP == histIn) {
3110 if (history[histIn] != NULL) free(history[histIn]);
3111 history[histIn] = StrSave(cmd);
3113 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3114 if (newhp == histIn || history[newhp] == NULL) return NULL;
3116 return history[histP];
3122 if (histP == histIn) return NULL;
3123 histP = (histP + 1) % HISTORY_SIZE;
3124 return history[histP];
3126 // end of borrowed code
3128 #define Abs(n) ((n)<0 ? -(n) : (n))
3132 InsertPxlSize (char *pattern, int targetPxlSize)
3134 char *base_fnt_lst, strInt[12], *p, *q;
3135 int alternatives, i, len, strIntLen;
3138 * Replace the "*" (if present) in the pixel-size slot of each
3139 * alternative with the targetPxlSize.
3143 while ((p = strchr(p, ',')) != NULL) {
3147 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3148 strIntLen = strlen(strInt);
3149 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3153 while (alternatives--) {
3154 char *comma = strchr(p, ',');
3155 for (i=0; i<14; i++) {
3156 char *hyphen = strchr(p, '-');
3158 if (comma && hyphen > comma) break;
3159 len = hyphen + 1 - p;
3160 if (i == 7 && *p == '*' && len == 2) {
3162 memcpy(q, strInt, strIntLen);
3172 len = comma + 1 - p;
3179 return base_fnt_lst;
3183 CreateFontSet (char *base_fnt_lst)
3186 char **missing_list;
3190 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3191 &missing_list, &missing_count, &def_string);
3192 if (appData.debugMode) {
3194 XFontStruct **font_struct_list;
3195 char **font_name_list;
3196 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3198 fprintf(debugFP, " got list %s, locale %s\n",
3199 XBaseFontNameListOfFontSet(fntSet),
3200 XLocaleOfFontSet(fntSet));
3201 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3202 for (i = 0; i < count; i++) {
3203 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3206 for (i = 0; i < missing_count; i++) {
3207 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3210 if (fntSet == NULL) {
3211 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3216 #else // not ENABLE_NLS
3218 * Find a font that matches "pattern" that is as close as
3219 * possible to the targetPxlSize. Prefer fonts that are k
3220 * pixels smaller to fonts that are k pixels larger. The
3221 * pattern must be in the X Consortium standard format,
3222 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3223 * The return value should be freed with XtFree when no
3227 FindFont (char *pattern, int targetPxlSize)
3229 char **fonts, *p, *best, *scalable, *scalableTail;
3230 int i, j, nfonts, minerr, err, pxlSize;
3232 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3234 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3235 programName, pattern);
3242 for (i=0; i<nfonts; i++) {
3245 if (*p != '-') continue;
3247 if (*p == NULLCHAR) break;
3248 if (*p++ == '-') j++;
3250 if (j < 7) continue;
3253 scalable = fonts[i];
3256 err = pxlSize - targetPxlSize;
3257 if (Abs(err) < Abs(minerr) ||
3258 (minerr > 0 && err < 0 && -err == minerr)) {
3264 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3265 /* If the error is too big and there is a scalable font,
3266 use the scalable font. */
3267 int headlen = scalableTail - scalable;
3268 p = (char *) XtMalloc(strlen(scalable) + 10);
3269 while (isdigit(*scalableTail)) scalableTail++;
3270 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3272 p = (char *) XtMalloc(strlen(best) + 2);
3273 safeStrCpy(p, best, strlen(best)+1 );
3275 if (appData.debugMode) {
3276 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3277 pattern, targetPxlSize, p);
3279 XFreeFontNames(fonts);
3286 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3287 // must be called before all non-first callse to CreateGCs()
3288 XtReleaseGC(shellWidget, highlineGC);
3289 XtReleaseGC(shellWidget, lightSquareGC);
3290 XtReleaseGC(shellWidget, darkSquareGC);
3291 XtReleaseGC(shellWidget, lineGC);
3292 if (appData.monoMode) {
3293 if (DefaultDepth(xDisplay, xScreen) == 1) {
3294 XtReleaseGC(shellWidget, wbPieceGC);
3296 XtReleaseGC(shellWidget, bwPieceGC);
3299 XtReleaseGC(shellWidget, prelineGC);
3300 XtReleaseGC(shellWidget, jailSquareGC);
3301 XtReleaseGC(shellWidget, wdPieceGC);
3302 XtReleaseGC(shellWidget, wlPieceGC);
3303 XtReleaseGC(shellWidget, wjPieceGC);
3304 XtReleaseGC(shellWidget, bdPieceGC);
3305 XtReleaseGC(shellWidget, blPieceGC);
3306 XtReleaseGC(shellWidget, bjPieceGC);
3311 CreateGCs (int redo)
3313 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3314 | GCBackground | GCFunction | GCPlaneMask;
3315 XGCValues gc_values;
3318 gc_values.plane_mask = AllPlanes;
3319 gc_values.line_width = lineGap;
3320 gc_values.line_style = LineSolid;
3321 gc_values.function = GXcopy;
3324 DeleteGCs(); // called a second time; clean up old GCs first
3325 } else { // [HGM] grid and font GCs created on first call only
3326 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3327 gc_values.background = XWhitePixel(xDisplay, xScreen);
3328 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3329 XSetFont(xDisplay, coordGC, coordFontID);
3331 // [HGM] make font for holdings counts (white on black)
3332 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3333 gc_values.background = XBlackPixel(xDisplay, xScreen);
3334 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3335 XSetFont(xDisplay, countGC, countFontID);
3337 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3338 gc_values.background = XBlackPixel(xDisplay, xScreen);
3339 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3341 if (appData.monoMode) {
3342 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3343 gc_values.background = XWhitePixel(xDisplay, xScreen);
3344 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3346 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3347 gc_values.background = XBlackPixel(xDisplay, xScreen);
3348 lightSquareGC = wbPieceGC
3349 = XtGetGC(shellWidget, value_mask, &gc_values);
3351 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3352 gc_values.background = XWhitePixel(xDisplay, xScreen);
3353 darkSquareGC = bwPieceGC
3354 = XtGetGC(shellWidget, value_mask, &gc_values);
3356 if (DefaultDepth(xDisplay, xScreen) == 1) {
3357 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3358 gc_values.function = GXcopyInverted;
3359 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3360 gc_values.function = GXcopy;
3361 if (XBlackPixel(xDisplay, xScreen) == 1) {
3362 bwPieceGC = darkSquareGC;
3363 wbPieceGC = copyInvertedGC;
3365 bwPieceGC = copyInvertedGC;
3366 wbPieceGC = lightSquareGC;
3370 gc_values.foreground = highlightSquareColor;
3371 gc_values.background = highlightSquareColor;
3372 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3374 gc_values.foreground = premoveHighlightColor;
3375 gc_values.background = premoveHighlightColor;
3376 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3378 gc_values.foreground = lightSquareColor;
3379 gc_values.background = darkSquareColor;
3380 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3382 gc_values.foreground = darkSquareColor;
3383 gc_values.background = lightSquareColor;
3384 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3386 gc_values.foreground = jailSquareColor;
3387 gc_values.background = jailSquareColor;
3388 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3390 gc_values.foreground = whitePieceColor;
3391 gc_values.background = darkSquareColor;
3392 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3394 gc_values.foreground = whitePieceColor;
3395 gc_values.background = lightSquareColor;
3396 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3398 gc_values.foreground = whitePieceColor;
3399 gc_values.background = jailSquareColor;
3400 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3402 gc_values.foreground = blackPieceColor;
3403 gc_values.background = darkSquareColor;
3404 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3406 gc_values.foreground = blackPieceColor;
3407 gc_values.background = lightSquareColor;
3408 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3410 gc_values.foreground = blackPieceColor;
3411 gc_values.background = jailSquareColor;
3412 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3417 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3425 fp = fopen(filename, "rb");
3427 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3434 for (y=0; y<h; ++y) {
3435 for (x=0; x<h; ++x) {
3440 XPutPixel(xim, x, y, blackPieceColor);
3442 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3445 XPutPixel(xim, x, y, darkSquareColor);
3447 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3450 XPutPixel(xim, x, y, whitePieceColor);
3452 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3455 XPutPixel(xim, x, y, lightSquareColor);
3457 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3465 /* create Pixmap of piece */
3466 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3468 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3471 /* create Pixmap of clipmask
3472 Note: We assume the white/black pieces have the same
3473 outline, so we make only 6 masks. This is okay
3474 since the XPM clipmask routines do the same. */
3476 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3478 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3481 /* now create the 1-bit version */
3482 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3485 values.foreground = 1;
3486 values.background = 0;
3488 /* Don't use XtGetGC, not read only */
3489 maskGC = XCreateGC(xDisplay, *mask,
3490 GCForeground | GCBackground, &values);
3491 XCopyPlane(xDisplay, temp, *mask, maskGC,
3492 0, 0, squareSize, squareSize, 0, 0, 1);
3493 XFreePixmap(xDisplay, temp);
3498 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3506 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3511 /* The XSynchronize calls were copied from CreatePieces.
3512 Not sure if needed, but can't hurt */
3513 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3516 /* temp needed by loadXIM() */
3517 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3518 0, 0, ss, ss, AllPlanes, XYPixmap);
3520 if (strlen(appData.pixmapDirectory) == 0) {
3524 if (appData.monoMode) {
3525 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3529 fprintf(stderr, _("\nLoading XIMs...\n"));
3531 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3532 fprintf(stderr, "%d", piece+1);
3533 for (kind=0; kind<4; kind++) {
3534 fprintf(stderr, ".");
3535 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3536 ExpandPathName(appData.pixmapDirectory),
3537 piece <= (int) WhiteKing ? "" : "w",
3538 pieceBitmapNames[piece],
3540 ximPieceBitmap[kind][piece] =
3541 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3542 0, 0, ss, ss, AllPlanes, XYPixmap);
3543 if (appData.debugMode)
3544 fprintf(stderr, _("(File:%s:) "), buf);
3545 loadXIM(ximPieceBitmap[kind][piece],
3547 &(xpmPieceBitmap2[kind][piece]),
3548 &(ximMaskPm2[piece]));
3549 if(piece <= (int)WhiteKing)
3550 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3552 fprintf(stderr," ");
3554 /* Load light and dark squares */
3555 /* If the LSQ and DSQ pieces don't exist, we will
3556 draw them with solid squares. */
3557 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3558 if (access(buf, 0) != 0) {
3562 fprintf(stderr, _("light square "));
3564 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3565 0, 0, ss, ss, AllPlanes, XYPixmap);
3566 if (appData.debugMode)
3567 fprintf(stderr, _("(File:%s:) "), buf);
3569 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3570 fprintf(stderr, _("dark square "));
3571 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3572 ExpandPathName(appData.pixmapDirectory), ss);
3573 if (appData.debugMode)
3574 fprintf(stderr, _("(File:%s:) "), buf);
3576 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3577 0, 0, ss, ss, AllPlanes, XYPixmap);
3578 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3579 xpmJailSquare = xpmLightSquare;
3581 fprintf(stderr, _("Done.\n"));
3583 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3586 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3590 CreateXPMBoard (char *s, int kind)
3594 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3595 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3596 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3602 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3603 // thisroutine has to be called t free the old piece pixmaps
3605 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3606 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3608 XFreePixmap(xDisplay, xpmLightSquare);
3609 XFreePixmap(xDisplay, xpmDarkSquare);
3618 u_int ss = squareSize;
3620 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3621 XpmColorSymbol symbols[4];
3622 static int redo = False;
3624 if(redo) FreeXPMPieces(); else redo = 1;
3626 /* The XSynchronize calls were copied from CreatePieces.
3627 Not sure if needed, but can't hurt */
3628 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3630 /* Setup translations so piece colors match square colors */
3631 symbols[0].name = "light_piece";
3632 symbols[0].value = appData.whitePieceColor;
3633 symbols[1].name = "dark_piece";
3634 symbols[1].value = appData.blackPieceColor;
3635 symbols[2].name = "light_square";
3636 symbols[2].value = appData.lightSquareColor;
3637 symbols[3].name = "dark_square";
3638 symbols[3].value = appData.darkSquareColor;
3640 attr.valuemask = XpmColorSymbols;
3641 attr.colorsymbols = symbols;
3642 attr.numsymbols = 4;
3644 if (appData.monoMode) {
3645 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3649 if (strlen(appData.pixmapDirectory) == 0) {
3650 XpmPieces* pieces = builtInXpms;
3653 while (pieces->size != squareSize && pieces->size) pieces++;
3654 if (!pieces->size) {
3655 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3658 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3659 for (kind=0; kind<4; kind++) {
3661 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3662 pieces->xpm[piece][kind],
3663 &(xpmPieceBitmap2[kind][piece]),
3664 NULL, &attr)) != 0) {
3665 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3669 if(piece <= (int) WhiteKing)
3670 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3674 xpmJailSquare = xpmLightSquare;
3678 fprintf(stderr, _("\nLoading XPMs...\n"));
3681 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3682 fprintf(stderr, "%d ", piece+1);
3683 for (kind=0; kind<4; kind++) {
3684 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3685 ExpandPathName(appData.pixmapDirectory),
3686 piece > (int) WhiteKing ? "w" : "",
3687 pieceBitmapNames[piece],
3689 if (appData.debugMode) {
3690 fprintf(stderr, _("(File:%s:) "), buf);
3692 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3693 &(xpmPieceBitmap2[kind][piece]),
3694 NULL, &attr)) != 0) {
3695 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3696 // [HGM] missing: read of unorthodox piece failed; substitute King.
3697 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3698 ExpandPathName(appData.pixmapDirectory),
3700 if (appData.debugMode) {
3701 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3703 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3704 &(xpmPieceBitmap2[kind][piece]),
3708 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3713 if(piece <= (int) WhiteKing)
3714 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3717 /* Load light and dark squares */
3718 /* If the LSQ and DSQ pieces don't exist, we will
3719 draw them with solid squares. */
3720 fprintf(stderr, _("light square "));
3721 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3722 if (access(buf, 0) != 0) {
3726 if (appData.debugMode)
3727 fprintf(stderr, _("(File:%s:) "), buf);
3729 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3730 &xpmLightSquare, NULL, &attr)) != 0) {
3731 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3734 fprintf(stderr, _("dark square "));
3735 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3736 ExpandPathName(appData.pixmapDirectory), ss);
3737 if (appData.debugMode) {
3738 fprintf(stderr, _("(File:%s:) "), buf);
3740 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3741 &xpmDarkSquare, NULL, &attr)) != 0) {
3742 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3746 xpmJailSquare = xpmLightSquare;
3747 fprintf(stderr, _("Done.\n"));
3749 oldVariant = -1; // kludge to force re-makig of animation masks
3750 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3753 #endif /* HAVE_LIBXPM */
3756 /* No built-in bitmaps */
3761 u_int ss = squareSize;
3763 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3766 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3767 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3768 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3769 pieceBitmapNames[piece],
3770 ss, kind == SOLID ? 's' : 'o');
3771 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3772 if(piece <= (int)WhiteKing)
3773 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3777 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3781 /* With built-in bitmaps */
3785 BuiltInBits* bib = builtInBits;
3788 u_int ss = squareSize;
3790 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3793 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3795 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3796 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3797 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3798 pieceBitmapNames[piece],
3799 ss, kind == SOLID ? 's' : 'o');
3800 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3801 bib->bits[kind][piece], ss, ss);
3802 if(piece <= (int)WhiteKing)
3803 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3807 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3813 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3818 char msg[MSG_SIZ], fullname[MSG_SIZ];
3820 if (*appData.bitmapDirectory != NULLCHAR) {
3821 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3822 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3823 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3824 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3825 &w, &h, pm, &x_hot, &y_hot);
3826 fprintf(stderr, "load %s\n", name);
3827 if (errcode != BitmapSuccess) {
3829 case BitmapOpenFailed:
3830 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3832 case BitmapFileInvalid:
3833 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3835 case BitmapNoMemory:
3836 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3840 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3844 fprintf(stderr, _("%s: %s...using built-in\n"),
3846 } else if (w != wreq || h != hreq) {
3848 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3849 programName, fullname, w, h, wreq, hreq);
3855 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3865 if (lineGap == 0) return;
3867 /* [HR] Split this into 2 loops for non-square boards. */
3869 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3870 gridSegments[i].x1 = 0;
3871 gridSegments[i].x2 =
3872 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3873 gridSegments[i].y1 = gridSegments[i].y2
3874 = lineGap / 2 + (i * (squareSize + lineGap));
3877 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3878 gridSegments[j + i].y1 = 0;
3879 gridSegments[j + i].y2 =
3880 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3881 gridSegments[j + i].x1 = gridSegments[j + i].x2
3882 = lineGap / 2 + (j * (squareSize + lineGap));
3887 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3889 XtActionProc proc = (XtActionProc) addr;
3891 (proc)(NULL, NULL, NULL, NULL);
3895 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3897 RecentEngineEvent((int) addr);
3901 AppendEnginesToMenu (Widget menu, char *list)
3909 if(appData.recentEngines <= 0) return;
3910 recentEngines = strdup(list);
3912 XtSetArg(args[j], XtNleftMargin, 20); j++;
3913 XtSetArg(args[j], XtNrightMargin, 20); j++;
3915 p = strchr(list, '\n'); if(p == NULL) break;
3916 if(i == 0) XtCreateManagedWidget(_("----"), smeLineObjectClass, menu, args, j); // at least one valid item to add
3918 XtSetArg(args[j], XtNlabel, XtNewString(list));
3919 entry = XtCreateManagedWidget("engine", smeBSBObjectClass, menu, args, j+1);
3920 XtAddCallback(entry, XtNcallback,
3921 (XtCallbackProc) MenuEngineSelect,
3923 i++; *p = '\n'; list = p + 1;
3928 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3935 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3938 XtSetArg(args[j], XtNleftMargin, 20); j++;
3939 XtSetArg(args[j], XtNrightMargin, 20); j++;
3941 while (mi->string != NULL) {
3942 if (strcmp(mi->string, "----") == 0) {
3943 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3946 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3947 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3949 XtAddCallback(entry, XtNcallback,
3950 (XtCallbackProc) MenuBarSelect,
3951 (caddr_t) mi->proc);
3955 if(!strcmp(mb->name, "Engine")) AppendEnginesToMenu(menu, appData.recentEngineList);
3959 CreateMenuBar (Menu *mb, int boardWidth)
3961 int i, j, nr = 0, wtot = 0, widths[10];
3964 char menuName[MSG_SIZ];
3969 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3970 XtSetArg(args[j], XtNvSpace, 0); j++;
3971 XtSetArg(args[j], XtNborderWidth, 0); j++;
3972 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3973 formWidget, args, j);
3975 while (mb->name != NULL) {
3976 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3977 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3979 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3980 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3981 XtSetArg(args[j], XtNborderWidth, 0); j++;
3982 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3984 CreateMenuBarPopup(menuBar, menuName, mb);
3986 XtSetArg(args[j], XtNwidth, &w); j++;
3987 XtGetValues(mb->subMenu, args, j);
3988 wtot += mb->textWidth = widths[nr++] = w;
3991 while(wtot > boardWidth - 40) {
3993 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3997 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3999 XtSetArg(args[j], XtNwidth, widths[i]); j++;
4000 XtSetValues(ma[i].subMenu, args, j);
4006 CreateButtonBar (MenuItem *mi)
4009 Widget button, buttonBar;
4013 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4015 XtSetArg(args[j], XtNhSpace, 0); j++;
4017 XtSetArg(args[j], XtNborderWidth, 0); j++;
4018 XtSetArg(args[j], XtNvSpace, 0); j++;
4019 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4020 formWidget, args, j);
4022 while (mi->string != NULL) {
4025 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4026 XtSetArg(args[j], XtNborderWidth, 0); j++;
4028 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4029 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4030 buttonBar, args, j);
4031 XtAddCallback(button, XtNcallback,
4032 (XtCallbackProc) MenuBarSelect,
4033 (caddr_t) mi->proc);
4040 CreatePieceMenu (char *name, int color)
4045 ChessSquare selection;
4047 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4048 boardWidget, args, 0);
4050 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4051 String item = pieceMenuStrings[color][i];
4053 if (strcmp(item, "----") == 0) {
4054 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4057 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4058 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4060 selection = pieceMenuTranslation[color][i];
4061 XtAddCallback(entry, XtNcallback,
4062 (XtCallbackProc) PieceMenuSelect,
4063 (caddr_t) selection);
4064 if (selection == WhitePawn || selection == BlackPawn) {
4065 XtSetArg(args[0], XtNpopupOnEntry, entry);
4066 XtSetValues(menu, args, 1);
4079 ChessSquare selection;
4081 whitePieceMenu = CreatePieceMenu("menuW", 0);
4082 blackPieceMenu = CreatePieceMenu("menuB", 1);
4084 XtRegisterGrabAction(PieceMenuPopup, True,
4085 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4086 GrabModeAsync, GrabModeAsync);
4088 XtSetArg(args[0], XtNlabel, _("Drop"));
4089 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4090 boardWidget, args, 1);
4091 for (i = 0; i < DROP_MENU_SIZE; i++) {
4092 String item = dropMenuStrings[i];
4094 if (strcmp(item, "----") == 0) {
4095 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4098 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4099 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4101 selection = dropMenuTranslation[i];
4102 XtAddCallback(entry, XtNcallback,
4103 (XtCallbackProc) DropMenuSelect,
4104 (caddr_t) selection);
4118 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4119 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4120 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4121 dmEnables[i].piece);
4122 XtSetSensitive(entry, p != NULL || !appData.testLegality
4123 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4124 && !appData.icsActive));
4126 while (p && *p++ == dmEnables[i].piece) count++;
4127 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4129 XtSetArg(args[j], XtNlabel, label); j++;
4130 XtSetValues(entry, args, j);
4135 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4137 String whichMenu; int menuNr = -2;
4138 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4139 if (event->type == ButtonRelease)
4140 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4141 else if (event->type == ButtonPress)
4142 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4144 case 0: whichMenu = params[0]; break;
4145 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4147 case -1: if (errorUp) ErrorPopDown();
4150 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4154 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4156 if (pmFromX < 0 || pmFromY < 0) return;
4157 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4161 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4163 if (pmFromX < 0 || pmFromY < 0) return;
4164 DropMenuEvent(piece, pmFromX, pmFromY);
4168 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4170 shiftKey = prms[0][0] & 1;
4175 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4177 shiftKey = prms[0][0] & 1;
4183 * If the user selects on a border boundary, return -1; if off the board,
4184 * return -2. Otherwise map the event coordinate to the square.
4187 EventToSquare (int x, int limit)
4194 if ((x % (squareSize + lineGap)) >= squareSize)
4196 x /= (squareSize + lineGap);
4203 do_flash_delay (unsigned long msec)
4209 drawHighlight (int file, int rank, GC gc)
4213 if (lineGap == 0) return;
4216 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4217 (squareSize + lineGap);
4218 y = lineGap/2 + rank * (squareSize + lineGap);
4220 x = lineGap/2 + file * (squareSize + lineGap);
4221 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4222 (squareSize + lineGap);
4225 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4226 squareSize+lineGap, squareSize+lineGap);
4229 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4230 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4233 SetHighlights (int fromX, int fromY, int toX, int toY)
4235 if (hi1X != fromX || hi1Y != fromY) {
4236 if (hi1X >= 0 && hi1Y >= 0) {
4237 drawHighlight(hi1X, hi1Y, lineGC);
4239 } // [HGM] first erase both, then draw new!
4240 if (hi2X != toX || hi2Y != toY) {
4241 if (hi2X >= 0 && hi2Y >= 0) {
4242 drawHighlight(hi2X, hi2Y, lineGC);
4245 if (hi1X != fromX || hi1Y != fromY) {
4246 if (fromX >= 0 && fromY >= 0) {
4247 drawHighlight(fromX, fromY, highlineGC);
4250 if (hi2X != toX || hi2Y != toY) {
4251 if (toX >= 0 && toY >= 0) {
4252 drawHighlight(toX, toY, highlineGC);
4255 if(toX<0) // clearing the highlights must have damaged arrow
4256 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4266 SetHighlights(-1, -1, -1, -1);
4271 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4273 if (pm1X != fromX || pm1Y != fromY) {
4274 if (pm1X >= 0 && pm1Y >= 0) {
4275 drawHighlight(pm1X, pm1Y, lineGC);
4277 if (fromX >= 0 && fromY >= 0) {
4278 drawHighlight(fromX, fromY, prelineGC);
4281 if (pm2X != toX || pm2Y != toY) {
4282 if (pm2X >= 0 && pm2Y >= 0) {
4283 drawHighlight(pm2X, pm2Y, lineGC);
4285 if (toX >= 0 && toY >= 0) {
4286 drawHighlight(toX, toY, prelineGC);
4296 ClearPremoveHighlights ()
4298 SetPremoveHighlights(-1, -1, -1, -1);
4302 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4304 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4305 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4307 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4308 if(textureW[kind] < W*squareSize)
4309 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4311 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4312 if(textureH[kind] < H*squareSize)
4313 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4315 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4320 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4321 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4323 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4324 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4325 squareSize, squareSize, x*fac, y*fac);
4327 if (useImages && useImageSqs) {
4331 pm = xpmLightSquare;
4336 case 2: /* neutral */
4341 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4342 squareSize, squareSize, x*fac, y*fac);
4352 case 2: /* neutral */
4357 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4362 I split out the routines to draw a piece so that I could
4363 make a generic flash routine.
4366 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4368 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4369 switch (square_color) {
4371 case 2: /* neutral */
4373 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4374 ? *pieceToOutline(piece)
4375 : *pieceToSolid(piece),
4376 dest, bwPieceGC, 0, 0,
4377 squareSize, squareSize, x, y);
4380 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4381 ? *pieceToSolid(piece)
4382 : *pieceToOutline(piece),
4383 dest, wbPieceGC, 0, 0,
4384 squareSize, squareSize, x, y);
4390 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4392 switch (square_color) {
4394 case 2: /* neutral */
4396 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4397 ? *pieceToOutline(piece)
4398 : *pieceToSolid(piece),
4399 dest, bwPieceGC, 0, 0,
4400 squareSize, squareSize, x, y, 1);
4403 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4404 ? *pieceToSolid(piece)
4405 : *pieceToOutline(piece),
4406 dest, wbPieceGC, 0, 0,
4407 squareSize, squareSize, x, y, 1);
4413 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4415 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4416 switch (square_color) {
4418 XCopyPlane(xDisplay, *pieceToSolid(piece),
4419 dest, (int) piece < (int) BlackPawn
4420 ? wlPieceGC : blPieceGC, 0, 0,
4421 squareSize, squareSize, x, y, 1);
4424 XCopyPlane(xDisplay, *pieceToSolid(piece),
4425 dest, (int) piece < (int) BlackPawn
4426 ? wdPieceGC : bdPieceGC, 0, 0,
4427 squareSize, squareSize, x, y, 1);
4429 case 2: /* neutral */
4431 XCopyPlane(xDisplay, *pieceToSolid(piece),
4432 dest, (int) piece < (int) BlackPawn
4433 ? wjPieceGC : bjPieceGC, 0, 0,
4434 squareSize, squareSize, x, y, 1);
4440 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4442 int kind, p = piece;
4444 switch (square_color) {
4446 case 2: /* neutral */
4448 if ((int)piece < (int) BlackPawn) {
4456 if ((int)piece < (int) BlackPawn) {
4464 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4465 if(useTexture & square_color+1) {
4466 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4467 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4468 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4469 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4470 XSetClipMask(xDisplay, wlPieceGC, None);
4471 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4473 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4474 dest, wlPieceGC, 0, 0,
4475 squareSize, squareSize, x, y);
4478 typedef void (*DrawFunc)();
4483 if (appData.monoMode) {
4484 if (DefaultDepth(xDisplay, xScreen) == 1) {
4485 return monoDrawPiece_1bit;
4487 return monoDrawPiece;
4491 return colorDrawPieceImage;
4493 return colorDrawPiece;
4497 /* [HR] determine square color depending on chess variant. */
4499 SquareColor (int row, int column)
4503 if (gameInfo.variant == VariantXiangqi) {
4504 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4506 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4508 } else if (row <= 4) {
4514 square_color = ((column + row) % 2) == 1;
4517 /* [hgm] holdings: next line makes all holdings squares light */
4518 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4520 return square_color;
4524 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4526 int square_color, x, y, direction, font_ascent, font_descent;
4529 XCharStruct overall;
4533 /* Calculate delay in milliseconds (2-delays per complete flash) */
4534 flash_delay = 500 / appData.flashRate;
4537 x = lineGap + ((BOARD_WIDTH-1)-column) *
4538 (squareSize + lineGap);
4539 y = lineGap + row * (squareSize + lineGap);
4541 x = lineGap + column * (squareSize + lineGap);
4542 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4543 (squareSize + lineGap);
4546 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4548 square_color = SquareColor(row, column);
4550 if ( // [HGM] holdings: blank out area between board and holdings
4551 column == BOARD_LEFT-1 || column == BOARD_RGHT
4552 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4553 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4554 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4556 // [HGM] print piece counts next to holdings
4557 string[1] = NULLCHAR;
4558 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4559 string[0] = '0' + piece;
4560 XTextExtents(countFontStruct, string, 1, &direction,
4561 &font_ascent, &font_descent, &overall);
4562 if (appData.monoMode) {
4563 XDrawImageString(xDisplay, xBoardWindow, countGC,
4564 x + squareSize - overall.width - 2,
4565 y + font_ascent + 1, string, 1);
4567 XDrawString(xDisplay, xBoardWindow, countGC,
4568 x + squareSize - overall.width - 2,
4569 y + font_ascent + 1, string, 1);
4572 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4573 string[0] = '0' + piece;
4574 XTextExtents(countFontStruct, string, 1, &direction,
4575 &font_ascent, &font_descent, &overall);
4576 if (appData.monoMode) {
4577 XDrawImageString(xDisplay, xBoardWindow, countGC,
4578 x + 2, y + font_ascent + 1, string, 1);
4580 XDrawString(xDisplay, xBoardWindow, countGC,
4581 x + 2, y + font_ascent + 1, string, 1);
4585 if (piece == EmptySquare || appData.blindfold) {
4586 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4588 drawfunc = ChooseDrawFunc();
4590 if (do_flash && appData.flashCount > 0) {
4591 for (i=0; i<appData.flashCount; ++i) {
4592 drawfunc(piece, square_color, x, y, xBoardWindow);
4593 XSync(xDisplay, False);
4594 do_flash_delay(flash_delay);
4596 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4597 XSync(xDisplay, False);
4598 do_flash_delay(flash_delay);
4601 drawfunc(piece, square_color, x, y, xBoardWindow);
4605 string[1] = NULLCHAR;
4606 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4607 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4608 string[0] = 'a' + column - BOARD_LEFT;
4609 XTextExtents(coordFontStruct, string, 1, &direction,
4610 &font_ascent, &font_descent, &overall);
4611 if (appData.monoMode) {
4612 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4613 x + squareSize - overall.width - 2,
4614 y + squareSize - font_descent - 1, string, 1);
4616 XDrawString(xDisplay, xBoardWindow, coordGC,
4617 x + squareSize - overall.width - 2,
4618 y + squareSize - font_descent - 1, string, 1);
4621 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4622 string[0] = ONE + row;
4623 XTextExtents(coordFontStruct, string, 1, &direction,
4624 &font_ascent, &font_descent, &overall);
4625 if (appData.monoMode) {
4626 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4627 x + 2, y + font_ascent + 1, string, 1);
4629 XDrawString(xDisplay, xBoardWindow, coordGC,
4630 x + 2, y + font_ascent + 1, string, 1);
4633 if(!partnerUp && marker[row][column]) {
4634 if(appData.monoMode) {
4635 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4636 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4637 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4638 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4640 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4641 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4646 /* Why is this needed on some versions of X? */
4648 EventProc (Widget widget, caddr_t unused, XEvent *event)
4650 if (!XtIsRealized(widget))
4653 switch (event->type) {
4655 if (event->xexpose.count > 0) return; /* no clipping is done */
4656 XDrawPosition(widget, True, NULL);
4657 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4658 flipView = !flipView; partnerUp = !partnerUp;
4659 XDrawPosition(widget, True, NULL);
4660 flipView = !flipView; partnerUp = !partnerUp;
4664 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4672 DrawPosition (int fullRedraw, Board board)
4674 XDrawPosition(boardWidget, fullRedraw, board);
4677 /* Returns 1 if there are "too many" differences between b1 and b2
4678 (i.e. more than 1 move was made) */
4680 too_many_diffs (Board b1, Board b2)
4685 for (i=0; i<BOARD_HEIGHT; ++i) {
4686 for (j=0; j<BOARD_WIDTH; ++j) {
4687 if (b1[i][j] != b2[i][j]) {
4688 if (++c > 4) /* Castling causes 4 diffs */
4696 /* Matrix describing castling maneuvers */
4697 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4698 static int castling_matrix[4][5] = {
4699 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4700 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4701 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4702 { 7, 7, 4, 5, 6 } /* 0-0, black */
4705 /* Checks whether castling occurred. If it did, *rrow and *rcol
4706 are set to the destination (row,col) of the rook that moved.
4708 Returns 1 if castling occurred, 0 if not.
4710 Note: Only handles a max of 1 castling move, so be sure
4711 to call too_many_diffs() first.
4714 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4719 /* For each type of castling... */
4720 for (i=0; i<4; ++i) {
4721 r = castling_matrix[i];
4723 /* Check the 4 squares involved in the castling move */
4725 for (j=1; j<=4; ++j) {
4726 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4733 /* All 4 changed, so it must be a castling move */
4742 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4744 DrawSeekAxis (int x, int y, int xTo, int yTo)
4746 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4750 DrawSeekBackground (int left, int top, int right, int bottom)
4752 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4756 DrawSeekText (char *buf, int x, int y)
4758 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4762 DrawSeekDot (int x, int y, int colorNr)
4764 int square = colorNr & 0x80;
4767 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4769 XFillRectangle(xDisplay, xBoardWindow, color,
4770 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4772 XFillArc(xDisplay, xBoardWindow, color,
4773 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4776 static int damage[2][BOARD_RANKS][BOARD_FILES];
4779 * event handler for redrawing the board
4782 XDrawPosition (Widget w, int repaint, Board board)
4785 static int lastFlipView = 0;
4786 static int lastBoardValid[2] = {0, 0};
4787 static Board lastBoard[2];
4790 int nr = twoBoards*partnerUp;
4792 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4794 if (board == NULL) {
4795 if (!lastBoardValid[nr]) return;
4796 board = lastBoard[nr];
4798 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4799 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4800 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4805 * It would be simpler to clear the window with XClearWindow()
4806 * but this causes a very distracting flicker.
4809 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4811 if ( lineGap && IsDrawArrowEnabled())
4812 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4813 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4815 /* If too much changes (begin observing new game, etc.), don't
4817 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4819 /* Special check for castling so we don't flash both the king
4820 and the rook (just flash the king). */
4822 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4823 /* Draw rook with NO flashing. King will be drawn flashing later */
4824 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4825 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4829 /* First pass -- Draw (newly) empty squares and repair damage.
4830 This prevents you from having a piece show up twice while it
4831 is flashing on its new square */
4832 for (i = 0; i < BOARD_HEIGHT; i++)
4833 for (j = 0; j < BOARD_WIDTH; j++)
4834 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4835 || damage[nr][i][j]) {
4836 DrawSquare(i, j, board[i][j], 0);
4837 damage[nr][i][j] = False;
4840 /* Second pass -- Draw piece(s) in new position and flash them */
4841 for (i = 0; i < BOARD_HEIGHT; i++)
4842 for (j = 0; j < BOARD_WIDTH; j++)
4843 if (board[i][j] != lastBoard[nr][i][j]) {
4844 DrawSquare(i, j, board[i][j], do_flash);
4848 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4849 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4850 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4852 for (i = 0; i < BOARD_HEIGHT; i++)
4853 for (j = 0; j < BOARD_WIDTH; j++) {
4854 DrawSquare(i, j, board[i][j], 0);
4855 damage[nr][i][j] = False;
4859 CopyBoard(lastBoard[nr], board);
4860 lastBoardValid[nr] = 1;
4861 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4862 lastFlipView = flipView;
4864 /* Draw highlights */
4865 if (pm1X >= 0 && pm1Y >= 0) {
4866 drawHighlight(pm1X, pm1Y, prelineGC);
4868 if (pm2X >= 0 && pm2Y >= 0) {
4869 drawHighlight(pm2X, pm2Y, prelineGC);
4871 if (hi1X >= 0 && hi1Y >= 0) {
4872 drawHighlight(hi1X, hi1Y, highlineGC);
4874 if (hi2X >= 0 && hi2Y >= 0) {
4875 drawHighlight(hi2X, hi2Y, highlineGC);
4877 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4879 /* If piece being dragged around board, must redraw that too */
4882 XSync(xDisplay, False);
4887 * event handler for redrawing the board
4890 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4892 XDrawPosition(w, True, NULL);
4897 * event handler for parsing user moves
4899 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4900 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4901 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4902 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4903 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4904 // and at the end FinishMove() to perform the move after optional promotion popups.
4905 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4907 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4909 if (w != boardWidget || errorExitStatus != -1) return;
4910 if(nprms) shiftKey = !strcmp(prms[0], "1");
4913 if (event->type == ButtonPress) {
4914 XtPopdown(promotionShell);
4915 XtDestroyWidget(promotionShell);
4916 promotionUp = False;
4924 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4925 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4926 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4930 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4932 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4933 DragPieceMove(event->xmotion.x, event->xmotion.y);
4937 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4938 { // [HGM] pv: walk PV
4939 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4942 static int savedIndex; /* gross that this is global */
4945 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4948 XawTextPosition index, dummy;
4951 XawTextGetSelectionPos(w, &index, &dummy);
4952 XtSetArg(arg, XtNstring, &val);
4953 XtGetValues(w, &arg, 1);
4954 ReplaceComment(savedIndex, val);
4955 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4956 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4960 EditCommentPopUp (int index, char *title, char *text)
4963 if (text == NULL) text = "";
4964 NewCommentPopup(title, text, index);
4973 extern Option boxOptions[];
4983 edit = boxOptions[0].handle;
4985 XtSetArg(args[j], XtNstring, &val); j++;
4986 XtGetValues(edit, args, j);
4988 SendMultiLineToICS(val);
4989 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4990 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4994 ICSInputBoxPopDown ()
5000 CommentPopUp (char *title, char *text)
5002 savedIndex = currentMove; // [HGM] vari
5003 NewCommentPopup(title, text, currentMove);
5012 static char *openName;
5018 (void) (*fileProc)(openFP, 0, openName);
5022 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
5024 fileProc = proc; /* I can't see a way not */
5025 fileOpenMode = openMode; /* to use globals here */
5026 { // [HGM] use file-selector dialog stolen from Ghostview
5027 int index; // this is not supported yet
5028 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5029 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5030 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5031 ScheduleDelayedEvent(&DelayedLoad, 50);
5038 if (!filenameUp) return;
5039 XtPopdown(fileNameShell);
5040 XtDestroyWidget(fileNameShell);
5046 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5051 XtSetArg(args[0], XtNlabel, &name);
5052 XtGetValues(w, args, 1);
5054 if (strcmp(name, _("cancel")) == 0) {
5059 FileNameAction(w, NULL, NULL, NULL);
5063 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5071 name = XawDialogGetValueString(w = XtParent(w));
5073 if ((name != NULL) && (*name != NULLCHAR)) {
5074 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5075 XtPopdown(w = XtParent(XtParent(w)));
5079 p = strrchr(buf, ' ');
5086 fullname = ExpandPathName(buf);
5088 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5091 f = fopen(fullname, fileOpenMode);
5093 DisplayError(_("Failed to open file"), errno);
5095 (void) (*fileProc)(f, index, buf);
5102 XtPopdown(w = XtParent(XtParent(w)));
5112 Widget dialog, layout;
5114 Dimension bw_width, pw_width;
5116 char *PromoChars = "wglcqrbnkac+=\0";
5119 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5120 XtGetValues(boardWidget, args, j);
5123 XtSetArg(args[j], XtNresizable, True); j++;
5124 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5126 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5127 shellWidget, args, j);
5129 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5130 layoutArgs, XtNumber(layoutArgs));
5133 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5134 XtSetArg(args[j], XtNborderWidth, 0); j++;
5135 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5138 if(gameInfo.variant != VariantShogi) {
5139 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5140 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5141 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5142 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5143 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5145 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5146 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5147 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5148 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5150 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5151 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5152 gameInfo.variant == VariantGiveaway) {
5153 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5155 if(gameInfo.variant == VariantCapablanca ||
5156 gameInfo.variant == VariantGothic ||
5157 gameInfo.variant == VariantCapaRandom) {
5158 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5159 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5161 } else // [HGM] shogi
5163 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5164 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5166 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5168 XtRealizeWidget(promotionShell);
5169 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5172 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5173 XtGetValues(promotionShell, args, j);
5175 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5176 lineGap + squareSize/3 +
5177 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5178 0 : 6*(squareSize + lineGap)), &x, &y);
5181 XtSetArg(args[j], XtNx, x); j++;
5182 XtSetArg(args[j], XtNy, y); j++;
5183 XtSetValues(promotionShell, args, j);
5185 XtPopup(promotionShell, XtGrabNone);
5193 if (!promotionUp) return;
5194 XtPopdown(promotionShell);
5195 XtDestroyWidget(promotionShell);
5196 promotionUp = False;
5200 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5202 int promoChar = * (const char *) client_data;
5206 if (fromX == -1) return;
5213 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5215 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5216 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5222 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5225 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5227 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5234 if (!errorUp) return;
5236 XtPopdown(errorShell);
5237 XtDestroyWidget(errorShell);
5238 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5242 ErrorPopUp (char *title, char *label, int modal)
5245 Widget dialog, layout;
5249 Dimension bw_width, pw_width;
5250 Dimension pw_height;
5254 XtSetArg(args[i], XtNresizable, True); i++;
5255 XtSetArg(args[i], XtNtitle, title); i++;
5257 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5258 shellWidget, args, i);
5260 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5261 layoutArgs, XtNumber(layoutArgs));
5264 XtSetArg(args[i], XtNlabel, label); i++;
5265 XtSetArg(args[i], XtNborderWidth, 0); i++;
5266 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5269 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5271 XtRealizeWidget(errorShell);
5272 CatchDeleteWindow(errorShell, "ErrorPopDown");
5275 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5276 XtGetValues(boardWidget, args, i);
5278 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5279 XtSetArg(args[i], XtNheight, &pw_height); i++;
5280 XtGetValues(errorShell, args, i);
5283 /* This code seems to tickle an X bug if it is executed too soon
5284 after xboard starts up. The coordinates get transformed as if
5285 the main window was positioned at (0, 0).
5287 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5288 0 - pw_height + squareSize / 3, &x, &y);
5290 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5291 RootWindowOfScreen(XtScreen(boardWidget)),
5292 (bw_width - pw_width) / 2,
5293 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5297 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5300 XtSetArg(args[i], XtNx, x); i++;
5301 XtSetArg(args[i], XtNy, y); i++;
5302 XtSetValues(errorShell, args, i);
5305 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5308 /* Disable all user input other than deleting the window */
5309 static int frozen = 0;
5315 /* Grab by a widget that doesn't accept input */
5316 XtAddGrab(messageWidget, TRUE, FALSE);
5320 /* Undo a FreezeUI */
5324 if (!frozen) return;
5325 XtRemoveGrab(messageWidget);
5330 ModeToWidgetName (GameMode mode)
5333 case BeginningOfGame:
5334 if (appData.icsActive)
5335 return "menuMode.ICS Client";
5336 else if (appData.noChessProgram ||
5337 *appData.cmailGameName != NULLCHAR)
5338 return "menuMode.Edit Game";
5340 return "menuMode.Machine Black";
5341 case MachinePlaysBlack:
5342 return "menuMode.Machine Black";
5343 case MachinePlaysWhite:
5344 return "menuMode.Machine White";
5346 return "menuMode.Analysis Mode";
5348 return "menuMode.Analyze File";
5349 case TwoMachinesPlay:
5350 return "menuMode.Two Machines";
5352 return "menuMode.Edit Game";
5353 case PlayFromGameFile:
5354 return "menuFile.Load Game";
5356 return "menuMode.Edit Position";
5358 return "menuMode.Training";
5359 case IcsPlayingWhite:
5360 case IcsPlayingBlack:
5364 return "menuMode.ICS Client";
5375 static int oldPausing = FALSE;
5376 static GameMode oldmode = (GameMode) -1;
5379 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5381 if (pausing != oldPausing) {
5382 oldPausing = pausing;
5384 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5386 XtSetArg(args[0], XtNleftBitmap, None);
5388 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5391 if (appData.showButtonBar) {
5392 /* Always toggle, don't set. Previous code messes up when
5393 invoked while the button is pressed, as releasing it
5394 toggles the state again. */
5397 XtSetArg(args[0], XtNbackground, &oldbg);
5398 XtSetArg(args[1], XtNforeground, &oldfg);
5399 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5401 XtSetArg(args[0], XtNbackground, oldfg);
5402 XtSetArg(args[1], XtNforeground, oldbg);
5404 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5408 wname = ModeToWidgetName(oldmode);
5409 if (wname != NULL) {
5410 XtSetArg(args[0], XtNleftBitmap, None);
5411 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5413 wname = ModeToWidgetName(gameMode);
5414 if (wname != NULL) {
5415 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5416 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5419 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5420 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5422 /* Maybe all the enables should be handled here, not just this one */
5423 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5424 gameMode == Training || gameMode == PlayFromGameFile);
5429 * Button/menu procedures
5432 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5438 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5440 cmailMsgLoaded = FALSE;
5441 if (gameNumber == 0) {
5442 int error = GameListBuild(f);
5444 DisplayError(_("Cannot build game list"), error);
5445 } else if (!ListEmpty(&gameList) &&
5446 ((ListGame *) gameList.tailPred)->number > 1) {
5447 GameListPopUp(f, title);
5453 return LoadGame(f, gameNumber, title, FALSE);
5457 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5459 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5462 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5466 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5472 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5478 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5484 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5490 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5496 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5502 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5504 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5507 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5511 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5513 FileNamePopUp(_("Save game file name?"),
5514 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5515 appData.oldSaveStyle ? ".game" : ".pgn",
5520 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5522 FileNamePopUp(_("Save position file name?"),
5523 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5524 appData.oldSaveStyle ? ".pos" : ".fen",
5529 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5531 ReloadCmailMsgEvent(FALSE);
5535 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5540 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5541 char *selected_fen_position=NULL;
5544 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5545 Atom *type_return, XtPointer *value_return,
5546 unsigned long *length_return, int *format_return)
5548 char *selection_tmp;
5550 if (!selected_fen_position) return False; /* should never happen */
5551 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5552 /* note: since no XtSelectionDoneProc was registered, Xt will
5553 * automatically call XtFree on the value returned. So have to
5554 * make a copy of it allocated with XtMalloc */
5555 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5556 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5558 *value_return=selection_tmp;
5559 *length_return=strlen(selection_tmp);
5560 *type_return=*target;
5561 *format_return = 8; /* bits per byte */
5563 } else if (*target == XA_TARGETS(xDisplay)) {
5564 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5565 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5566 targets_tmp[1] = XA_STRING;
5567 *value_return = targets_tmp;
5568 *type_return = XA_ATOM;
5571 // This code leads to a read of value_return out of bounds on 64-bit systems.
5572 // Other code which I have seen always sets *format_return to 32 independent of
5573 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5574 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5575 *format_return = 8 * sizeof(Atom);
5576 if (*format_return > 32) {
5577 *length_return *= *format_return / 32;
5578 *format_return = 32;
5581 *format_return = 32;
5589 /* note: when called from menu all parameters are NULL, so no clue what the
5590 * Widget which was clicked on was, or what the click event was
5593 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5596 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5597 * have a notion of a position that is selected but not copied.
5598 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5600 if(gameMode == EditPosition) EditPositionDone(TRUE);
5601 if (selected_fen_position) free(selected_fen_position);
5602 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5603 if (!selected_fen_position) return;
5604 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5606 SendPositionSelection,
5607 NULL/* lose_ownership_proc */ ,
5608 NULL/* transfer_done_proc */);
5609 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5611 SendPositionSelection,
5612 NULL/* lose_ownership_proc */ ,
5613 NULL/* transfer_done_proc */);
5616 /* function called when the data to Paste is ready */
5618 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5619 Atom *type, XtPointer value, unsigned long *len, int *format)
5622 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5623 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5624 EditPositionPasteFEN(fenstr);
5628 /* called when Paste Position button is pressed,
5629 * all parameters will be NULL */
5630 void PastePositionProc(w, event, prms, nprms)
5636 XtGetSelectionValue(menuBarWidget,
5637 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5638 /* (XtSelectionCallbackProc) */ PastePositionCB,
5639 NULL, /* client_data passed to PastePositionCB */
5641 /* better to use the time field from the event that triggered the
5642 * call to this function, but that isn't trivial to get
5650 SendGameSelection (Widget w, Atom *selection, Atom *target,
5651 Atom *type_return, XtPointer *value_return,
5652 unsigned long *length_return, int *format_return)
5654 char *selection_tmp;
5656 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5657 FILE* f = fopen(gameCopyFilename, "r");
5660 if (f == NULL) return False;
5664 selection_tmp = XtMalloc(len + 1);
5665 count = fread(selection_tmp, 1, len, f);
5668 XtFree(selection_tmp);
5671 selection_tmp[len] = NULLCHAR;
5672 *value_return = selection_tmp;
5673 *length_return = len;
5674 *type_return = *target;
5675 *format_return = 8; /* bits per byte */
5677 } else if (*target == XA_TARGETS(xDisplay)) {
5678 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5679 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5680 targets_tmp[1] = XA_STRING;
5681 *value_return = targets_tmp;
5682 *type_return = XA_ATOM;
5685 // This code leads to a read of value_return out of bounds on 64-bit systems.
5686 // Other code which I have seen always sets *format_return to 32 independent of
5687 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5688 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5689 *format_return = 8 * sizeof(Atom);
5690 if (*format_return > 32) {
5691 *length_return *= *format_return / 32;
5692 *format_return = 32;
5695 *format_return = 32;
5707 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5708 * have a notion of a game that is selected but not copied.
5709 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5711 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5714 NULL/* lose_ownership_proc */ ,
5715 NULL/* transfer_done_proc */);
5716 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5719 NULL/* lose_ownership_proc */ ,
5720 NULL/* transfer_done_proc */);
5723 /* note: when called from menu all parameters are NULL, so no clue what the
5724 * Widget which was clicked on was, or what the click event was
5727 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5731 ret = SaveGameToFile(gameCopyFilename, FALSE);
5738 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5740 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5744 /* function called when the data to Paste is ready */
5746 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5747 Atom *type, XtPointer value, unsigned long *len, int *format)
5750 if (value == NULL || *len == 0) {
5751 return; /* nothing had been selected to copy */
5753 f = fopen(gamePasteFilename, "w");
5755 DisplayError(_("Can't open temp file"), errno);
5758 fwrite(value, 1, *len, f);
5761 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5764 /* called when Paste Game button is pressed,
5765 * all parameters will be NULL */
5767 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5769 XtGetSelectionValue(menuBarWidget,
5770 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5771 /* (XtSelectionCallbackProc) */ PasteGameCB,
5772 NULL, /* client_data passed to PasteGameCB */
5774 /* better to use the time field from the event that triggered the
5775 * call to this function, but that isn't trivial to get
5786 SaveGameProc(NULL, NULL, NULL, NULL);
5791 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5797 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5803 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5805 MachineBlackEvent();
5809 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5811 MachineWhiteEvent();
5815 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5819 if (!first.analysisSupport) {
5820 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5821 DisplayError(buf, 0);
5824 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5825 if (appData.icsActive) {
5826 if (gameMode != IcsObserving) {
5827 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5828 DisplayError(buf, 0);
5830 if (appData.icsEngineAnalyze) {
5831 if (appData.debugMode)
5832 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5838 /* if enable, use want disable icsEngineAnalyze */
5839 if (appData.icsEngineAnalyze) {
5844 appData.icsEngineAnalyze = TRUE;
5845 if (appData.debugMode)
5846 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5848 #ifndef OPTIONSDIALOG
5849 if (!appData.showThinking)
5850 ShowThinkingProc(w,event,prms,nprms);
5857 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5859 if (!first.analysisSupport) {
5861 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5862 DisplayError(buf, 0);
5865 // Reset(FALSE, TRUE);
5866 #ifndef OPTIONSDIALOG
5867 if (!appData.showThinking)
5868 ShowThinkingProc(w,event,prms,nprms);
5871 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5872 AnalysisPeriodicEvent(1);
5876 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5882 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5888 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5894 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5900 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5902 EditPositionEvent();
5906 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5912 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5916 if (PopDown(1)) { // popdown succesful
5918 XtSetArg(args[j], XtNleftBitmap, None); j++;
5919 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5920 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5921 } else // was not up
5926 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5928 if (!PopDown(4)) ICSInputBoxPopUp();
5932 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5938 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5944 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5950 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5956 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5962 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5968 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5974 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5980 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5982 UserAdjudicationEvent(+1);
5986 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5988 UserAdjudicationEvent(-1);
5992 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5994 UserAdjudicationEvent(0);
5998 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6000 if (shellUp[4] == True)
6005 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6006 { // [HGM] input: let up-arrow recall previous line from history
6013 if (!shellUp[4]) return;
6014 edit = boxOptions[0].handle;
6016 XtSetArg(args[j], XtNstring, &val); j++;
6017 XtGetValues(edit, args, j);
6018 val = PrevInHistory(val);
6019 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6020 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6022 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6023 XawTextReplace(edit, 0, 0, &t);
6024 XawTextSetInsertionPoint(edit, 9999);
6029 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6030 { // [HGM] input: let down-arrow recall next line from history
6035 if (!shellUp[4]) return;
6036 edit = boxOptions[0].handle;
6037 val = NextInHistory();
6038 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6039 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6041 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6042 XawTextReplace(edit, 0, 0, &t);
6043 XawTextSetInsertionPoint(edit, 9999);
6048 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6050 StopObservingEvent();
6054 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6056 StopExaminingEvent();
6060 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6067 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6074 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6080 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6082 if (!TempBackwardActive) {
6083 TempBackwardActive = True;
6089 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6091 /* Check to see if triggered by a key release event for a repeating key.
6092 * If so the next queued event will be a key press of the same key at the same time */
6093 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6095 XPeekEvent(xDisplay, &next);
6096 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6097 next.xkey.keycode == event->xkey.keycode)
6101 TempBackwardActive = False;
6105 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6111 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6117 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6123 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6129 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6131 TruncateGameEvent();
6135 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6141 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6147 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6149 flipView = !flipView;
6150 DrawPosition(True, NULL);
6154 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6158 PonderNextMoveEvent(!appData.ponderNextMove);
6159 #ifndef OPTIONSDIALOG
6160 if (appData.ponderNextMove) {
6161 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6163 XtSetArg(args[0], XtNleftBitmap, None);
6165 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6170 #ifndef OPTIONSDIALOG
6172 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6176 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6178 if (appData.alwaysPromoteToQueen) {
6179 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6181 XtSetArg(args[0], XtNleftBitmap, None);
6183 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6188 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6192 appData.animateDragging = !appData.animateDragging;
6194 if (appData.animateDragging) {
6195 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6198 XtSetArg(args[0], XtNleftBitmap, None);
6200 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6205 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6209 appData.animate = !appData.animate;
6211 if (appData.animate) {
6212 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6215 XtSetArg(args[0], XtNleftBitmap, None);
6217 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6222 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6226 appData.autoCallFlag = !appData.autoCallFlag;
6228 if (appData.autoCallFlag) {
6229 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6231 XtSetArg(args[0], XtNleftBitmap, None);
6233 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6238 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6242 appData.autoFlipView = !appData.autoFlipView;
6244 if (appData.autoFlipView) {
6245 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6247 XtSetArg(args[0], XtNleftBitmap, None);
6249 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6254 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6258 appData.blindfold = !appData.blindfold;
6260 if (appData.blindfold) {
6261 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6263 XtSetArg(args[0], XtNleftBitmap, None);
6265 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6268 DrawPosition(True, NULL);
6272 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6276 appData.testLegality = !appData.testLegality;
6278 if (appData.testLegality) {
6279 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6281 XtSetArg(args[0], XtNleftBitmap, None);
6283 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6289 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6293 if (appData.flashCount == 0) {
6294 appData.flashCount = 3;
6296 appData.flashCount = -appData.flashCount;
6299 if (appData.flashCount > 0) {
6300 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6302 XtSetArg(args[0], XtNleftBitmap, None);
6304 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6310 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6314 appData.highlightDragging = !appData.highlightDragging;
6316 if (appData.highlightDragging) {
6317 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6319 XtSetArg(args[0], XtNleftBitmap, None);
6321 XtSetValues(XtNameToWidget(menuBarWidget,
6322 "menuOptions.Highlight Dragging"), args, 1);
6327 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6331 appData.highlightLastMove = !appData.highlightLastMove;
6333 if (appData.highlightLastMove) {
6334 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6336 XtSetArg(args[0], XtNleftBitmap, None);
6338 XtSetValues(XtNameToWidget(menuBarWidget,
6339 "menuOptions.Highlight Last Move"), args, 1);
6343 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6347 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6349 if (appData.highlightMoveWithArrow) {
6350 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6352 XtSetArg(args[0], XtNleftBitmap, None);
6354 XtSetValues(XtNameToWidget(menuBarWidget,
6355 "menuOptions.Arrow"), args, 1);
6360 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6364 appData.icsAlarm = !appData.icsAlarm;
6366 if (appData.icsAlarm) {
6367 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6369 XtSetArg(args[0], XtNleftBitmap, None);
6371 XtSetValues(XtNameToWidget(menuBarWidget,
6372 "menuOptions.ICS Alarm"), args, 1);
6377 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6381 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6383 if (appData.ringBellAfterMoves) {
6384 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6386 XtSetArg(args[0], XtNleftBitmap, None);
6388 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6393 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6397 appData.oneClick = !appData.oneClick;
6399 if (appData.oneClick) {
6400 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6402 XtSetArg(args[0], XtNleftBitmap, None);
6404 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6409 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6413 PeriodicUpdatesEvent(!appData.periodicUpdates);
6415 if (appData.periodicUpdates) {
6416 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6418 XtSetArg(args[0], XtNleftBitmap, None);
6420 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6425 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6429 appData.popupExitMessage = !appData.popupExitMessage;
6431 if (appData.popupExitMessage) {
6432 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6434 XtSetArg(args[0], XtNleftBitmap, None);
6436 XtSetValues(XtNameToWidget(menuBarWidget,
6437 "menuOptions.Popup Exit Message"), args, 1);
6441 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6445 appData.popupMoveErrors = !appData.popupMoveErrors;
6447 if (appData.popupMoveErrors) {
6448 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6450 XtSetArg(args[0], XtNleftBitmap, None);
6452 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6458 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6462 appData.premove = !appData.premove;
6464 if (appData.premove) {
6465 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6467 XtSetArg(args[0], XtNleftBitmap, None);
6469 XtSetValues(XtNameToWidget(menuBarWidget,
6470 "menuOptions.Premove"), args, 1);
6475 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6479 appData.showCoords = !appData.showCoords;
6481 if (appData.showCoords) {
6482 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6484 XtSetArg(args[0], XtNleftBitmap, None);
6486 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6489 DrawPosition(True, NULL);
6493 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6495 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6496 ShowThinkingEvent();
6500 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6504 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6505 ShowThinkingEvent();
6507 if (appData.hideThinkingFromHuman) {
6508 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6510 XtSetArg(args[0], XtNleftBitmap, None);
6512 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6518 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6522 saveSettingsOnExit = !saveSettingsOnExit;
6524 if (saveSettingsOnExit) {
6525 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6527 XtSetArg(args[0], XtNleftBitmap, None);
6529 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6534 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6536 SaveSettings(settingsFileName);
6540 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6543 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6549 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6553 if (nprms && *nprms > 0)
6557 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6562 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6568 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6574 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6578 char *zippy = _(" (with Zippy code)");
6582 snprintf(buf, sizeof(buf),
6584 "Copyright 1991 Digital Equipment Corporation\n"
6585 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6586 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6587 "%s is free software and carries NO WARRANTY;"
6588 "see the file COPYING for more information."),
6589 programVersion, zippy, PACKAGE);
6590 ErrorPopUp(_("About XBoard"), buf, FALSE);
6594 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6596 appData.debugMode = !appData.debugMode;
6600 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6606 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6612 DisplayMessage (char *message, char *extMessage)
6614 /* display a message in the message widget */
6623 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6628 message = extMessage;
6632 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6634 /* need to test if messageWidget already exists, since this function
6635 can also be called during the startup, if for example a Xresource
6636 is not set up correctly */
6639 XtSetArg(arg, XtNlabel, message);
6640 XtSetValues(messageWidget, &arg, 1);
6647 DisplayTitle (char *text)
6651 char title[MSG_SIZ];
6654 if (text == NULL) text = "";
6656 if (appData.titleInWindow) {
6658 XtSetArg(args[i], XtNlabel, text); i++;
6659 XtSetValues(titleWidget, args, i);
6662 if (*text != NULLCHAR) {
6663 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6664 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6665 } else if (appData.icsActive) {
6666 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6667 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6668 } else if (appData.cmailGameName[0] != NULLCHAR) {
6669 snprintf(icon, sizeof(icon), "%s", "CMail");
6670 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6672 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6673 } else if (gameInfo.variant == VariantGothic) {
6674 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6675 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6678 } else if (gameInfo.variant == VariantFalcon) {
6679 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6680 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6682 } else if (appData.noChessProgram) {
6683 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6684 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6686 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6687 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6690 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6691 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6692 XtSetValues(shellWidget, args, i);
6693 XSync(xDisplay, False);
6698 DisplayError (String message, int error)
6703 if (appData.debugMode || appData.matchMode) {
6704 fprintf(stderr, "%s: %s\n", programName, message);
6707 if (appData.debugMode || appData.matchMode) {
6708 fprintf(stderr, "%s: %s: %s\n",
6709 programName, message, strerror(error));
6711 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6714 ErrorPopUp(_("Error"), message, FALSE);
6719 DisplayMoveError (String message)
6723 DrawPosition(FALSE, NULL);
6724 if (appData.debugMode || appData.matchMode) {
6725 fprintf(stderr, "%s: %s\n", programName, message);
6727 if (appData.popupMoveErrors) {
6728 ErrorPopUp(_("Error"), message, FALSE);
6730 DisplayMessage(message, "");
6736 DisplayFatalError (String message, int error, int status)
6740 errorExitStatus = status;
6742 fprintf(stderr, "%s: %s\n", programName, message);
6744 fprintf(stderr, "%s: %s: %s\n",
6745 programName, message, strerror(error));
6746 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6749 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6750 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6757 DisplayInformation (String message)
6760 ErrorPopUp(_("Information"), message, TRUE);
6764 DisplayNote (String message)
6767 ErrorPopUp(_("Note"), message, FALSE);
6771 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6777 DisplayIcsInteractionTitle (String message)
6779 if (oldICSInteractionTitle == NULL) {
6780 /* Magic to find the old window title, adapted from vim */
6781 char *wina = getenv("WINDOWID");
6783 Window win = (Window) atoi(wina);
6784 Window root, parent, *children;
6785 unsigned int nchildren;
6786 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6788 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6789 if (!XQueryTree(xDisplay, win, &root, &parent,
6790 &children, &nchildren)) break;
6791 if (children) XFree((void *)children);
6792 if (parent == root || parent == 0) break;
6795 XSetErrorHandler(oldHandler);
6797 if (oldICSInteractionTitle == NULL) {
6798 oldICSInteractionTitle = "xterm";
6801 printf("\033]0;%s\007", message);
6805 char pendingReplyPrefix[MSG_SIZ];
6806 ProcRef pendingReplyPR;
6809 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6812 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6816 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6820 AskQuestionPopDown ()
6822 if (!askQuestionUp) return;
6823 XtPopdown(askQuestionShell);
6824 XtDestroyWidget(askQuestionShell);
6825 askQuestionUp = False;
6829 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6835 reply = XawDialogGetValueString(w = XtParent(w));
6836 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6837 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6838 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6839 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6840 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6841 AskQuestionPopDown();
6843 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6847 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6852 XtSetArg(args[0], XtNlabel, &name);
6853 XtGetValues(w, args, 1);
6855 if (strcmp(name, _("cancel")) == 0) {
6856 AskQuestionPopDown();
6858 AskQuestionReplyAction(w, NULL, NULL, NULL);
6863 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6866 Widget popup, layout, dialog, edit;
6872 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6873 pendingReplyPR = pr;
6876 XtSetArg(args[i], XtNresizable, True); i++;
6877 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6878 askQuestionShell = popup =
6879 XtCreatePopupShell(title, transientShellWidgetClass,
6880 shellWidget, args, i);
6883 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6884 layoutArgs, XtNumber(layoutArgs));
6887 XtSetArg(args[i], XtNlabel, question); i++;
6888 XtSetArg(args[i], XtNvalue, ""); i++;
6889 XtSetArg(args[i], XtNborderWidth, 0); i++;
6890 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6893 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6894 (XtPointer) dialog);
6895 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6896 (XtPointer) dialog);
6898 XtRealizeWidget(popup);
6899 CatchDeleteWindow(popup, "AskQuestionPopDown");
6901 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6902 &x, &y, &win_x, &win_y, &mask);
6904 XtSetArg(args[0], XtNx, x - 10);
6905 XtSetArg(args[1], XtNy, y - 30);
6906 XtSetValues(popup, args, 2);
6908 XtPopup(popup, XtGrabExclusive);
6909 askQuestionUp = True;
6911 edit = XtNameToWidget(dialog, "*value");
6912 XtSetKeyboardFocus(popup, edit);
6917 PlaySound (char *name)
6919 if (*name == NULLCHAR) {
6921 } else if (strcmp(name, "$") == 0) {
6922 putc(BELLCHAR, stderr);
6925 char *prefix = "", *sep = "";
6926 if(appData.soundProgram[0] == NULLCHAR) return;
6927 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6928 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6936 PlaySound(appData.soundMove);
6942 PlaySound(appData.soundIcsWin);
6948 PlaySound(appData.soundIcsLoss);
6954 PlaySound(appData.soundIcsDraw);
6958 PlayIcsUnfinishedSound ()
6960 PlaySound(appData.soundIcsUnfinished);
6966 PlaySound(appData.soundIcsAlarm);
6972 PlaySound(appData.soundTell);
6978 system("stty echo");
6985 system("stty -echo");
6990 RunCommand (char *buf)
6996 Colorize (ColorClass cc, int continuation)
6999 int count, outCount, error;
7001 if (textColors[(int)cc].bg > 0) {
7002 if (textColors[(int)cc].fg > 0) {
7003 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7004 textColors[(int)cc].fg, textColors[(int)cc].bg);
7006 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7007 textColors[(int)cc].bg);
7010 if (textColors[(int)cc].fg > 0) {
7011 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7012 textColors[(int)cc].fg);
7014 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7017 count = strlen(buf);
7018 outCount = OutputToProcess(NoProc, buf, count, &error);
7019 if (outCount < count) {
7020 DisplayFatalError(_("Error writing to display"), error, 1);
7023 if (continuation) return;
7026 PlaySound(appData.soundShout);
7029 PlaySound(appData.soundSShout);
7032 PlaySound(appData.soundChannel1);
7035 PlaySound(appData.soundChannel);
7038 PlaySound(appData.soundKibitz);
7041 PlaySound(appData.soundTell);
7043 case ColorChallenge:
7044 PlaySound(appData.soundChallenge);
7047 PlaySound(appData.soundRequest);
7050 PlaySound(appData.soundSeek);
7062 return getpwuid(getuid())->pw_name;
7066 ExpandPathName (char *path)
7068 static char static_buf[4*MSG_SIZ];
7069 char *d, *s, buf[4*MSG_SIZ];
7075 while (*s && isspace(*s))
7084 if (*(s+1) == '/') {
7085 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7089 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7090 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7091 pwd = getpwnam(buf);
7094 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7098 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7099 strcat(d, strchr(s+1, '/'));
7103 safeStrCpy(d, s, 4*MSG_SIZ );
7111 static char host_name[MSG_SIZ];
7113 #if HAVE_GETHOSTNAME
7114 gethostname(host_name, MSG_SIZ);
7116 #else /* not HAVE_GETHOSTNAME */
7117 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7118 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7120 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7122 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7123 #endif /* not HAVE_GETHOSTNAME */
7126 XtIntervalId delayedEventTimerXID = 0;
7127 DelayedEventCallback delayedEventCallback = 0;
7132 delayedEventTimerXID = 0;
7133 delayedEventCallback();
7137 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7139 if(delayedEventTimerXID && delayedEventCallback == cb)
7140 // [HGM] alive: replace, rather than add or flush identical event
7141 XtRemoveTimeOut(delayedEventTimerXID);
7142 delayedEventCallback = cb;
7143 delayedEventTimerXID =
7144 XtAppAddTimeOut(appContext, millisec,
7145 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7148 DelayedEventCallback
7151 if (delayedEventTimerXID) {
7152 return delayedEventCallback;
7159 CancelDelayedEvent ()
7161 if (delayedEventTimerXID) {
7162 XtRemoveTimeOut(delayedEventTimerXID);
7163 delayedEventTimerXID = 0;
7167 XtIntervalId loadGameTimerXID = 0;
7170 LoadGameTimerRunning ()
7172 return loadGameTimerXID != 0;
7176 StopLoadGameTimer ()
7178 if (loadGameTimerXID != 0) {
7179 XtRemoveTimeOut(loadGameTimerXID);
7180 loadGameTimerXID = 0;
7188 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7190 loadGameTimerXID = 0;
7195 StartLoadGameTimer (long millisec)
7198 XtAppAddTimeOut(appContext, millisec,
7199 (XtTimerCallbackProc) LoadGameTimerCallback,
7203 XtIntervalId analysisClockXID = 0;
7206 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7208 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7209 || appData.icsEngineAnalyze) { // [DM]
7210 AnalysisPeriodicEvent(0);
7211 StartAnalysisClock();
7216 StartAnalysisClock ()
7219 XtAppAddTimeOut(appContext, 2000,
7220 (XtTimerCallbackProc) AnalysisClockCallback,
7224 XtIntervalId clockTimerXID = 0;
7227 ClockTimerRunning ()
7229 return clockTimerXID != 0;
7235 if (clockTimerXID != 0) {
7236 XtRemoveTimeOut(clockTimerXID);
7245 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7252 StartClockTimer (long millisec)
7255 XtAppAddTimeOut(appContext, millisec,
7256 (XtTimerCallbackProc) ClockTimerCallback,
7261 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7266 /* check for low time warning */
7267 Pixel foregroundOrWarningColor = timerForegroundPixel;
7270 appData.lowTimeWarning &&
7271 (timer / 1000) < appData.icsAlarmTime)
7272 foregroundOrWarningColor = lowTimeWarningColor;
7274 if (appData.clockMode) {
7275 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7276 XtSetArg(args[0], XtNlabel, buf);
7278 snprintf(buf, MSG_SIZ, "%s ", color);
7279 XtSetArg(args[0], XtNlabel, buf);
7284 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7285 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7287 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7288 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7291 XtSetValues(w, args, 3);
7295 DisplayWhiteClock (long timeRemaining, int highlight)
7299 if(appData.noGUI) return;
7300 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7301 if (highlight && iconPixmap == bIconPixmap) {
7302 iconPixmap = wIconPixmap;
7303 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7304 XtSetValues(shellWidget, args, 1);
7309 DisplayBlackClock (long timeRemaining, int highlight)
7313 if(appData.noGUI) return;
7314 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7315 if (highlight && iconPixmap == wIconPixmap) {
7316 iconPixmap = bIconPixmap;
7317 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7318 XtSetValues(shellWidget, args, 1);
7337 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7341 int to_prog[2], from_prog[2];
7345 if (appData.debugMode) {
7346 fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7349 /* We do NOT feed the cmdLine to the shell; we just
7350 parse it into blank-separated arguments in the
7351 most simple-minded way possible.
7354 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7357 while(*p == ' ') p++;
7359 if(*p == '"' || *p == '\'')
7360 p = strchr(++argv[i-1], *p);
7361 else p = strchr(p, ' ');
7362 if (p == NULL) break;
7367 SetUpChildIO(to_prog, from_prog);
7369 if ((pid = fork()) == 0) {
7371 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7372 close(to_prog[1]); // first close the unused pipe ends
7373 close(from_prog[0]);
7374 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7375 dup2(from_prog[1], 1);
7376 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7377 close(from_prog[1]); // and closing again loses one of the pipes!
7378 if(fileno(stderr) >= 2) // better safe than sorry...
7379 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7381 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7386 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7388 execvp(argv[0], argv);
7390 /* If we get here, exec failed */
7395 /* Parent process */
7397 close(from_prog[1]);
7399 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7402 cp->fdFrom = from_prog[0];
7403 cp->fdTo = to_prog[1];
7408 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7410 AlarmCallBack (int n)
7416 DestroyChildProcess (ProcRef pr, int signalType)
7418 ChildProc *cp = (ChildProc *) pr;
7420 if (cp->kind != CPReal) return;
7422 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7423 signal(SIGALRM, AlarmCallBack);
7425 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7426 kill(cp->pid, SIGKILL); // kill it forcefully
7427 wait((int *) 0); // and wait again
7431 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7433 /* Process is exiting either because of the kill or because of
7434 a quit command sent by the backend; either way, wait for it to die.
7443 InterruptChildProcess (ProcRef pr)
7445 ChildProc *cp = (ChildProc *) pr;
7447 if (cp->kind != CPReal) return;
7448 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7452 OpenTelnet (char *host, char *port, ProcRef *pr)
7454 char cmdLine[MSG_SIZ];
7456 if (port[0] == NULLCHAR) {
7457 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7459 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7461 return StartChildProcess(cmdLine, "", pr);
7465 OpenTCP (char *host, char *port, ProcRef *pr)
7468 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7469 #else /* !OMIT_SOCKETS */
7470 struct addrinfo hints;
7471 struct addrinfo *ais, *ai;
7476 memset(&hints, 0, sizeof(hints));
7477 hints.ai_family = AF_UNSPEC;
7478 hints.ai_socktype = SOCK_STREAM;
7480 error = getaddrinfo(host, port, &hints, &ais);
7482 /* a getaddrinfo error is not an errno, so can't return it */
7483 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7484 host, port, gai_strerror(error));
7488 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7489 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7493 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7506 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7512 #endif /* !OMIT_SOCKETS */
7518 OpenCommPort (char *name, ProcRef *pr)
7523 fd = open(name, 2, 0);
7524 if (fd < 0) return errno;
7526 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7537 OpenLoopback (ProcRef *pr)
7542 SetUpChildIO(to, from);
7544 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7547 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7555 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7557 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7561 #define INPUT_SOURCE_BUF_SIZE 8192
7570 char buf[INPUT_SOURCE_BUF_SIZE];
7575 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7577 InputSource *is = (InputSource *) closure;
7582 if (is->lineByLine) {
7583 count = read(is->fd, is->unused,
7584 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7586 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7589 is->unused += count;
7591 while (p < is->unused) {
7592 q = memchr(p, '\n', is->unused - p);
7593 if (q == NULL) break;
7595 (is->func)(is, is->closure, p, q - p, 0);
7599 while (p < is->unused) {
7604 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7609 (is->func)(is, is->closure, is->buf, count, error);
7614 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7617 ChildProc *cp = (ChildProc *) pr;
7619 is = (InputSource *) calloc(1, sizeof(InputSource));
7620 is->lineByLine = lineByLine;
7624 is->fd = fileno(stdin);
7626 is->kind = cp->kind;
7627 is->fd = cp->fdFrom;
7630 is->unused = is->buf;
7633 is->xid = XtAppAddInput(appContext, is->fd,
7634 (XtPointer) (XtInputReadMask),
7635 (XtInputCallbackProc) DoInputCallback,
7637 is->closure = closure;
7638 return (InputSourceRef) is;
7642 RemoveInputSource (InputSourceRef isr)
7644 InputSource *is = (InputSource *) isr;
7646 if (is->xid == 0) return;
7647 XtRemoveInput(is->xid);
7652 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7654 static int line = 0;
7655 ChildProc *cp = (ChildProc *) pr;
7660 if (appData.noJoin || !appData.useInternalWrap)
7661 outCount = fwrite(message, 1, count, stdout);
7664 int width = get_term_width();
7665 int len = wrap(NULL, message, count, width, &line);
7666 char *msg = malloc(len);
7670 outCount = fwrite(message, 1, count, stdout);
7673 dbgchk = wrap(msg, message, count, width, &line);
7674 if (dbgchk != len && appData.debugMode)
7675 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7676 outCount = fwrite(msg, 1, dbgchk, stdout);
7682 outCount = write(cp->fdTo, message, count);
7692 /* Output message to process, with "ms" milliseconds of delay
7693 between each character. This is needed when sending the logon
7694 script to ICC, which for some reason doesn't like the
7695 instantaneous send. */
7697 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7699 ChildProc *cp = (ChildProc *) pr;
7704 r = write(cp->fdTo, message++, 1);
7717 /**** Animation code by Hugh Fisher, DCS, ANU.
7719 Known problem: if a window overlapping the board is
7720 moved away while a piece is being animated underneath,
7721 the newly exposed area won't be updated properly.
7722 I can live with this.
7724 Known problem: if you look carefully at the animation
7725 of pieces in mono mode, they are being drawn as solid
7726 shapes without interior detail while moving. Fixing
7727 this would be a major complication for minimal return.
7730 /* Masks for XPM pieces. Black and white pieces can have
7731 different shapes, but in the interest of retaining my
7732 sanity pieces must have the same outline on both light
7733 and dark squares, and all pieces must use the same
7734 background square colors/images. */
7736 static int xpmDone = 0;
7739 CreateAnimMasks (int pieceDepth)
7745 unsigned long plane;
7748 /* Need a bitmap just to get a GC with right depth */
7749 buf = XCreatePixmap(xDisplay, xBoardWindow,
7751 values.foreground = 1;
7752 values.background = 0;
7753 /* Don't use XtGetGC, not read only */
7754 maskGC = XCreateGC(xDisplay, buf,
7755 GCForeground | GCBackground, &values);
7756 XFreePixmap(xDisplay, buf);
7758 buf = XCreatePixmap(xDisplay, xBoardWindow,
7759 squareSize, squareSize, pieceDepth);
7760 values.foreground = XBlackPixel(xDisplay, xScreen);
7761 values.background = XWhitePixel(xDisplay, xScreen);
7762 bufGC = XCreateGC(xDisplay, buf,
7763 GCForeground | GCBackground, &values);
7765 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7766 /* Begin with empty mask */
7767 if(!xpmDone) // [HGM] pieces: keep using existing
7768 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7769 squareSize, squareSize, 1);
7770 XSetFunction(xDisplay, maskGC, GXclear);
7771 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7772 0, 0, squareSize, squareSize);
7774 /* Take a copy of the piece */
7779 XSetFunction(xDisplay, bufGC, GXcopy);
7780 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7782 0, 0, squareSize, squareSize, 0, 0);
7784 /* XOR the background (light) over the piece */
7785 XSetFunction(xDisplay, bufGC, GXxor);
7787 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7788 0, 0, squareSize, squareSize, 0, 0);
7790 XSetForeground(xDisplay, bufGC, lightSquareColor);
7791 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7794 /* We now have an inverted piece image with the background
7795 erased. Construct mask by just selecting all the non-zero
7796 pixels - no need to reconstruct the original image. */
7797 XSetFunction(xDisplay, maskGC, GXor);
7799 /* Might be quicker to download an XImage and create bitmap
7800 data from it rather than this N copies per piece, but it
7801 only takes a fraction of a second and there is a much
7802 longer delay for loading the pieces. */
7803 for (n = 0; n < pieceDepth; n ++) {
7804 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7805 0, 0, squareSize, squareSize,
7811 XFreePixmap(xDisplay, buf);
7812 XFreeGC(xDisplay, bufGC);
7813 XFreeGC(xDisplay, maskGC);
7817 InitAnimState (AnimState *anim, XWindowAttributes *info)
7822 /* Each buffer is square size, same depth as window */
7823 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7824 squareSize, squareSize, info->depth);
7825 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7826 squareSize, squareSize, info->depth);
7828 /* Create a plain GC for blitting */
7829 mask = GCForeground | GCBackground | GCFunction |
7830 GCPlaneMask | GCGraphicsExposures;
7831 values.foreground = XBlackPixel(xDisplay, xScreen);
7832 values.background = XWhitePixel(xDisplay, xScreen);
7833 values.function = GXcopy;
7834 values.plane_mask = AllPlanes;
7835 values.graphics_exposures = False;
7836 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7838 /* Piece will be copied from an existing context at
7839 the start of each new animation/drag. */
7840 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7842 /* Outline will be a read-only copy of an existing */
7843 anim->outlineGC = None;
7849 XWindowAttributes info;
7851 if (xpmDone && gameInfo.variant == oldVariant) return;
7852 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7853 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7855 InitAnimState(&game, &info);
7856 InitAnimState(&player, &info);
7858 /* For XPM pieces, we need bitmaps to use as masks. */
7860 CreateAnimMasks(info.depth), xpmDone = 1;
7865 static Boolean frameWaiting;
7868 FrameAlarm (int sig)
7870 frameWaiting = False;
7871 /* In case System-V style signals. Needed?? */
7872 signal(SIGALRM, FrameAlarm);
7876 FrameDelay (int time)
7878 struct itimerval delay;
7880 XSync(xDisplay, False);
7883 frameWaiting = True;
7884 signal(SIGALRM, FrameAlarm);
7885 delay.it_interval.tv_sec =
7886 delay.it_value.tv_sec = time / 1000;
7887 delay.it_interval.tv_usec =
7888 delay.it_value.tv_usec = (time % 1000) * 1000;
7889 setitimer(ITIMER_REAL, &delay, NULL);
7890 while (frameWaiting) pause();
7891 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7892 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7893 setitimer(ITIMER_REAL, &delay, NULL);
7900 FrameDelay (int time)
7902 XSync(xDisplay, False);
7904 usleep(time * 1000);
7915 /* Convert board position to corner of screen rect and color */
7918 ScreenSquare (int column, int row, XPoint *pt, int *color)
7921 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7922 pt->y = lineGap + row * (squareSize + lineGap);
7924 pt->x = lineGap + column * (squareSize + lineGap);
7925 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7927 *color = SquareColor(row, column);
7930 /* Convert window coords to square */
7933 BoardSquare (int x, int y, int *column, int *row)
7935 *column = EventToSquare(x, BOARD_WIDTH);
7936 if (flipView && *column >= 0)
7937 *column = BOARD_WIDTH - 1 - *column;
7938 *row = EventToSquare(y, BOARD_HEIGHT);
7939 if (!flipView && *row >= 0)
7940 *row = BOARD_HEIGHT - 1 - *row;
7945 #undef Max /* just in case */
7947 #define Max(a, b) ((a) > (b) ? (a) : (b))
7948 #define Min(a, b) ((a) < (b) ? (a) : (b))
7951 SetRect (XRectangle *rect, int x, int y, int width, int height)
7955 rect->width = width;
7956 rect->height = height;
7959 /* Test if two frames overlap. If they do, return
7960 intersection rect within old and location of
7961 that rect within new. */
7964 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
7966 if (old->x > new->x + size || new->x > old->x + size ||
7967 old->y > new->y + size || new->y > old->y + size) {
7970 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7971 size - abs(old->x - new->x), size - abs(old->y - new->y));
7972 pt->x = Max(old->x - new->x, 0);
7973 pt->y = Max(old->y - new->y, 0);
7978 /* For two overlapping frames, return the rect(s)
7979 in the old that do not intersect with the new. */
7982 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
7986 /* If old = new (shouldn't happen) then nothing to draw */
7987 if (old->x == new->x && old->y == new->y) {
7991 /* Work out what bits overlap. Since we know the rects
7992 are the same size we don't need a full intersect calc. */
7994 /* Top or bottom edge? */
7995 if (new->y > old->y) {
7996 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
7998 } else if (old->y > new->y) {
7999 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8000 size, old->y - new->y);
8003 /* Left or right edge - don't overlap any update calculated above. */
8004 if (new->x > old->x) {
8005 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8006 new->x - old->x, size - abs(new->y - old->y));
8008 } else if (old->x > new->x) {
8009 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8010 old->x - new->x, size - abs(new->y - old->y));
8017 /* Generate a series of frame coords from start->mid->finish.
8018 The movement rate doubles until the half way point is
8019 reached, then halves back down to the final destination,
8020 which gives a nice slow in/out effect. The algorithmn
8021 may seem to generate too many intermediates for short
8022 moves, but remember that the purpose is to attract the
8023 viewers attention to the piece about to be moved and
8024 then to where it ends up. Too few frames would be less
8028 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8030 int fraction, n, count;
8034 /* Slow in, stepping 1/16th, then 1/8th, ... */
8036 for (n = 0; n < factor; n++)
8038 for (n = 0; n < factor; n++) {
8039 frames[count].x = start->x + (mid->x - start->x) / fraction;
8040 frames[count].y = start->y + (mid->y - start->y) / fraction;
8042 fraction = fraction / 2;
8046 frames[count] = *mid;
8049 /* Slow out, stepping 1/2, then 1/4, ... */
8051 for (n = 0; n < factor; n++) {
8052 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8053 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8055 fraction = fraction * 2;
8060 /* Draw a piece on the screen without disturbing what's there */
8063 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8067 /* Bitmap for piece being moved. */
8068 if (appData.monoMode) {
8069 *mask = *pieceToSolid(piece);
8070 } else if (useImages) {
8072 *mask = xpmMask[piece];
8074 *mask = ximMaskPm[piece];
8077 *mask = *pieceToSolid(piece);
8080 /* GC for piece being moved. Square color doesn't matter, but
8081 since it gets modified we make a copy of the original. */
8083 if (appData.monoMode)
8088 if (appData.monoMode)
8093 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8095 /* Outline only used in mono mode and is not modified */
8097 *outline = bwPieceGC;
8099 *outline = wbPieceGC;
8103 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8108 /* Draw solid rectangle which will be clipped to shape of piece */
8109 XFillRectangle(xDisplay, dest, clip,
8110 0, 0, squareSize, squareSize);
8111 if (appData.monoMode)
8112 /* Also draw outline in contrasting color for black
8113 on black / white on white cases */
8114 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8115 0, 0, squareSize, squareSize, 0, 0, 1);
8117 /* Copy the piece */
8122 if(appData.upsideDown && flipView) kind ^= 2;
8123 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8125 0, 0, squareSize, squareSize,
8130 /* Animate the movement of a single piece */
8133 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8137 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8138 /* The old buffer is initialised with the start square (empty) */
8139 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8140 anim->prevFrame = *start;
8142 /* The piece will be drawn using its own bitmap as a matte */
8143 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8144 XSetClipMask(xDisplay, anim->pieceGC, mask);
8148 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8150 XRectangle updates[4];
8155 /* Save what we are about to draw into the new buffer */
8156 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8157 frame->x, frame->y, squareSize, squareSize,
8160 /* Erase bits of the previous frame */
8161 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8162 /* Where the new frame overlapped the previous,
8163 the contents in newBuf are wrong. */
8164 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8165 overlap.x, overlap.y,
8166 overlap.width, overlap.height,
8168 /* Repaint the areas in the old that don't overlap new */
8169 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8170 for (i = 0; i < count; i++)
8171 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8172 updates[i].x - anim->prevFrame.x,
8173 updates[i].y - anim->prevFrame.y,
8174 updates[i].width, updates[i].height,
8175 updates[i].x, updates[i].y);
8177 /* Easy when no overlap */
8178 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8179 0, 0, squareSize, squareSize,
8180 anim->prevFrame.x, anim->prevFrame.y);
8183 /* Save this frame for next time round */
8184 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8185 0, 0, squareSize, squareSize,
8187 anim->prevFrame = *frame;
8189 /* Draw piece over original screen contents, not current,
8190 and copy entire rect. Wipes out overlapping piece images. */
8191 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8192 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8193 0, 0, squareSize, squareSize,
8194 frame->x, frame->y);
8198 EndAnimation (AnimState *anim, XPoint *finish)
8200 XRectangle updates[4];
8205 /* The main code will redraw the final square, so we
8206 only need to erase the bits that don't overlap. */
8207 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8208 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8209 for (i = 0; i < count; i++)
8210 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8211 updates[i].x - anim->prevFrame.x,
8212 updates[i].y - anim->prevFrame.y,
8213 updates[i].width, updates[i].height,
8214 updates[i].x, updates[i].y);
8216 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8217 0, 0, squareSize, squareSize,
8218 anim->prevFrame.x, anim->prevFrame.y);
8223 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8227 BeginAnimation(anim, piece, startColor, start);
8228 for (n = 0; n < nFrames; n++) {
8229 AnimationFrame(anim, &(frames[n]), piece);
8230 FrameDelay(appData.animSpeed);
8232 EndAnimation(anim, finish);
8236 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8239 ChessSquare piece = board[fromY][toY];
8240 board[fromY][toY] = EmptySquare;
8241 DrawPosition(FALSE, board);
8243 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8244 y = lineGap + toY * (squareSize + lineGap);
8246 x = lineGap + toX * (squareSize + lineGap);
8247 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8249 for(i=1; i<4*kFactor; i++) {
8250 int r = squareSize * 9 * i/(20*kFactor - 5);
8251 XFillArc(xDisplay, xBoardWindow, highlineGC,
8252 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8253 FrameDelay(appData.animSpeed);
8255 board[fromY][toY] = piece;
8258 /* Main control logic for deciding what to animate and how */
8261 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8265 XPoint start, finish, mid;
8266 XPoint frames[kFactor * 2 + 1];
8267 int nFrames, startColor, endColor;
8269 /* Are we animating? */
8270 if (!appData.animate || appData.blindfold)
8273 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8274 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8275 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8277 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8278 piece = board[fromY][fromX];
8279 if (piece >= EmptySquare) return;
8284 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8287 ScreenSquare(fromX, fromY, &start, &startColor);
8288 ScreenSquare(toX, toY, &finish, &endColor);
8291 /* Knight: make straight movement then diagonal */
8292 if (abs(toY - fromY) < abs(toX - fromX)) {
8293 mid.x = start.x + (finish.x - start.x) / 2;
8297 mid.y = start.y + (finish.y - start.y) / 2;
8300 mid.x = start.x + (finish.x - start.x) / 2;
8301 mid.y = start.y + (finish.y - start.y) / 2;
8304 /* Don't use as many frames for very short moves */
8305 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8306 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8308 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8309 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8310 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8312 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8313 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8316 /* Be sure end square is redrawn */
8317 damage[0][toY][toX] = True;
8321 DragPieceBegin (int x, int y, Boolean instantly)
8323 int boardX, boardY, color;
8326 /* Are we animating? */
8327 if (!appData.animateDragging || appData.blindfold)
8330 /* Figure out which square we start in and the
8331 mouse position relative to top left corner. */
8332 BoardSquare(x, y, &boardX, &boardY);
8333 player.startBoardX = boardX;
8334 player.startBoardY = boardY;
8335 ScreenSquare(boardX, boardY, &corner, &color);
8336 player.startSquare = corner;
8337 player.startColor = color;
8338 /* As soon as we start dragging, the piece will jump slightly to
8339 be centered over the mouse pointer. */
8340 player.mouseDelta.x = squareSize/2;
8341 player.mouseDelta.y = squareSize/2;
8342 /* Initialise animation */
8343 player.dragPiece = PieceForSquare(boardX, boardY);
8345 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8346 player.dragActive = True;
8347 BeginAnimation(&player, player.dragPiece, color, &corner);
8348 /* Mark this square as needing to be redrawn. Note that
8349 we don't remove the piece though, since logically (ie
8350 as seen by opponent) the move hasn't been made yet. */
8351 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8352 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8353 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8354 corner.x, corner.y, squareSize, squareSize,
8355 0, 0); // [HGM] zh: unstack in stead of grab
8356 if(gatingPiece != EmptySquare) {
8357 /* Kludge alert: When gating we want the introduced
8358 piece to appear on the from square. To generate an
8359 image of it, we draw it on the board, copy the image,
8360 and draw the original piece again. */
8361 ChessSquare piece = boards[currentMove][boardY][boardX];
8362 DrawSquare(boardY, boardX, gatingPiece, 0);
8363 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8364 corner.x, corner.y, squareSize, squareSize, 0, 0);
8365 DrawSquare(boardY, boardX, piece, 0);
8367 damage[0][boardY][boardX] = True;
8369 player.dragActive = False;
8374 ChangeDragPiece (ChessSquare piece)
8377 player.dragPiece = piece;
8378 /* The piece will be drawn using its own bitmap as a matte */
8379 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8380 XSetClipMask(xDisplay, player.pieceGC, mask);
8384 DragPieceMove (int x, int y)
8388 /* Are we animating? */
8389 if (!appData.animateDragging || appData.blindfold)
8393 if (! player.dragActive)
8395 /* Move piece, maintaining same relative position
8396 of mouse within square */
8397 corner.x = x - player.mouseDelta.x;
8398 corner.y = y - player.mouseDelta.y;
8399 AnimationFrame(&player, &corner, player.dragPiece);
8401 if (appData.highlightDragging) {
8403 BoardSquare(x, y, &boardX, &boardY);
8404 SetHighlights(fromX, fromY, boardX, boardY);
8410 DragPieceEnd (int x, int y)
8412 int boardX, boardY, color;
8415 /* Are we animating? */
8416 if (!appData.animateDragging || appData.blindfold)
8420 if (! player.dragActive)
8422 /* Last frame in sequence is square piece is
8423 placed on, which may not match mouse exactly. */
8424 BoardSquare(x, y, &boardX, &boardY);
8425 ScreenSquare(boardX, boardY, &corner, &color);
8426 EndAnimation(&player, &corner);
8428 /* Be sure end square is redrawn */
8429 damage[0][boardY][boardX] = True;
8431 /* This prevents weird things happening with fast successive
8432 clicks which on my Sun at least can cause motion events
8433 without corresponding press/release. */
8434 player.dragActive = False;
8437 /* Handle expose event while piece being dragged */
8442 if (!player.dragActive || appData.blindfold)
8445 /* What we're doing: logically, the move hasn't been made yet,
8446 so the piece is still in it's original square. But visually
8447 it's being dragged around the board. So we erase the square
8448 that the piece is on and draw it at the last known drag point. */
8449 BlankSquare(player.startSquare.x, player.startSquare.y,
8450 player.startColor, EmptySquare, xBoardWindow, 1);
8451 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8452 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8455 #include <sys/ioctl.h>
8459 int fd, default_width;
8462 default_width = 79; // this is FICS default anyway...
8464 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8466 if (!ioctl(fd, TIOCGSIZE, &win))
8467 default_width = win.ts_cols;
8468 #elif defined(TIOCGWINSZ)
8470 if (!ioctl(fd, TIOCGWINSZ, &win))
8471 default_width = win.ws_col;
8473 return default_width;
8479 static int old_width = 0;
8480 int new_width = get_term_width();
8482 if (old_width != new_width)
8483 ics_printf("set width %d\n", new_width);
8484 old_width = new_width;
8488 NotifyFrontendLogin ()
8493 /* [AS] Arrow highlighting support */
8495 static double A_WIDTH = 5; /* Width of arrow body */
8497 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8498 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8509 return (int) (x + 0.5);
8513 SquareToPos (int rank, int file, int *x, int *y)
8516 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8517 *y = lineGap + rank * (squareSize + lineGap);
8519 *x = lineGap + file * (squareSize + lineGap);
8520 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8524 /* Draw an arrow between two points using current settings */
8526 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8529 double dx, dy, j, k, x, y;
8532 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8534 arrow[0].x = s_x + A_WIDTH + 0.5;
8537 arrow[1].x = s_x + A_WIDTH + 0.5;
8538 arrow[1].y = d_y - h;
8540 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8541 arrow[2].y = d_y - h;
8546 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8547 arrow[5].y = d_y - h;
8549 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8550 arrow[4].y = d_y - h;
8552 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8555 else if( d_y == s_y ) {
8556 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8559 arrow[0].y = s_y + A_WIDTH + 0.5;
8561 arrow[1].x = d_x - w;
8562 arrow[1].y = s_y + A_WIDTH + 0.5;
8564 arrow[2].x = d_x - w;
8565 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8570 arrow[5].x = d_x - w;
8571 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8573 arrow[4].x = d_x - w;
8574 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8577 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8580 /* [AS] Needed a lot of paper for this! :-) */
8581 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8582 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8584 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8586 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8591 arrow[0].x = Round(x - j);
8592 arrow[0].y = Round(y + j*dx);
8594 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8595 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8598 x = (double) d_x - k;
8599 y = (double) d_y - k*dy;
8602 x = (double) d_x + k;
8603 y = (double) d_y + k*dy;
8606 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8608 arrow[6].x = Round(x - j);
8609 arrow[6].y = Round(y + j*dx);
8611 arrow[2].x = Round(arrow[6].x + 2*j);
8612 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8614 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8615 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8620 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8621 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8624 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8625 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8626 // Polygon( hdc, arrow, 7 );
8630 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8633 hor = 64*s_col + 32; vert = 64*s_row + 32;
8634 for(i=0; i<= 64; i++) {
8635 damage[0][vert+6>>6][hor+6>>6] = True;
8636 damage[0][vert-6>>6][hor+6>>6] = True;
8637 damage[0][vert+6>>6][hor-6>>6] = True;
8638 damage[0][vert-6>>6][hor-6>>6] = True;
8639 hor += d_col - s_col; vert += d_row - s_row;
8643 /* [AS] Draw an arrow between two squares */
8645 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8647 int s_x, s_y, d_x, d_y;
8649 if( s_col == d_col && s_row == d_row ) {
8653 /* Get source and destination points */
8654 SquareToPos( s_row, s_col, &s_x, &s_y);
8655 SquareToPos( d_row, d_col, &d_x, &d_y);
8658 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8660 else if( d_y < s_y ) {
8661 d_y += squareSize / 2 + squareSize / 4;
8664 d_y += squareSize / 2;
8668 d_x += squareSize / 2 - squareSize / 4;
8670 else if( d_x < s_x ) {
8671 d_x += squareSize / 2 + squareSize / 4;
8674 d_x += squareSize / 2;
8677 s_x += squareSize / 2;
8678 s_y += squareSize / 2;
8681 A_WIDTH = squareSize / 14.; //[HGM] make float
8683 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8684 ArrowDamage(s_col, s_row, d_col, d_row);
8688 IsDrawArrowEnabled ()
8690 return appData.highlightMoveWithArrow && squareSize >= 32;
8694 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8696 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8697 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8701 UpdateLogos (int displ)
8703 return; // no logos in XBoard yet