2 * xboard.c -- X front end for XBoard
4 * Copyright 1991 by Digital Equipment Corporation, Maynard,
7 * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8 * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
10 * The following terms apply to Digital Equipment Corporation's copyright
12 * ------------------------------------------------------------------------
15 * Permission to use, copy, modify, and distribute this software and its
16 * documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appear in all copies and that
18 * both that copyright notice and this permission notice appear in
19 * supporting documentation, and that the name of Digital not be
20 * used in advertising or publicity pertaining to distribution of the
21 * software without specific, written prior permission.
23 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
30 * ------------------------------------------------------------------------
32 * The following terms apply to the enhanced version of XBoard
33 * distributed by the Free Software Foundation:
34 * ------------------------------------------------------------------------
36 * GNU XBoard is free software: you can redistribute it and/or modify
37 * it under the terms of the GNU General Public License as published by
38 * the Free Software Foundation, either version 3 of the License, or (at
39 * your option) any later version.
41 * GNU XBoard is distributed in the hope that it will be useful, but
42 * WITHOUT ANY WARRANTY; without even the implied warranty of
43 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44 * General Public License for more details.
46 * You should have received a copy of the GNU General Public License
47 * along with this program. If not, see http://www.gnu.org/licenses/. *
49 *------------------------------------------------------------------------
50 ** See the file ChangeLog for a revision history. */
60 #include <sys/types.h>
66 # if HAVE_SYS_SOCKET_H
67 # include <sys/socket.h>
68 # include <netinet/in.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 # if HAVE_LAN_SOCKET_H
72 # include <lan/socket.h>
74 # include <lan/netdb.h>
75 # else /* not HAVE_LAN_SOCKET_H */
76 # define OMIT_SOCKETS 1
77 # endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
88 # else /* not HAVE_STRING_H */
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
110 # include <sys/time.h>
121 # include <sys/wait.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
132 # include <sys/ndir.h>
133 # define HAVE_DIR_STRUCT
136 # include <sys/dir.h>
137 # define HAVE_DIR_STRUCT
141 # define HAVE_DIR_STRUCT
149 #include <X11/Intrinsic.h>
150 #include <X11/StringDefs.h>
151 #include <X11/Shell.h>
152 #include <X11/cursorfont.h>
153 #include <X11/Xatom.h>
154 #include <X11/Xmu/Atoms.h>
156 #include <X11/Xaw3d/Dialog.h>
157 #include <X11/Xaw3d/Form.h>
158 #include <X11/Xaw3d/List.h>
159 #include <X11/Xaw3d/Label.h>
160 #include <X11/Xaw3d/SimpleMenu.h>
161 #include <X11/Xaw3d/SmeBSB.h>
162 #include <X11/Xaw3d/SmeLine.h>
163 #include <X11/Xaw3d/Box.h>
164 #include <X11/Xaw3d/MenuButton.h>
165 #include <X11/Xaw3d/Text.h>
166 #include <X11/Xaw3d/AsciiText.h>
168 #include <X11/Xaw/Dialog.h>
169 #include <X11/Xaw/Form.h>
170 #include <X11/Xaw/List.h>
171 #include <X11/Xaw/Label.h>
172 #include <X11/Xaw/SimpleMenu.h>
173 #include <X11/Xaw/SmeBSB.h>
174 #include <X11/Xaw/SmeLine.h>
175 #include <X11/Xaw/Box.h>
176 #include <X11/Xaw/MenuButton.h>
177 #include <X11/Xaw/Text.h>
178 #include <X11/Xaw/AsciiText.h>
181 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
186 #include "pixmaps/pixmaps.h"
187 #define IMAGE_EXT "xpm"
189 #define IMAGE_EXT "xim"
190 #include "bitmaps/bitmaps.h"
193 #include "bitmaps/icon_white.bm"
194 #include "bitmaps/icon_black.bm"
195 #include "bitmaps/checkmark.bm"
197 #include "frontend.h"
199 #include "backendz.h"
203 #include "xgamelist.h"
204 #include "xhistory.h"
205 #include "xedittags.h"
208 // must be moved to xengineoutput.h
210 void EngineOutputProc P((Widget w, XEvent *event,
211 String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213 String *prms, Cardinal *nprms));
220 #define usleep(t) _sleep2(((t)+500)/1000)
224 # define _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
245 int main P((int argc, char **argv));
246 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
247 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
248 RETSIGTYPE CmailSigHandler P((int sig));
249 RETSIGTYPE IntSigHandler P((int sig));
250 RETSIGTYPE TermSizeSigHandler P((int sig));
251 void CreateGCs P((int redo));
252 void CreateAnyPieces P((void));
253 void CreateXIMPieces P((void));
254 void CreateXPMPieces P((void));
255 void CreateXPMBoard P((char *s, int n));
256 void CreatePieces P((void));
257 void CreatePieceMenus P((void));
258 Widget CreateMenuBar P((Menu *mb, int boardWidth));
259 Widget CreateButtonBar P ((MenuItem *mi));
261 char *InsertPxlSize P((char *pattern, int targetPxlSize));
262 XFontSet CreateFontSet P((char *base_fnt_lst));
264 char *FindFont P((char *pattern, int targetPxlSize));
266 void PieceMenuPopup P((Widget w, XEvent *event,
267 String *params, Cardinal *num_params));
268 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
269 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
270 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
271 u_int wreq, u_int hreq));
272 void CreateGrid P((void));
273 int EventToSquare P((int x, int limit));
274 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
275 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
276 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
277 void HandleUserMove P((Widget w, XEvent *event,
278 String *prms, Cardinal *nprms));
279 void AnimateUserMove P((Widget w, XEvent * event,
280 String * params, Cardinal * nParams));
281 void HandlePV P((Widget w, XEvent * event,
282 String * params, Cardinal * nParams));
283 void SelectPV P((Widget w, XEvent * event,
284 String * params, Cardinal * nParams));
285 void StopPV P((Widget w, XEvent * event,
286 String * params, Cardinal * nParams));
287 void WhiteClock P((Widget w, XEvent *event,
288 String *prms, Cardinal *nprms));
289 void BlackClock P((Widget w, XEvent *event,
290 String *prms, Cardinal *nprms));
291 void DrawPositionProc P((Widget w, XEvent *event,
292 String *prms, Cardinal *nprms));
293 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
295 void CommentClick P((Widget w, XEvent * event,
296 String * params, Cardinal * nParams));
297 void CommentPopUp P((char *title, char *label));
298 void CommentPopDown P((void));
299 void ICSInputBoxPopUp P((void));
300 void ICSInputBoxPopDown P((void));
301 void FileNamePopUp P((char *label, char *def, char *filter,
302 FileProc proc, char *openMode));
303 void FileNamePopDown P((void));
304 void FileNameCallback P((Widget w, XtPointer client_data,
305 XtPointer call_data));
306 void FileNameAction P((Widget w, XEvent *event,
307 String *prms, Cardinal *nprms));
308 void AskQuestionReplyAction P((Widget w, XEvent *event,
309 String *prms, Cardinal *nprms));
310 void AskQuestionProc P((Widget w, XEvent *event,
311 String *prms, Cardinal *nprms));
312 void AskQuestionPopDown P((void));
313 void PromotionPopDown P((void));
314 void PromotionCallback P((Widget w, XtPointer client_data,
315 XtPointer call_data));
316 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
317 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
318 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
321 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
323 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
325 void LoadPositionProc P((Widget w, XEvent *event,
326 String *prms, Cardinal *nprms));
327 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
329 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
331 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
333 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
335 void PastePositionProc P((Widget w, XEvent *event, String *prms,
337 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
340 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
341 void SavePositionProc P((Widget w, XEvent *event,
342 String *prms, Cardinal *nprms));
343 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
344 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
346 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
347 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
350 void MachineWhiteProc P((Widget w, XEvent *event,
351 String *prms, Cardinal *nprms));
352 void AnalyzeModeProc P((Widget w, XEvent *event,
353 String *prms, Cardinal *nprms));
354 void AnalyzeFileProc P((Widget w, XEvent *event,
355 String *prms, Cardinal *nprms));
356 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
358 void MatchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void MatchOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void IcsClientProc P((Widget w, XEvent *event, String *prms,
362 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void EditPositionProc P((Widget w, XEvent *event,
364 String *prms, Cardinal *nprms));
365 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void EditCommentProc P((Widget w, XEvent *event,
367 String *prms, Cardinal *nprms));
368 void IcsInputBoxProc P((Widget w, XEvent *event,
369 String *prms, Cardinal *nprms));
370 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void StopObservingProc P((Widget w, XEvent *event, String *prms,
387 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
389 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 Boolean TempBackwardActive = False;
395 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
396 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
401 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
403 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
404 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
406 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
408 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
410 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
412 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
413 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
415 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
416 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
418 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
420 void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
422 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 //void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
424 void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
425 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
427 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
429 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
431 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
433 //void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
436 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
438 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
440 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
442 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
450 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
451 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
452 void DisplayMove P((int moveNumber));
453 void DisplayTitle P((char *title));
454 void ICSInitScript P((void));
455 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
456 void ErrorPopUp P((char *title, char *text, int modal));
457 void ErrorPopDown P((void));
458 static char *ExpandPathName P((char *path));
459 static void CreateAnimVars P((void));
460 static void DragPieceMove P((int x, int y));
461 static void DrawDragPiece P((void));
462 char *ModeToWidgetName P((GameMode mode));
463 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
469 void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
470 void LoadEngineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
471 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
472 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
473 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
474 void IcsOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
475 void SoundOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
476 void BoardOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
477 void LoadOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
478 void SaveOptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
479 void EditBookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
480 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
481 void GameListOptionsPopDown P(());
482 void GenericPopDown P(());
483 void update_ics_width P(());
484 int get_term_width P(());
485 int CopyMemoProc P(());
486 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
487 Boolean IsDrawArrowEnabled P(());
490 * XBoard depends on Xt R4 or higher
492 int xtVersion = XtSpecificationRelease;
497 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
498 jailSquareColor, highlightSquareColor, premoveHighlightColor;
499 Pixel lowTimeWarningColor;
500 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
501 bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
502 wjPieceGC, bjPieceGC, prelineGC, countGC;
503 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
504 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
505 whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
506 commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
507 menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
508 ICSInputShell, fileNameShell, askQuestionShell;
509 Widget historyShell, evalGraphShell, gameListShell;
510 int hOffset; // [HGM] dual
511 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
512 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
513 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
515 XFontSet fontSet, clockFontSet;
518 XFontStruct *clockFontStruct;
520 Font coordFontID, countFontID;
521 XFontStruct *coordFontStruct, *countFontStruct;
522 XtAppContext appContext;
524 char *oldICSInteractionTitle;
528 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
530 Position commentX = -1, commentY = -1;
531 Dimension commentW, commentH;
532 typedef unsigned int BoardSize;
534 Boolean chessProgram;
536 int minX, minY; // [HGM] placement: volatile limits on upper-left corner
537 int squareSize, smallLayout = 0, tinyLayout = 0,
538 marginW, marginH, // [HGM] for run-time resizing
539 fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
540 ICSInputBoxUp = False, askQuestionUp = False,
541 filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
542 errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
543 Dimension textHeight;
544 Pixel timerForegroundPixel, timerBackgroundPixel;
545 Pixel buttonForegroundPixel, buttonBackgroundPixel;
546 char *chessDir, *programName, *programVersion,
547 *gameCopyFilename, *gamePasteFilename;
548 Boolean alwaysOnTop = False;
549 Boolean saveSettingsOnExit;
550 char *settingsFileName;
551 char *icsTextMenuString;
553 char *firstChessProgramNames;
554 char *secondChessProgramNames;
556 WindowPlacement wpMain;
557 WindowPlacement wpConsole;
558 WindowPlacement wpComment;
559 WindowPlacement wpMoveHistory;
560 WindowPlacement wpEvalGraph;
561 WindowPlacement wpEngineOutput;
562 WindowPlacement wpGameList;
563 WindowPlacement wpTags;
565 extern Widget shells[];
566 extern Boolean shellUp[];
570 Pixmap pieceBitmap[2][(int)BlackPawn];
571 Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */
572 Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/
573 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */
574 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
575 Pixmap xpmBoardBitmap[2];
576 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
577 XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */
578 Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */
579 Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */
580 XImage *ximLightSquare, *ximDarkSquare;
583 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
584 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
586 #define White(piece) ((int)(piece) < (int)BlackPawn)
588 /* Variables for doing smooth animation. This whole thing
589 would be much easier if the board was double-buffered,
590 but that would require a fairly major rewrite. */
595 GC blitGC, pieceGC, outlineGC;
596 XPoint startSquare, prevFrame, mouseDelta;
600 int startBoardX, startBoardY;
603 /* There can be two pieces being animated at once: a player
604 can begin dragging a piece before the remote opponent has moved. */
606 static AnimState game, player;
608 /* Bitmaps for use as masks when drawing XPM pieces.
609 Need one for each black and white piece. */
610 static Pixmap xpmMask[BlackKing + 1];
612 /* This magic number is the number of intermediate frames used
613 in each half of the animation. For short moves it's reduced
614 by 1. The total number of frames will be factor * 2 + 1. */
617 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
619 MenuItem fileMenu[] = {
620 {N_("New Game Ctrl+N"), "New Game", ResetProc},
621 {N_("New Shuffle Game ..."), "New Shuffle Game", ShuffleMenuProc},
622 {N_("New Variant ... Alt+Shift+V"), "New Variant", NewVariantProc}, // [HGM] variant: not functional yet
623 {"----", NULL, NothingProc},
624 {N_("Load Game Ctrl+O"), "Load Game", LoadGameProc},
625 {N_("Load Position Ctrl+Shift+O"), "Load Position", LoadPositionProc},
626 // {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
627 // {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
628 // {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
629 {N_("Next Position Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
630 {N_("Prev Position Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
631 {"----", NULL, NothingProc},
632 // {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
633 {N_("Save Game Ctrl+S"), "Save Game", SaveGameProc},
634 {N_("Save Position Ctrl+Shift+S"), "Save Position", SavePositionProc},
635 {"----", NULL, NothingProc},
636 {N_("Mail Move"), "Mail Move", MailMoveProc},
637 {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
638 {"----", NULL, NothingProc},
639 {N_("Quit Ctr+Q"), "Exit", QuitProc},
643 MenuItem editMenu[] = {
644 {N_("Copy Game Ctrl+C"), "Copy Game", CopyGameProc},
645 {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
646 {N_("Copy Game List"), "Copy Game List", CopyGameListProc},
647 {"----", NULL, NothingProc},
648 {N_("Paste Game Ctrl+V"), "Paste Game", PasteGameProc},
649 {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
650 {"----", NULL, NothingProc},
651 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
652 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
653 {N_("Edit Tags"), "Edit Tags", EditTagsProc},
654 {N_("Edit Comment"), "Edit Comment", EditCommentProc},
655 {N_("Edit Book"), "Edit Book", EditBookProc},
656 {"----", NULL, NothingProc},
657 {N_("Revert Home"), "Revert", RevertProc},
658 {N_("Annotate"), "Annotate", AnnotateProc},
659 {N_("Truncate Game End"), "Truncate Game", TruncateGameProc},
660 {"----", NULL, NothingProc},
661 {N_("Backward Alt+Left"), "Backward", BackwardProc},
662 {N_("Forward Alt+Right"), "Forward", ForwardProc},
663 {N_("Back to Start Alt+Home"), "Back to Start", ToStartProc},
664 {N_("Forward to End Alt+End"), "Forward to End", ToEndProc},
668 MenuItem viewMenu[] = {
669 {N_("Flip View F2"), "Flip View", FlipViewProc},
670 {"----", NULL, NothingProc},
671 {N_("Engine Output Alt+Shift+O"), "Show Engine Output", EngineOutputProc},
672 {N_("Move History Alt+Shift+H"), "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
673 {N_("Evaluation Graph Alt+Shift+E"), "Show Evaluation Graph", EvalGraphProc},
674 {N_("Game List Alt+Shift+G"), "Show Game List", ShowGameListProc},
675 {N_("ICS text menu"), "ICStex", IcsTextProc},
676 {"----", NULL, NothingProc},
677 {N_("Tags"), "Show Tags", EditTagsProc},
678 {N_("Comments"), "Show Comments", EditCommentProc},
679 {N_("ICS Input Box"), "ICS Input Box", IcsInputBoxProc},
680 {"----", NULL, NothingProc},
681 {N_("Board..."), "Board Options", BoardOptionsProc},
682 {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
686 MenuItem modeMenu[] = {
687 {N_("Machine White Ctrl+W"), "Machine White", MachineWhiteProc},
688 {N_("Machine Black Ctrl+B"), "Machine Black", MachineBlackProc},
689 {N_("Two Machines Ctrl+T"), "Two Machines", TwoMachinesProc},
690 {N_("Analysis Mode Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
691 {N_("Analyze Game Ctrl+G"), "Analyze File", AnalyzeFileProc },
692 {N_("Edit Game Ctrl+E"), "Edit Game", EditGameProc},
693 {N_("Edit Position Ctrl+Shift+E"), "Edit Position", EditPositionProc},
694 {N_("Training"), "Training", TrainingProc},
695 {N_("ICS Client"), "ICS Client", IcsClientProc},
696 {"----", NULL, NothingProc},
697 {N_("Machine Match"), "Machine Match", MatchProc},
698 {N_("Pause Pause"), "Pause", PauseProc},
702 MenuItem actionMenu[] = {
703 {N_("Accept F3"), "Accept", AcceptProc},
704 {N_("Decline F4"), "Decline", DeclineProc},
705 {N_("Rematch F12"), "Rematch", RematchProc},
706 {"----", NULL, NothingProc},
707 {N_("Call Flag F5"), "Call Flag", CallFlagProc},
708 {N_("Draw F6"), "Draw", DrawProc},
709 {N_("Adjourn F7"), "Adjourn", AdjournProc},
710 {N_("Abort F8"),"Abort", AbortProc},
711 {N_("Resign F9"), "Resign", ResignProc},
712 {"----", NULL, NothingProc},
713 {N_("Stop Observing F10"), "Stop Observing", StopObservingProc},
714 {N_("Stop Examining F11"), "Stop Examining", StopExaminingProc},
715 {N_("Upload to Examine"), "Upload to Examine", UploadProc},
716 {"----", NULL, NothingProc},
717 {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
718 {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
719 {N_("Adjudicate Draw"), "Adjudicate Draw", AdjuDrawProc},
723 MenuItem engineMenu[] = {
724 {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
725 {"----", NULL, NothingProc},
726 {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
727 {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
728 {"----", NULL, NothingProc},
729 {N_("Hint"), "Hint", HintProc},
730 {N_("Book"), "Book", BookProc},
731 {"----", NULL, NothingProc},
732 {N_("Move Now Ctrl+M"), "Move Now", MoveNowProc},
733 {N_("Retract Move Ctrl+X"), "Retract Move", RetractMoveProc},
737 MenuItem optionsMenu[] = {
738 #define OPTIONSDIALOG
740 {N_("General ..."), "General", OptionsProc},
742 {N_("Time Control ... Alt+Shift+T"), "Time Control", TimeControlProc},
743 {N_("Common Engine ... Alt+Shift+U"), "Common Engine", UciMenuProc},
744 {N_("Adjudications ... Alt+Shift+J"), "Adjudications", EngineMenuProc},
745 {N_("ICS ..."), "ICS", IcsOptionsProc},
746 {N_("Match ..."), "Match", MatchOptionsProc},
747 {N_("Load Game ..."), "Load Game", LoadOptionsProc},
748 {N_("Save Game ..."), "Save Game", SaveOptionsProc},
749 // {N_(" ..."), "", OptionsProc},
750 {N_("Game List ..."), "Game List", GameListOptionsPopUp},
751 {N_("Sounds ..."), "Sounds", SoundOptionsProc},
752 {"----", NULL, NothingProc},
753 #ifndef OPTIONSDIALOG
754 {N_("Always Queen Ctrl+Shift+Q"), "Always Queen", AlwaysQueenProc},
755 {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
756 {N_("Animate Moving Ctrl+Shift+A"), "Animate Moving", AnimateMovingProc},
757 {N_("Auto Flag Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
758 {N_("Auto Flip View"), "Auto Flip View", AutoflipProc},
759 {N_("Blindfold"), "Blindfold", BlindfoldProc},
760 {N_("Flash Moves"), "Flash Moves", FlashMovesProc},
762 {N_("Highlight Dragging"), "Highlight Dragging", HighlightDraggingProc},
764 {N_("Highlight Last Move"), "Highlight Last Move", HighlightLastMoveProc},
765 {N_("Highlight With Arrow"), "Arrow", HighlightArrowProc},
766 {N_("Move Sound"), "Move Sound", MoveSoundProc},
767 // {N_("ICS Alarm"), "ICS Alarm", IcsAlarmProc},
768 {N_("One-Click Moving"), "OneClick", OneClickProc},
769 {N_("Periodic Updates"), "Periodic Updates", PeriodicUpdatesProc},
770 {N_("Ponder Next Move Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
771 {N_("Popup Exit Message"), "Popup Exit Message", PopupExitMessageProc},
772 {N_("Popup Move Errors"), "Popup Move Errors", PopupMoveErrorsProc},
773 // {N_("Premove"), "Premove", PremoveProc},
774 {N_("Show Coords"), "Show Coords", ShowCoordsProc},
775 {N_("Hide Thinking Ctrl+Shift+H"), "Hide Thinking", HideThinkingProc},
776 {N_("Test Legality Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
777 {"----", NULL, NothingProc},
779 {N_("Save Settings Now"), "Save Settings Now", SaveSettingsProc},
780 {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
784 MenuItem helpMenu[] = {
785 {N_("Info XBoard"), "Info XBoard", InfoProc},
786 {N_("Man XBoard F1"), "Man XBoard", ManProc},
787 {"----", NULL, NothingProc},
788 {N_("About XBoard"), "About XBoard", AboutProc},
793 {N_("File"), "File", fileMenu},
794 {N_("Edit"), "Edit", editMenu},
795 {N_("View"), "View", viewMenu},
796 {N_("Mode"), "Mode", modeMenu},
797 {N_("Action"), "Action", actionMenu},
798 {N_("Engine"), "Engine", engineMenu},
799 {N_("Options"), "Options", optionsMenu},
800 {N_("Help"), "Help", helpMenu},
804 #define PAUSE_BUTTON "P"
805 MenuItem buttonBar[] = {
806 {"<<", "<<", ToStartProc},
807 {"<", "<", BackwardProc},
808 {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseProc},
809 {">", ">", ForwardProc},
810 {">>", ">>", ToEndProc},
814 #define PIECE_MENU_SIZE 18
815 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
816 { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
817 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
818 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
819 N_("Empty square"), N_("Clear board") },
820 { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
821 N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
822 N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
823 N_("Empty square"), N_("Clear board") }
825 /* must be in same order as pieceMenuStrings! */
826 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
827 { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
828 WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
829 WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
830 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
831 { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
832 BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
833 BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
834 PromotePiece, DemotePiece, EmptySquare, ClearBoard },
837 #define DROP_MENU_SIZE 6
838 String dropMenuStrings[DROP_MENU_SIZE] = {
839 "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
841 /* must be in same order as dropMenuStrings! */
842 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
843 (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
844 WhiteRook, WhiteQueen
852 DropMenuEnables dmEnables[] = {
870 { XtNborderWidth, 0 },
871 { XtNdefaultDistance, 0 },
875 { XtNborderWidth, 0 },
876 { XtNresizable, (XtArgVal) True },
880 { XtNborderWidth, 0 },
886 { XtNjustify, (XtArgVal) XtJustifyRight },
887 { XtNlabel, (XtArgVal) "..." },
888 { XtNresizable, (XtArgVal) True },
889 { XtNresize, (XtArgVal) False }
892 Arg messageArgs[] = {
893 { XtNjustify, (XtArgVal) XtJustifyLeft },
894 { XtNlabel, (XtArgVal) "..." },
895 { XtNresizable, (XtArgVal) True },
896 { XtNresize, (XtArgVal) False }
900 { XtNborderWidth, 0 },
901 { XtNjustify, (XtArgVal) XtJustifyLeft }
904 XtResource clientResources[] = {
905 { "flashCount", "flashCount", XtRInt, sizeof(int),
906 XtOffset(AppDataPtr, flashCount), XtRImmediate,
907 (XtPointer) FLASH_COUNT },
910 XrmOptionDescRec shellOptions[] = {
911 { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
912 { "-flash", "flashCount", XrmoptionNoArg, "3" },
913 { "-xflash", "flashCount", XrmoptionNoArg, "0" },
916 XtActionsRec boardActions[] = {
917 { "DrawPosition", DrawPositionProc },
918 { "HandleUserMove", HandleUserMove },
919 { "AnimateUserMove", AnimateUserMove },
920 { "HandlePV", HandlePV },
921 { "SelectPV", SelectPV },
922 { "StopPV", StopPV },
923 { "FileNameAction", FileNameAction },
924 { "AskQuestionProc", AskQuestionProc },
925 { "AskQuestionReplyAction", AskQuestionReplyAction },
926 { "PieceMenuPopup", PieceMenuPopup },
927 { "WhiteClock", WhiteClock },
928 { "BlackClock", BlackClock },
929 { "ResetProc", ResetProc },
930 { "NewVariantProc", NewVariantProc },
931 { "LoadGameProc", LoadGameProc },
932 { "LoadNextGameProc", LoadNextGameProc },
933 { "LoadPrevGameProc", LoadPrevGameProc },
934 { "LoadSelectedProc", LoadSelectedProc },
935 { "SetFilterProc", SetFilterProc },
936 { "ReloadGameProc", ReloadGameProc },
937 { "LoadPositionProc", LoadPositionProc },
938 { "LoadNextPositionProc", LoadNextPositionProc },
939 { "LoadPrevPositionProc", LoadPrevPositionProc },
940 { "ReloadPositionProc", ReloadPositionProc },
941 { "CopyPositionProc", CopyPositionProc },
942 { "PastePositionProc", PastePositionProc },
943 { "CopyGameProc", CopyGameProc },
944 { "CopyGameListProc", CopyGameListProc },
945 { "PasteGameProc", PasteGameProc },
946 { "SaveGameProc", SaveGameProc },
947 { "SavePositionProc", SavePositionProc },
948 { "MailMoveProc", MailMoveProc },
949 { "ReloadCmailMsgProc", ReloadCmailMsgProc },
950 { "QuitProc", QuitProc },
951 { "MachineWhiteProc", MachineWhiteProc },
952 { "MachineBlackProc", MachineBlackProc },
953 { "AnalysisModeProc", AnalyzeModeProc },
954 { "AnalyzeFileProc", AnalyzeFileProc },
955 { "TwoMachinesProc", TwoMachinesProc },
956 { "IcsClientProc", IcsClientProc },
957 { "EditGameProc", EditGameProc },
958 { "EditPositionProc", EditPositionProc },
959 { "TrainingProc", EditPositionProc },
960 { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
961 { "EvalGraphProc", EvalGraphProc}, // [HGM] Winboard_x avaluation graph window
962 { "ShowGameListProc", ShowGameListProc },
963 { "ShowMoveListProc", HistoryShowProc},
964 { "EditTagsProc", EditTagsProc },
965 { "EditBookProc", EditBookProc },
966 { "EditCommentProc", EditCommentProc },
967 { "IcsInputBoxProc", IcsInputBoxProc },
968 { "PauseProc", PauseProc },
969 { "AcceptProc", AcceptProc },
970 { "DeclineProc", DeclineProc },
971 { "RematchProc", RematchProc },
972 { "CallFlagProc", CallFlagProc },
973 { "DrawProc", DrawProc },
974 { "AdjournProc", AdjournProc },
975 { "AbortProc", AbortProc },
976 { "ResignProc", ResignProc },
977 { "AdjuWhiteProc", AdjuWhiteProc },
978 { "AdjuBlackProc", AdjuBlackProc },
979 { "AdjuDrawProc", AdjuDrawProc },
980 { "TypeInProc", TypeInProc },
981 { "EnterKeyProc", EnterKeyProc },
982 { "UpKeyProc", UpKeyProc },
983 { "DownKeyProc", DownKeyProc },
984 { "StopObservingProc", StopObservingProc },
985 { "StopExaminingProc", StopExaminingProc },
986 { "UploadProc", UploadProc },
987 { "BackwardProc", BackwardProc },
988 { "ForwardProc", ForwardProc },
989 { "TempBackwardProc", TempBackwardProc },
990 { "TempForwardProc", TempForwardProc },
991 { "ToStartProc", ToStartProc },
992 { "ToEndProc", ToEndProc },
993 { "RevertProc", RevertProc },
994 { "AnnotateProc", AnnotateProc },
995 { "TruncateGameProc", TruncateGameProc },
996 { "MoveNowProc", MoveNowProc },
997 { "RetractMoveProc", RetractMoveProc },
998 { "EngineMenuProc", (XtActionProc) EngineMenuProc },
999 { "UciMenuProc", (XtActionProc) UciMenuProc },
1000 { "TimeControlProc", (XtActionProc) TimeControlProc },
1001 { "FlipViewProc", FlipViewProc },
1002 { "PonderNextMoveProc", PonderNextMoveProc },
1003 #ifndef OPTIONSDIALOG
1004 { "AlwaysQueenProc", AlwaysQueenProc },
1005 { "AnimateDraggingProc", AnimateDraggingProc },
1006 { "AnimateMovingProc", AnimateMovingProc },
1007 { "AutoflagProc", AutoflagProc },
1008 { "AutoflipProc", AutoflipProc },
1009 { "BlindfoldProc", BlindfoldProc },
1010 { "FlashMovesProc", FlashMovesProc },
1012 { "HighlightDraggingProc", HighlightDraggingProc },
1014 { "HighlightLastMoveProc", HighlightLastMoveProc },
1015 // { "IcsAlarmProc", IcsAlarmProc },
1016 { "MoveSoundProc", MoveSoundProc },
1017 { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1018 { "PopupExitMessageProc", PopupExitMessageProc },
1019 { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1020 // { "PremoveProc", PremoveProc },
1021 { "ShowCoordsProc", ShowCoordsProc },
1022 { "ShowThinkingProc", ShowThinkingProc },
1023 { "HideThinkingProc", HideThinkingProc },
1024 { "TestLegalityProc", TestLegalityProc },
1026 { "SaveSettingsProc", SaveSettingsProc },
1027 { "SaveOnExitProc", SaveOnExitProc },
1028 { "InfoProc", InfoProc },
1029 { "ManProc", ManProc },
1030 { "HintProc", HintProc },
1031 { "BookProc", BookProc },
1032 { "AboutGameProc", AboutGameProc },
1033 { "AboutProc", AboutProc },
1034 { "DebugProc", DebugProc },
1035 { "NothingProc", NothingProc },
1036 { "CommentClick", (XtActionProc) CommentClick },
1037 { "CommentPopDown", (XtActionProc) CommentPopDown },
1038 { "TagsPopDown", (XtActionProc) TagsPopDown },
1039 { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1040 { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1041 { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1042 { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1043 { "GameListPopDown", (XtActionProc) GameListPopDown },
1044 { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1045 { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1046 { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1047 { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1048 { "GenericPopDown", (XtActionProc) GenericPopDown },
1049 { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1050 { "SelectMove", (XtActionProc) SelectMove },
1053 char globalTranslations[] =
1054 ":<Key>F9: ResignProc() \n \
1055 :Ctrl<Key>n: ResetProc() \n \
1056 :Meta<Key>V: NewVariantProc() \n \
1057 :Ctrl<Key>o: LoadGameProc() \n \
1058 :Meta<Key>Next: LoadNextGameProc() \n \
1059 :Meta<Key>Prior: LoadPrevGameProc() \n \
1060 :Ctrl<Key>Down: LoadSelectedProc(3) \n \
1061 :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
1062 :Ctrl<Key>s: SaveGameProc() \n \
1063 :Ctrl<Key>c: CopyGameProc() \n \
1064 :Ctrl<Key>v: PasteGameProc() \n \
1065 :Ctrl<Key>O: LoadPositionProc() \n \
1066 :Shift<Key>Next: LoadNextPositionProc() \n \
1067 :Shift<Key>Prior: LoadPrevPositionProc() \n \
1068 :Ctrl<Key>S: SavePositionProc() \n \
1069 :Ctrl<Key>C: CopyPositionProc() \n \
1070 :Ctrl<Key>V: PastePositionProc() \n \
1071 :Ctrl<Key>q: QuitProc() \n \
1072 :Ctrl<Key>w: MachineWhiteProc() \n \
1073 :Ctrl<Key>b: MachineBlackProc() \n \
1074 :Ctrl<Key>t: TwoMachinesProc() \n \
1075 :Ctrl<Key>a: AnalysisModeProc() \n \
1076 :Ctrl<Key>g: AnalyzeFileProc() \n \
1077 :Ctrl<Key>e: EditGameProc() \n \
1078 :Ctrl<Key>E: EditPositionProc() \n \
1079 :Meta<Key>O: EngineOutputProc() \n \
1080 :Meta<Key>E: EvalGraphProc() \n \
1081 :Meta<Key>G: ShowGameListProc() \n \
1082 :Meta<Key>H: ShowMoveListProc() \n \
1083 :<Key>Pause: PauseProc() \n \
1084 :<Key>F3: AcceptProc() \n \
1085 :<Key>F4: DeclineProc() \n \
1086 :<Key>F12: RematchProc() \n \
1087 :<Key>F5: CallFlagProc() \n \
1088 :<Key>F6: DrawProc() \n \
1089 :<Key>F7: AdjournProc() \n \
1090 :<Key>F8: AbortProc() \n \
1091 :<Key>F10: StopObservingProc() \n \
1092 :<Key>F11: StopExaminingProc() \n \
1093 :Meta Ctrl<Key>F12: DebugProc() \n \
1094 :Meta<Key>End: ToEndProc() \n \
1095 :Meta<Key>Right: ForwardProc() \n \
1096 :Meta<Key>Home: ToStartProc() \n \
1097 :Meta<Key>Left: BackwardProc() \n \
1098 :<Key>Left: BackwardProc() \n \
1099 :<Key>Right: ForwardProc() \n \
1100 :<Key>Home: RevertProc() \n \
1101 :<Key>End: TruncateGameProc() \n \
1102 :Ctrl<Key>m: MoveNowProc() \n \
1103 :Ctrl<Key>x: RetractMoveProc() \n \
1104 :Meta<Key>J: EngineMenuProc() \n \
1105 :Meta<Key>U: UciMenuProc() \n \
1106 :Meta<Key>T: TimeControlProc() \n \
1107 :Ctrl<Key>P: PonderNextMoveProc() \n "
1108 #ifndef OPTIONSDIALOG
1110 :Ctrl<Key>Q: AlwaysQueenProc() \n \
1111 :Ctrl<Key>F: AutoflagProc() \n \
1112 :Ctrl<Key>A: AnimateMovingProc() \n \
1113 :Ctrl<Key>L: TestLegalityProc() \n \
1114 :Ctrl<Key>H: HideThinkingProc() \n "
1117 :<Key>F1: ManProc() \n \
1118 :<Key>F2: FlipViewProc() \n \
1119 :<KeyDown>Return: TempBackwardProc() \n \
1120 :<KeyUp>Return: TempForwardProc() \n";
1122 char boardTranslations[] =
1123 "<Btn1Down>: HandleUserMove(0) \n \
1124 Shift<Btn1Up>: HandleUserMove(1) \n \
1125 <Btn1Up>: HandleUserMove(0) \n \
1126 <Btn1Motion>: AnimateUserMove() \n \
1127 <Btn3Motion>: HandlePV() \n \
1128 <Btn2Motion>: HandlePV() \n \
1129 <Btn3Up>: PieceMenuPopup(menuB) \n \
1130 <Btn2Up>: PieceMenuPopup(menuB) \n \
1131 Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1132 PieceMenuPopup(menuB) \n \
1133 Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1134 PieceMenuPopup(menuW) \n \
1135 Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1136 PieceMenuPopup(menuW) \n \
1137 Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1138 PieceMenuPopup(menuB) \n";
1140 char whiteTranslations[] =
1141 "Shift<BtnDown>: WhiteClock(1)\n \
1142 <BtnDown>: WhiteClock(0)\n";
1143 char blackTranslations[] =
1144 "Shift<BtnDown>: BlackClock(1)\n \
1145 <BtnDown>: BlackClock(0)\n";
1147 char ICSInputTranslations[] =
1148 "<Key>Up: UpKeyProc() \n "
1149 "<Key>Down: DownKeyProc() \n "
1150 "<Key>Return: EnterKeyProc() \n";
1152 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1153 // as the widget is destroyed before the up-click can call extend-end
1154 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1156 String xboardResources[] = {
1157 "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1158 "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1159 "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1164 /* Max possible square size */
1165 #define MAXSQSIZE 256
1167 static int xpm_avail[MAXSQSIZE];
1169 #ifdef HAVE_DIR_STRUCT
1171 /* Extract piece size from filename */
1173 xpm_getsize (char *name, int len, char *ext)
1181 if ((p=strchr(name, '.')) == NULL ||
1182 StrCaseCmp(p+1, ext) != 0)
1188 while (*p && isdigit(*p))
1195 /* Setup xpm_avail */
1197 xpm_getavail (char *dirname, char *ext)
1203 for (i=0; i<MAXSQSIZE; ++i)
1206 if (appData.debugMode)
1207 fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1209 dir = opendir(dirname);
1212 fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1213 programName, dirname);
1217 while ((ent=readdir(dir)) != NULL) {
1218 i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1219 if (i > 0 && i < MAXSQSIZE)
1229 xpm_print_avail (FILE *fp, char *ext)
1233 fprintf(fp, _("Available `%s' sizes:\n"), ext);
1234 for (i=1; i<MAXSQSIZE; ++i) {
1240 /* Return XPM piecesize closest to size */
1242 xpm_closest_to (char *dirname, int size, char *ext)
1245 int sm_diff = MAXSQSIZE;
1249 xpm_getavail(dirname, ext);
1251 if (appData.debugMode)
1252 xpm_print_avail(stderr, ext);
1254 for (i=1; i<MAXSQSIZE; ++i) {
1257 diff = (diff<0) ? -diff : diff;
1258 if (diff < sm_diff) {
1266 fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1272 #else /* !HAVE_DIR_STRUCT */
1273 /* If we are on a system without a DIR struct, we can't
1274 read the directory, so we can't collect a list of
1275 filenames, etc., so we can't do any size-fitting. */
1277 xpm_closest_to (char *dirname, int size, char *ext)
1279 fprintf(stderr, _("\
1280 Warning: No DIR structure found on this system --\n\
1281 Unable to autosize for XPM/XIM pieces.\n\
1282 Please report this error to %s.\n\
1283 Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1286 #endif /* HAVE_DIR_STRUCT */
1288 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1289 "magenta", "cyan", "white" };
1293 TextColors textColors[(int)NColorClasses];
1295 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1297 parse_color (char *str, int which)
1299 char *p, buf[100], *d;
1302 if (strlen(str) > 99) /* watch bounds on buf */
1307 for (i=0; i<which; ++i) {
1314 /* Could be looking at something like:
1316 .. in which case we want to stop on a comma also */
1317 while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1321 return -1; /* Use default for empty field */
1324 if (which == 2 || isdigit(*p))
1327 while (*p && isalpha(*p))
1332 for (i=0; i<8; ++i) {
1333 if (!StrCaseCmp(buf, cnames[i]))
1334 return which? (i+40) : (i+30);
1336 if (!StrCaseCmp(buf, "default")) return -1;
1338 fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1343 parse_cpair (ColorClass cc, char *str)
1345 if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1346 fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1351 /* bg and attr are optional */
1352 textColors[(int)cc].bg = parse_color(str, 1);
1353 if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1354 textColors[(int)cc].attr = 0;
1360 /* Arrange to catch delete-window events */
1361 Atom wm_delete_window;
1363 CatchDeleteWindow (Widget w, String procname)
1366 XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1367 snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1368 XtAugmentTranslations(w, XtParseTranslationTable(buf));
1375 XtSetArg(args[0], XtNiconic, False);
1376 XtSetValues(shellWidget, args, 1);
1378 XtPopup(shellWidget, XtGrabNone); /* Raise if lowered */
1381 //---------------------------------------------------------------------------------------------------------
1382 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1385 #define CW_USEDEFAULT (1<<31)
1386 #define ICS_TEXT_MENU_SIZE 90
1387 #define DEBUG_FILE "xboard.debug"
1388 #define SetCurrentDirectory chdir
1389 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1393 // these two must some day move to frontend.h, when they are implemented
1394 Boolean GameListIsUp();
1396 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1399 // front-end part of option handling
1401 // [HGM] This platform-dependent table provides the location for storing the color info
1402 extern char *crWhite, * crBlack;
1406 &appData.whitePieceColor,
1407 &appData.blackPieceColor,
1408 &appData.lightSquareColor,
1409 &appData.darkSquareColor,
1410 &appData.highlightSquareColor,
1411 &appData.premoveHighlightColor,
1412 &appData.lowTimeWarningColor,
1423 // [HGM] font: keep a font for each square size, even non-stndard ones
1424 #define NUM_SIZES 18
1425 #define MAX_SIZE 130
1426 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1427 char *fontTable[NUM_FONTS][MAX_SIZE];
1430 ParseFont (char *name, int number)
1431 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1433 if(sscanf(name, "size%d:", &size)) {
1434 // [HGM] font: font is meant for specific boardSize (likely from settings file);
1435 // defer processing it until we know if it matches our board size
1436 if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1437 fontTable[number][size] = strdup(strchr(name, ':')+1);
1438 fontValid[number][size] = True;
1443 case 0: // CLOCK_FONT
1444 appData.clockFont = strdup(name);
1446 case 1: // MESSAGE_FONT
1447 appData.font = strdup(name);
1449 case 2: // COORD_FONT
1450 appData.coordFont = strdup(name);
1455 fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1460 { // only 2 fonts currently
1461 appData.clockFont = CLOCK_FONT_NAME;
1462 appData.coordFont = COORD_FONT_NAME;
1463 appData.font = DEFAULT_FONT_NAME;
1468 { // no-op, until we identify the code for this already in XBoard and move it here
1472 ParseColor (int n, char *name)
1473 { // in XBoard, just copy the color-name string
1474 if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1478 ParseTextAttribs (ColorClass cc, char *s)
1480 (&appData.colorShout)[cc] = strdup(s);
1484 ParseBoardSize (void *addr, char *name)
1486 appData.boardSize = strdup(name);
1491 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1495 SetCommPortDefaults ()
1496 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1499 // [HGM] args: these three cases taken out to stay in front-end
1501 SaveFontArg (FILE *f, ArgDescriptor *ad)
1504 int i, n = (int)(intptr_t)ad->argLoc;
1506 case 0: // CLOCK_FONT
1507 name = appData.clockFont;
1509 case 1: // MESSAGE_FONT
1510 name = appData.font;
1512 case 2: // COORD_FONT
1513 name = appData.coordFont;
1518 for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1519 if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1520 fontTable[n][squareSize] = strdup(name);
1521 fontValid[n][squareSize] = True;
1524 for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1525 fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1530 { // nothing to do, as the sounds are at all times represented by their text-string names already
1534 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1535 { // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1536 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1540 SaveColor (FILE *f, ArgDescriptor *ad)
1541 { // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1542 if(colorVariable[(int)(intptr_t)ad->argLoc])
1543 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1547 SaveBoardSize (FILE *f, char *name, void *addr)
1548 { // wrapper to shield back-end from BoardSize & sizeInfo
1549 fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1553 ParseCommPortSettings (char *s)
1554 { // no such option in XBoard (yet)
1557 extern Widget engineOutputShell;
1560 GetActualPlacement (Widget wg, WindowPlacement *wp)
1565 XWindowAttributes winAt;
1572 XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1573 XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1574 wp->x = rx - winAt.x;
1575 wp->y = ry - winAt.y;
1576 wp->height = winAt.height;
1577 wp->width = winAt.width;
1582 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1583 // In XBoard this will have to wait until awareness of window parameters is implemented
1584 GetActualPlacement(shellWidget, &wpMain);
1585 if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1586 if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1587 if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1588 if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1589 if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1590 if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1594 PrintCommPortSettings (FILE *f, char *name)
1595 { // This option does not exist in XBoard
1599 MySearchPath (char *installDir, char *name, char *fullname)
1600 { // just append installDir and name. Perhaps ExpandPath should be used here?
1601 name = ExpandPathName(name);
1602 if(name && name[0] == '/')
1603 safeStrCpy(fullname, name, MSG_SIZ );
1605 sprintf(fullname, "%s%c%s", installDir, '/', name);
1611 MyGetFullPathName (char *name, char *fullname)
1612 { // should use ExpandPath?
1613 name = ExpandPathName(name);
1614 safeStrCpy(fullname, name, MSG_SIZ );
1619 EnsureOnScreen (int *x, int *y, int minX, int minY)
1626 { // [HGM] args: allows testing if main window is realized from back-end
1627 return xBoardWindow != 0;
1631 PopUpStartupDialog ()
1632 { // start menu not implemented in XBoard
1636 ConvertToLine (int argc, char **argv)
1638 static char line[128*1024], buf[1024];
1642 for(i=1; i<argc; i++)
1644 if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1645 && argv[i][0] != '{' )
1646 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1648 snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1649 strncat(line, buf, 128*1024 - strlen(line) - 1 );
1652 line[strlen(line)-1] = NULLCHAR;
1656 //--------------------------------------------------------------------------------------------
1658 extern Boolean twoBoards, partnerUp;
1661 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1663 #define BoardSize int
1665 InitDrawingSizes (BoardSize boardSize, int flags)
1666 { // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1667 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1669 XtGeometryResult gres;
1671 static Dimension oldWidth, oldHeight;
1672 static VariantClass oldVariant;
1673 static int oldDual = -1, oldMono = -1;
1675 if(!formWidget) return;
1677 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1678 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1679 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1681 if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1683 * Enable shell resizing.
1685 shellArgs[0].value = (XtArgVal) &w;
1686 shellArgs[1].value = (XtArgVal) &h;
1687 XtGetValues(shellWidget, shellArgs, 2);
1689 shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1690 shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1691 XtSetValues(shellWidget, &shellArgs[2], 4);
1693 XtSetArg(args[0], XtNdefaultDistance, &sep);
1694 XtGetValues(formWidget, args, 1);
1696 oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1698 hOffset = boardWidth + 10;
1699 for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1700 secondSegments[i] = gridSegments[i];
1701 secondSegments[i].x1 += hOffset;
1702 secondSegments[i].x2 += hOffset;
1705 XtSetArg(args[0], XtNwidth, boardWidth);
1706 XtSetArg(args[1], XtNheight, boardHeight);
1707 XtSetValues(boardWidget, args, 2);
1709 timerWidth = (boardWidth - sep) / 2;
1710 XtSetArg(args[0], XtNwidth, timerWidth);
1711 XtSetValues(whiteTimerWidget, args, 1);
1712 XtSetValues(blackTimerWidget, args, 1);
1714 XawFormDoLayout(formWidget, False);
1716 if (appData.titleInWindow) {
1718 XtSetArg(args[i], XtNborderWidth, &bor); i++;
1719 XtSetArg(args[i], XtNheight, &h); i++;
1720 XtGetValues(titleWidget, args, i);
1722 w = boardWidth - 2*bor;
1724 XtSetArg(args[0], XtNwidth, &w);
1725 XtGetValues(menuBarWidget, args, 1);
1726 w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1729 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1730 if (gres != XtGeometryYes && appData.debugMode) {
1732 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1733 programName, gres, w, h, wr, hr);
1737 XawFormDoLayout(formWidget, True);
1740 * Inhibit shell resizing.
1742 shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1743 shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1744 shellArgs[4].value = shellArgs[2].value = w;
1745 shellArgs[5].value = shellArgs[3].value = h;
1746 XtSetValues(shellWidget, &shellArgs[0], 6);
1749 // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1752 if(gameInfo.variant != oldVariant) { // and only if variant changed
1755 for(i=0; i<4; i++) {
1757 for(p=0; p<=(int)WhiteKing; p++)
1758 xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1759 if(gameInfo.variant == VariantShogi) {
1760 xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1761 xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1762 xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1763 xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1764 xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1767 if(gameInfo.variant == VariantGothic) {
1768 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1771 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1772 xpmPieceBitmap[i][(int)WhiteAngel] = xpmPieceBitmap2[i][(int)WhiteFalcon];
1773 xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1776 // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1777 for(p=0; p<=(int)WhiteKing; p++)
1778 ximMaskPm[p] = ximMaskPm2[p]; // defaults
1779 if(gameInfo.variant == VariantShogi) {
1780 ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1781 ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1782 ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1783 ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1784 ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1787 if(gameInfo.variant == VariantGothic) {
1788 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1791 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1792 ximMaskPm[(int)WhiteAngel] = ximMaskPm2[(int)WhiteFalcon];
1793 ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1798 for(i=0; i<2; i++) {
1800 for(p=0; p<=(int)WhiteKing; p++)
1801 pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1802 if(gameInfo.variant == VariantShogi) {
1803 pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1804 pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1805 pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1806 pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1807 pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1810 if(gameInfo.variant == VariantGothic) {
1811 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1814 if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1815 pieceBitmap[i][(int)WhiteAngel] = pieceBitmap2[i][(int)WhiteFalcon];
1816 pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1820 oldMono = -10; // kludge to force recreation of animation masks
1823 if(appData.monoMode != oldMono)
1826 oldMono = appData.monoMode;
1831 ParseIcsTextColors ()
1832 { // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1833 if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1834 parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1835 parse_cpair(ColorChannel1, appData.colorChannel1) < 0 ||
1836 parse_cpair(ColorChannel, appData.colorChannel) < 0 ||
1837 parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1838 parse_cpair(ColorTell, appData.colorTell) < 0 ||
1839 parse_cpair(ColorChallenge, appData.colorChallenge) < 0 ||
1840 parse_cpair(ColorRequest, appData.colorRequest) < 0 ||
1841 parse_cpair(ColorSeek, appData.colorSeek) < 0 ||
1842 parse_cpair(ColorNormal, appData.colorNormal) < 0)
1844 if (appData.colorize) {
1846 _("%s: can't parse color names; disabling colorization\n"),
1849 appData.colorize = FALSE;
1855 { // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1856 XrmValue vFrom, vTo;
1857 int forceMono = False;
1859 if (!appData.monoMode) {
1860 vFrom.addr = (caddr_t) appData.lightSquareColor;
1861 vFrom.size = strlen(appData.lightSquareColor);
1862 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1863 if (vTo.addr == NULL) {
1864 appData.monoMode = True;
1867 lightSquareColor = *(Pixel *) vTo.addr;
1870 if (!appData.monoMode) {
1871 vFrom.addr = (caddr_t) appData.darkSquareColor;
1872 vFrom.size = strlen(appData.darkSquareColor);
1873 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1874 if (vTo.addr == NULL) {
1875 appData.monoMode = True;
1878 darkSquareColor = *(Pixel *) vTo.addr;
1881 if (!appData.monoMode) {
1882 vFrom.addr = (caddr_t) appData.whitePieceColor;
1883 vFrom.size = strlen(appData.whitePieceColor);
1884 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1885 if (vTo.addr == NULL) {
1886 appData.monoMode = True;
1889 whitePieceColor = *(Pixel *) vTo.addr;
1892 if (!appData.monoMode) {
1893 vFrom.addr = (caddr_t) appData.blackPieceColor;
1894 vFrom.size = strlen(appData.blackPieceColor);
1895 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1896 if (vTo.addr == NULL) {
1897 appData.monoMode = True;
1900 blackPieceColor = *(Pixel *) vTo.addr;
1904 if (!appData.monoMode) {
1905 vFrom.addr = (caddr_t) appData.highlightSquareColor;
1906 vFrom.size = strlen(appData.highlightSquareColor);
1907 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1908 if (vTo.addr == NULL) {
1909 appData.monoMode = True;
1912 highlightSquareColor = *(Pixel *) vTo.addr;
1916 if (!appData.monoMode) {
1917 vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1918 vFrom.size = strlen(appData.premoveHighlightColor);
1919 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1920 if (vTo.addr == NULL) {
1921 appData.monoMode = True;
1924 premoveHighlightColor = *(Pixel *) vTo.addr;
1932 { // [HGM] taken out of main
1934 if (appData.monoMode && // [HGM] no sense to go on to certain doom
1935 (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1936 appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1938 if (appData.bitmapDirectory[0] != NULLCHAR) {
1942 CreateXPMBoard(appData.liteBackTextureFile, 1);
1943 CreateXPMBoard(appData.darkBackTextureFile, 0);
1947 /* Create regular pieces */
1948 if (!useImages) CreatePieces();
1953 main (int argc, char **argv)
1955 int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1956 XSetWindowAttributes window_attributes;
1958 Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1959 XrmValue vFrom, vTo;
1960 XtGeometryResult gres;
1963 int forceMono = False;
1965 srandom(time(0)); // [HGM] book: make random truly random
1967 setbuf(stdout, NULL);
1968 setbuf(stderr, NULL);
1971 if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1972 printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1976 programName = strrchr(argv[0], '/');
1977 if (programName == NULL)
1978 programName = argv[0];
1983 XtSetLanguageProc(NULL, NULL, NULL);
1984 bindtextdomain(PACKAGE, LOCALEDIR);
1985 textdomain(PACKAGE);
1989 XtAppInitialize(&appContext, "XBoard", shellOptions,
1990 XtNumber(shellOptions),
1991 &argc, argv, xboardResources, NULL, 0);
1992 appData.boardSize = "";
1993 InitAppData(ConvertToLine(argc, argv));
1995 if (p == NULL) p = "/tmp";
1996 i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1997 gameCopyFilename = (char*) malloc(i);
1998 gamePasteFilename = (char*) malloc(i);
1999 snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
2000 snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
2002 XtGetApplicationResources(shellWidget, (XtPointer) &appData,
2003 clientResources, XtNumber(clientResources),
2006 { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
2007 static char buf[MSG_SIZ];
2008 EscapeExpand(buf, appData.firstInitString);
2009 appData.firstInitString = strdup(buf);
2010 EscapeExpand(buf, appData.secondInitString);
2011 appData.secondInitString = strdup(buf);
2012 EscapeExpand(buf, appData.firstComputerString);
2013 appData.firstComputerString = strdup(buf);
2014 EscapeExpand(buf, appData.secondComputerString);
2015 appData.secondComputerString = strdup(buf);
2018 if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2021 if (chdir(chessDir) != 0) {
2022 fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2028 if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2029 /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2030 if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) {
2031 printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2034 setbuf(debugFP, NULL);
2038 if (appData.debugMode) {
2039 fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2043 /* [HGM,HR] make sure board size is acceptable */
2044 if(appData.NrFiles > BOARD_FILES ||
2045 appData.NrRanks > BOARD_RANKS )
2046 DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2049 /* This feature does not work; animation needs a rewrite */
2050 appData.highlightDragging = FALSE;
2054 xDisplay = XtDisplay(shellWidget);
2055 xScreen = DefaultScreen(xDisplay);
2056 wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2058 gameInfo.variant = StringToVariant(appData.variant);
2059 InitPosition(FALSE);
2062 InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2064 if (isdigit(appData.boardSize[0])) {
2065 i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2066 &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2067 &fontPxlSize, &smallLayout, &tinyLayout);
2069 fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2070 programName, appData.boardSize);
2074 /* Find some defaults; use the nearest known size */
2075 SizeDefaults *szd, *nearest;
2076 int distance = 99999;
2077 nearest = szd = sizeDefaults;
2078 while (szd->name != NULL) {
2079 if (abs(szd->squareSize - squareSize) < distance) {
2081 distance = abs(szd->squareSize - squareSize);
2082 if (distance == 0) break;
2086 if (i < 2) lineGap = nearest->lineGap;
2087 if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2088 if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2089 if (i < 5) fontPxlSize = nearest->fontPxlSize;
2090 if (i < 6) smallLayout = nearest->smallLayout;
2091 if (i < 7) tinyLayout = nearest->tinyLayout;
2094 SizeDefaults *szd = sizeDefaults;
2095 if (*appData.boardSize == NULLCHAR) {
2096 while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2097 DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2100 if (szd->name == NULL) szd--;
2101 appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2103 while (szd->name != NULL &&
2104 StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2105 if (szd->name == NULL) {
2106 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2107 programName, appData.boardSize);
2111 squareSize = szd->squareSize;
2112 lineGap = szd->lineGap;
2113 clockFontPxlSize = szd->clockFontPxlSize;
2114 coordFontPxlSize = szd->coordFontPxlSize;
2115 fontPxlSize = szd->fontPxlSize;
2116 smallLayout = szd->smallLayout;
2117 tinyLayout = szd->tinyLayout;
2118 // [HGM] font: use defaults from settings file if available and not overruled
2120 if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2121 appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2122 if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2123 appData.font = fontTable[MESSAGE_FONT][squareSize];
2124 if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2125 appData.coordFont = fontTable[COORD_FONT][squareSize];
2127 /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2128 if (strlen(appData.pixmapDirectory) > 0) {
2129 p = ExpandPathName(appData.pixmapDirectory);
2131 fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2132 appData.pixmapDirectory);
2135 if (appData.debugMode) {
2136 fprintf(stderr, _("\
2137 XBoard square size (hint): %d\n\
2138 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2140 squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2141 if (appData.debugMode) {
2142 fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2145 defaultLineGap = lineGap;
2146 if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2148 /* [HR] height treated separately (hacked) */
2149 boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2150 boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2151 if (appData.showJail == 1) {
2152 /* Jail on top and bottom */
2153 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2154 XtSetArg(boardArgs[2], XtNheight,
2155 boardHeight + 2*(lineGap + squareSize));
2156 } else if (appData.showJail == 2) {
2158 XtSetArg(boardArgs[1], XtNwidth,
2159 boardWidth + 2*(lineGap + squareSize));
2160 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2163 XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2164 XtSetArg(boardArgs[2], XtNheight, boardHeight);
2168 * Determine what fonts to use.
2171 appData.font = InsertPxlSize(appData.font, fontPxlSize);
2172 appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2173 appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2174 fontSet = CreateFontSet(appData.font);
2175 clockFontSet = CreateFontSet(appData.clockFont);
2177 /* For the coordFont, use the 0th font of the fontset. */
2178 XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2179 XFontStruct **font_struct_list;
2180 XFontSetExtents *fontSize;
2181 char **font_name_list;
2182 XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2183 coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2184 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2185 fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
2186 textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
2189 appData.font = FindFont(appData.font, fontPxlSize);
2190 appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2191 appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2192 clockFontID = XLoadFont(xDisplay, appData.clockFont);
2193 clockFontStruct = XQueryFont(xDisplay, clockFontID);
2194 coordFontID = XLoadFont(xDisplay, appData.coordFont);
2195 coordFontStruct = XQueryFont(xDisplay, coordFontID);
2197 countFontID = coordFontID; // [HGM] holdings
2198 countFontStruct = coordFontStruct;
2200 xdb = XtDatabase(xDisplay);
2202 XrmPutLineResource(&xdb, "*international: True");
2203 vTo.size = sizeof(XFontSet);
2204 vTo.addr = (XtPointer) &fontSet;
2205 XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2207 XrmPutStringResource(&xdb, "*font", appData.font);
2211 * Detect if there are not enough colors available and adapt.
2213 if (DefaultDepth(xDisplay, xScreen) <= 2) {
2214 appData.monoMode = True;
2217 forceMono = MakeColors();
2220 fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2222 appData.monoMode = True;
2225 if (appData.lowTimeWarning && !appData.monoMode) {
2226 vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2227 vFrom.size = strlen(appData.lowTimeWarningColor);
2228 XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2229 if (vTo.addr == NULL)
2230 appData.monoMode = True;
2232 lowTimeWarningColor = *(Pixel *) vTo.addr;
2235 if (appData.monoMode && appData.debugMode) {
2236 fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2237 (unsigned long) XWhitePixel(xDisplay, xScreen),
2238 (unsigned long) XBlackPixel(xDisplay, xScreen));
2241 ParseIcsTextColors();
2242 textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2243 textColors[ColorNone].attr = 0;
2245 XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2251 layoutName = "tinyLayout";
2252 } else if (smallLayout) {
2253 layoutName = "smallLayout";
2255 layoutName = "normalLayout";
2257 /* Outer layoutWidget is there only to provide a name for use in
2258 resources that depend on the layout style */
2260 XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2261 layoutArgs, XtNumber(layoutArgs));
2263 XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2264 formArgs, XtNumber(formArgs));
2265 XtSetArg(args[0], XtNdefaultDistance, &sep);
2266 XtGetValues(formWidget, args, 1);
2269 widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
2270 XtSetArg(args[0], XtNtop, XtChainTop);
2271 XtSetArg(args[1], XtNbottom, XtChainTop);
2272 XtSetArg(args[2], XtNright, XtChainLeft);
2273 XtSetValues(menuBarWidget, args, 3);
2275 widgetList[j++] = whiteTimerWidget =
2276 XtCreateWidget("whiteTime", labelWidgetClass,
2277 formWidget, timerArgs, XtNumber(timerArgs));
2279 XtSetArg(args[0], XtNfontSet, clockFontSet);
2281 XtSetArg(args[0], XtNfont, clockFontStruct);
2283 XtSetArg(args[1], XtNtop, XtChainTop);
2284 XtSetArg(args[2], XtNbottom, XtChainTop);
2285 XtSetValues(whiteTimerWidget, args, 3);
2287 widgetList[j++] = blackTimerWidget =
2288 XtCreateWidget("blackTime", labelWidgetClass,
2289 formWidget, timerArgs, XtNumber(timerArgs));
2291 XtSetArg(args[0], XtNfontSet, clockFontSet);
2293 XtSetArg(args[0], XtNfont, clockFontStruct);
2295 XtSetArg(args[1], XtNtop, XtChainTop);
2296 XtSetArg(args[2], XtNbottom, XtChainTop);
2297 XtSetValues(blackTimerWidget, args, 3);
2299 if (appData.titleInWindow) {
2300 widgetList[j++] = titleWidget =
2301 XtCreateWidget("title", labelWidgetClass, formWidget,
2302 titleArgs, XtNumber(titleArgs));
2303 XtSetArg(args[0], XtNtop, XtChainTop);
2304 XtSetArg(args[1], XtNbottom, XtChainTop);
2305 XtSetValues(titleWidget, args, 2);
2308 if (appData.showButtonBar) {
2309 widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2310 XtSetArg(args[0], XtNleft, XtChainRight); // [HGM] glue to right window edge
2311 XtSetArg(args[1], XtNright, XtChainRight); // for good run-time sizing
2312 XtSetArg(args[2], XtNtop, XtChainTop);
2313 XtSetArg(args[3], XtNbottom, XtChainTop);
2314 XtSetValues(buttonBarWidget, args, 4);
2317 widgetList[j++] = messageWidget =
2318 XtCreateWidget("message", labelWidgetClass, formWidget,
2319 messageArgs, XtNumber(messageArgs));
2320 XtSetArg(args[0], XtNtop, XtChainTop);
2321 XtSetArg(args[1], XtNbottom, XtChainTop);
2322 XtSetValues(messageWidget, args, 2);
2324 widgetList[j++] = boardWidget =
2325 XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2326 XtNumber(boardArgs));
2328 XtManageChildren(widgetList, j);
2330 timerWidth = (boardWidth - sep) / 2;
2331 XtSetArg(args[0], XtNwidth, timerWidth);
2332 XtSetValues(whiteTimerWidget, args, 1);
2333 XtSetValues(blackTimerWidget, args, 1);
2335 XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2336 XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2337 XtGetValues(whiteTimerWidget, args, 2);
2339 if (appData.showButtonBar) {
2340 XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2341 XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2342 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2346 * formWidget uses these constraints but they are stored
2350 XtSetArg(args[i], XtNfromHoriz, 0); i++;
2351 XtSetValues(menuBarWidget, args, i);
2352 if (appData.titleInWindow) {
2355 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2356 XtSetValues(whiteTimerWidget, args, i);
2358 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2359 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2360 XtSetValues(blackTimerWidget, args, i);
2362 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2363 XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2364 XtSetValues(titleWidget, args, i);
2366 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2367 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2368 XtSetValues(messageWidget, args, i);
2369 if (appData.showButtonBar) {
2371 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2372 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2373 XtSetValues(buttonBarWidget, args, i);
2377 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2378 XtSetValues(whiteTimerWidget, args, i);
2380 XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2381 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2382 XtSetValues(blackTimerWidget, args, i);
2384 XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2385 XtSetValues(titleWidget, args, i);
2387 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2388 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2389 XtSetValues(messageWidget, args, i);
2390 if (appData.showButtonBar) {
2392 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2393 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2394 XtSetValues(buttonBarWidget, args, i);
2399 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2400 XtSetValues(whiteTimerWidget, args, i);
2402 XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2403 XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2404 XtSetValues(blackTimerWidget, args, i);
2406 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2407 XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2408 XtSetValues(messageWidget, args, i);
2409 if (appData.showButtonBar) {
2411 XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2412 XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2413 XtSetValues(buttonBarWidget, args, i);
2417 XtSetArg(args[0], XtNfromVert, messageWidget);
2418 XtSetArg(args[1], XtNtop, XtChainTop);
2419 XtSetArg(args[2], XtNbottom, XtChainBottom);
2420 XtSetArg(args[3], XtNleft, XtChainLeft);
2421 XtSetArg(args[4], XtNright, XtChainRight);
2422 XtSetValues(boardWidget, args, 5);
2424 XtRealizeWidget(shellWidget);
2427 XtSetArg(args[0], XtNx, wpMain.x);
2428 XtSetArg(args[1], XtNy, wpMain.y);
2429 XtSetValues(shellWidget, args, 2);
2433 * Correct the width of the message and title widgets.
2434 * It is not known why some systems need the extra fudge term.
2435 * The value "2" is probably larger than needed.
2437 XawFormDoLayout(formWidget, False);
2439 #define WIDTH_FUDGE 2
2441 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2442 XtSetArg(args[i], XtNheight, &h); i++;
2443 XtGetValues(messageWidget, args, i);
2444 if (appData.showButtonBar) {
2446 XtSetArg(args[i], XtNwidth, &w); i++;
2447 XtGetValues(buttonBarWidget, args, i);
2448 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2450 w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2453 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2454 if (gres != XtGeometryYes && appData.debugMode) {
2455 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2456 programName, gres, w, h, wr, hr);
2459 /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2460 /* The size used for the child widget in layout lags one resize behind
2461 its true size, so we resize a second time, 1 pixel smaller. Yeech! */
2463 gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2464 if (gres != XtGeometryYes && appData.debugMode) {
2465 fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2466 programName, gres, w, h, wr, hr);
2469 if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
2470 XtSetArg(args[0], XtNleft, XtChainLeft); // [HGM] glue ends for good run-time sizing
2471 XtSetArg(args[1], XtNright, XtChainRight);
2472 XtSetValues(messageWidget, args, 2);
2474 if (appData.titleInWindow) {
2476 XtSetArg(args[i], XtNborderWidth, &bor); i++;
2477 XtSetArg(args[i], XtNheight, &h); i++;
2478 XtGetValues(titleWidget, args, i);
2480 w = boardWidth - 2*bor;
2482 XtSetArg(args[0], XtNwidth, &w);
2483 XtGetValues(menuBarWidget, args, 1);
2484 w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2487 gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2488 if (gres != XtGeometryYes && appData.debugMode) {
2490 _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2491 programName, gres, w, h, wr, hr);
2494 XawFormDoLayout(formWidget, True);
2496 xBoardWindow = XtWindow(boardWidget);
2498 // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2499 // not need to go into InitDrawingSizes().
2503 * Create X checkmark bitmap and initialize option menu checks.
2505 ReadBitmap(&xMarkPixmap, "checkmark.bm",
2506 checkmark_bits, checkmark_width, checkmark_height);
2507 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2508 #ifndef OPTIONSDIALOG
2509 if (appData.alwaysPromoteToQueen) {
2510 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2513 if (appData.animateDragging) {
2514 XtSetValues(XtNameToWidget(menuBarWidget,
2515 "menuOptions.Animate Dragging"),
2518 if (appData.animate) {
2519 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2522 if (appData.autoCallFlag) {
2523 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2526 if (appData.autoFlipView) {
2527 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2530 if (appData.blindfold) {
2531 XtSetValues(XtNameToWidget(menuBarWidget,
2532 "menuOptions.Blindfold"), args, 1);
2534 if (appData.flashCount > 0) {
2535 XtSetValues(XtNameToWidget(menuBarWidget,
2536 "menuOptions.Flash Moves"),
2540 if (appData.highlightDragging) {
2541 XtSetValues(XtNameToWidget(menuBarWidget,
2542 "menuOptions.Highlight Dragging"),
2546 if (appData.highlightLastMove) {
2547 XtSetValues(XtNameToWidget(menuBarWidget,
2548 "menuOptions.Highlight Last Move"),
2551 if (appData.highlightMoveWithArrow) {
2552 XtSetValues(XtNameToWidget(menuBarWidget,
2553 "menuOptions.Arrow"),
2556 // if (appData.icsAlarm) {
2557 // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2560 if (appData.ringBellAfterMoves) {
2561 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2564 if (appData.oneClick) {
2565 XtSetValues(XtNameToWidget(menuBarWidget,
2566 "menuOptions.OneClick"), args, 1);
2568 if (appData.periodicUpdates) {
2569 XtSetValues(XtNameToWidget(menuBarWidget,
2570 "menuOptions.Periodic Updates"), args, 1);
2572 if (appData.ponderNextMove) {
2573 XtSetValues(XtNameToWidget(menuBarWidget,
2574 "menuOptions.Ponder Next Move"), args, 1);
2576 if (appData.popupExitMessage) {
2577 XtSetValues(XtNameToWidget(menuBarWidget,
2578 "menuOptions.Popup Exit Message"), args, 1);
2580 if (appData.popupMoveErrors) {
2581 XtSetValues(XtNameToWidget(menuBarWidget,
2582 "menuOptions.Popup Move Errors"), args, 1);
2584 // if (appData.premove) {
2585 // XtSetValues(XtNameToWidget(menuBarWidget,
2586 // "menuOptions.Premove"), args, 1);
2588 if (appData.showCoords) {
2589 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2592 if (appData.hideThinkingFromHuman) {
2593 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2596 if (appData.testLegality) {
2597 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2601 if (saveSettingsOnExit) {
2602 XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2609 ReadBitmap(&wIconPixmap, "icon_white.bm",
2610 icon_white_bits, icon_white_width, icon_white_height);
2611 ReadBitmap(&bIconPixmap, "icon_black.bm",
2612 icon_black_bits, icon_black_width, icon_black_height);
2613 iconPixmap = wIconPixmap;
2615 XtSetArg(args[i], XtNiconPixmap, iconPixmap); i++;
2616 XtSetValues(shellWidget, args, i);
2619 * Create a cursor for the board widget.
2621 window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2622 XChangeWindowAttributes(xDisplay, xBoardWindow,
2623 CWCursor, &window_attributes);
2626 * Inhibit shell resizing.
2628 shellArgs[0].value = (XtArgVal) &w;
2629 shellArgs[1].value = (XtArgVal) &h;
2630 XtGetValues(shellWidget, shellArgs, 2);
2631 shellArgs[4].value = shellArgs[2].value = w;
2632 shellArgs[5].value = shellArgs[3].value = h;
2633 XtSetValues(shellWidget, &shellArgs[2], 4);
2634 marginW = w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2635 marginH = h - boardHeight;
2637 CatchDeleteWindow(shellWidget, "QuitProc");
2645 if (appData.animate || appData.animateDragging)
2648 XtAugmentTranslations(formWidget,
2649 XtParseTranslationTable(globalTranslations));
2650 XtAugmentTranslations(boardWidget,
2651 XtParseTranslationTable(boardTranslations));
2652 XtAugmentTranslations(whiteTimerWidget,
2653 XtParseTranslationTable(whiteTranslations));
2654 XtAugmentTranslations(blackTimerWidget,
2655 XtParseTranslationTable(blackTranslations));
2657 /* Why is the following needed on some versions of X instead
2658 * of a translation? */
2659 XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2660 (XtEventHandler) EventProc, NULL);
2662 XtAddEventHandler(formWidget, KeyPressMask, False,
2663 (XtEventHandler) MoveTypeInProc, NULL);
2665 /* [AS] Restore layout */
2666 if( wpMoveHistory.visible ) {
2670 if( wpEvalGraph.visible )
2675 if( wpEngineOutput.visible ) {
2676 EngineOutputPopUp();
2681 if (errorExitStatus == -1) {
2682 if (appData.icsActive) {
2683 /* We now wait until we see "login:" from the ICS before
2684 sending the logon script (problems with timestamp otherwise) */
2685 /*ICSInitScript();*/
2686 if (appData.icsInputBox) ICSInputBoxPopUp();
2690 signal(SIGWINCH, TermSizeSigHandler);
2692 signal(SIGINT, IntSigHandler);
2693 signal(SIGTERM, IntSigHandler);
2694 if (*appData.cmailGameName != NULLCHAR) {
2695 signal(SIGUSR1, CmailSigHandler);
2698 gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2700 // XtSetKeyboardFocus(shellWidget, formWidget);
2701 XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2703 XtAppMainLoop(appContext);
2704 if (appData.debugMode) fclose(debugFP); // [DM] debug
2708 static Boolean noEcho;
2713 if (appData.icsActive && oldICSInteractionTitle != NULL) {
2714 DisplayIcsInteractionTitle(oldICSInteractionTitle);
2716 if (saveSettingsOnExit) SaveSettings(settingsFileName);
2717 unlink(gameCopyFilename);
2718 unlink(gamePasteFilename);
2719 if(noEcho) EchoOn();
2723 TermSizeSigHandler (int sig)
2729 IntSigHandler (int sig)
2735 CmailSigHandler (int sig)
2740 signal(SIGUSR1, SIG_IGN); /* suspend handler */
2742 /* Activate call-back function CmailSigHandlerCallBack() */
2743 OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2745 signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2749 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2752 ReloadCmailMsgEvent(TRUE); /* Reload cmail msg */
2754 /**** end signal code ****/
2760 /* try to open the icsLogon script, either in the location given
2761 * or in the users HOME directory
2768 f = fopen(appData.icsLogon, "r");
2771 homedir = getenv("HOME");
2772 if (homedir != NULL)
2774 safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2775 strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2776 strncat(buf, appData.icsLogon, MSG_SIZ - strlen(buf) - 1);
2777 f = fopen(buf, "r");
2782 ProcessICSInitScript(f);
2784 printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2803 GreyRevert (Boolean grey)
2806 if (!menuBarWidget) return;
2807 w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2809 DisplayError("menuEdit.Revert", 0);
2811 XtSetSensitive(w, !grey);
2813 w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2815 DisplayError("menuEdit.Annotate", 0);
2817 XtSetSensitive(w, !grey);
2822 SetMenuEnables (Enables *enab)
2825 if (!menuBarWidget) return;
2826 while (enab->name != NULL) {
2827 w = XtNameToWidget(menuBarWidget, enab->name);
2829 DisplayError(enab->name, 0);
2831 XtSetSensitive(w, enab->value);
2837 Enables icsEnables[] = {
2838 { "menuFile.Mail Move", False },
2839 { "menuFile.Reload CMail Message", False },
2840 { "menuMode.Machine Black", False },
2841 { "menuMode.Machine White", False },
2842 { "menuMode.Analysis Mode", False },
2843 { "menuMode.Analyze File", False },
2844 { "menuMode.Two Machines", False },
2845 { "menuMode.Machine Match", False },
2847 { "menuEngine.Hint", False },
2848 { "menuEngine.Book", False },
2849 { "menuEngine.Move Now", False },
2850 #ifndef OPTIONSDIALOG
2851 { "menuOptions.Periodic Updates", False },
2852 { "menuOptions.Hide Thinking", False },
2853 { "menuOptions.Ponder Next Move", False },
2856 { "menuEngine.Engine #1 Settings", False },
2857 { "menuEngine.Engine #2 Settings", False },
2858 { "menuEngine.Load Engine", False },
2859 { "menuEdit.Annotate", False },
2860 { "menuOptions.Match", False },
2864 Enables ncpEnables[] = {
2865 { "menuFile.Mail Move", False },
2866 { "menuFile.Reload CMail Message", False },
2867 { "menuMode.Machine White", False },
2868 { "menuMode.Machine Black", False },
2869 { "menuMode.Analysis Mode", False },
2870 { "menuMode.Analyze File", False },
2871 { "menuMode.Two Machines", False },
2872 { "menuMode.Machine Match", False },
2873 { "menuMode.ICS Client", False },
2874 { "menuView.ICStex", False },
2875 { "menuView.ICS Input Box", False },
2876 { "Action", False },
2877 { "menuEdit.Revert", False },
2878 { "menuEdit.Annotate", False },
2879 { "menuEngine.Engine #1 Settings", False },
2880 { "menuEngine.Engine #2 Settings", False },
2881 { "menuEngine.Move Now", False },
2882 { "menuEngine.Retract Move", False },
2883 { "menuOptions.ICS", False },
2884 #ifndef OPTIONSDIALOG
2885 { "menuOptions.Auto Flag", False },
2886 { "menuOptions.Auto Flip View", False },
2887 // { "menuOptions.ICS Alarm", False },
2888 { "menuOptions.Move Sound", False },
2889 { "menuOptions.Hide Thinking", False },
2890 { "menuOptions.Periodic Updates", False },
2891 { "menuOptions.Ponder Next Move", False },
2893 { "menuEngine.Hint", False },
2894 { "menuEngine.Book", False },
2898 Enables gnuEnables[] = {
2899 { "menuMode.ICS Client", False },
2900 { "menuView.ICStex", False },
2901 { "menuView.ICS Input Box", False },
2902 { "menuAction.Accept", False },
2903 { "menuAction.Decline", False },
2904 { "menuAction.Rematch", False },
2905 { "menuAction.Adjourn", False },
2906 { "menuAction.Stop Examining", False },
2907 { "menuAction.Stop Observing", False },
2908 { "menuAction.Upload to Examine", False },
2909 { "menuEdit.Revert", False },
2910 { "menuEdit.Annotate", False },
2911 { "menuOptions.ICS", False },
2913 /* The next two options rely on SetCmailMode being called *after* */
2914 /* SetGNUMode so that when GNU is being used to give hints these */
2915 /* menu options are still available */
2917 { "menuFile.Mail Move", False },
2918 { "menuFile.Reload CMail Message", False },
2919 // [HGM] The following have been added to make a switch from ncp to GNU mode possible
2920 { "menuMode.Machine White", True },
2921 { "menuMode.Machine Black", True },
2922 { "menuMode.Analysis Mode", True },
2923 { "menuMode.Analyze File", True },
2924 { "menuMode.Two Machines", True },
2925 { "menuMode.Machine Match", True },
2926 { "menuEngine.Engine #1 Settings", True },
2927 { "menuEngine.Engine #2 Settings", True },
2928 { "menuEngine.Hint", True },
2929 { "menuEngine.Book", True },
2930 { "menuEngine.Move Now", True },
2931 { "menuEngine.Retract Move", True },
2936 Enables cmailEnables[] = {
2938 { "menuAction.Call Flag", False },
2939 { "menuAction.Draw", True },
2940 { "menuAction.Adjourn", False },
2941 { "menuAction.Abort", False },
2942 { "menuAction.Stop Observing", False },
2943 { "menuAction.Stop Examining", False },
2944 { "menuFile.Mail Move", True },
2945 { "menuFile.Reload CMail Message", True },
2949 Enables trainingOnEnables[] = {
2950 { "menuMode.Edit Comment", False },
2951 { "menuMode.Pause", False },
2952 { "menuEdit.Forward", False },
2953 { "menuEdit.Backward", False },
2954 { "menuEdit.Forward to End", False },
2955 { "menuEdit.Back to Start", False },
2956 { "menuEngine.Move Now", False },
2957 { "menuEdit.Truncate Game", False },
2961 Enables trainingOffEnables[] = {
2962 { "menuMode.Edit Comment", True },
2963 { "menuMode.Pause", True },
2964 { "menuEdit.Forward", True },
2965 { "menuEdit.Backward", True },
2966 { "menuEdit.Forward to End", True },
2967 { "menuEdit.Back to Start", True },
2968 { "menuEngine.Move Now", True },
2969 { "menuEdit.Truncate Game", True },
2973 Enables machineThinkingEnables[] = {
2974 { "menuFile.Load Game", False },
2975 // { "menuFile.Load Next Game", False },
2976 // { "menuFile.Load Previous Game", False },
2977 // { "menuFile.Reload Same Game", False },
2978 { "menuEdit.Paste Game", False },
2979 { "menuFile.Load Position", False },
2980 // { "menuFile.Load Next Position", False },
2981 // { "menuFile.Load Previous Position", False },
2982 // { "menuFile.Reload Same Position", False },
2983 { "menuEdit.Paste Position", False },
2984 { "menuMode.Machine White", False },
2985 { "menuMode.Machine Black", False },
2986 { "menuMode.Two Machines", False },
2987 // { "menuMode.Machine Match", False },
2988 { "menuEngine.Retract Move", False },
2992 Enables userThinkingEnables[] = {
2993 { "menuFile.Load Game", True },
2994 // { "menuFile.Load Next Game", True },
2995 // { "menuFile.Load Previous Game", True },
2996 // { "menuFile.Reload Same Game", True },
2997 { "menuEdit.Paste Game", True },
2998 { "menuFile.Load Position", True },
2999 // { "menuFile.Load Next Position", True },
3000 // { "menuFile.Load Previous Position", True },
3001 // { "menuFile.Reload Same Position", True },
3002 { "menuEdit.Paste Position", True },
3003 { "menuMode.Machine White", True },
3004 { "menuMode.Machine Black", True },
3005 { "menuMode.Two Machines", True },
3006 // { "menuMode.Machine Match", True },
3007 { "menuEngine.Retract Move", True },
3014 SetMenuEnables(icsEnables);
3017 if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
3018 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
3019 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
3027 SetMenuEnables(ncpEnables);
3033 SetMenuEnables(gnuEnables);
3039 SetMenuEnables(cmailEnables);
3043 SetTrainingModeOn ()
3045 SetMenuEnables(trainingOnEnables);
3046 if (appData.showButtonBar) {
3047 XtSetSensitive(buttonBarWidget, False);
3053 SetTrainingModeOff ()
3055 SetMenuEnables(trainingOffEnables);
3056 if (appData.showButtonBar) {
3057 XtSetSensitive(buttonBarWidget, True);
3062 SetUserThinkingEnables ()
3064 if (appData.noChessProgram) return;
3065 SetMenuEnables(userThinkingEnables);
3069 SetMachineThinkingEnables ()
3071 if (appData.noChessProgram) return;
3072 SetMenuEnables(machineThinkingEnables);
3074 case MachinePlaysBlack:
3075 case MachinePlaysWhite:
3076 case TwoMachinesPlay:
3077 XtSetSensitive(XtNameToWidget(menuBarWidget,
3078 ModeToWidgetName(gameMode)), True);
3085 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3086 #define HISTORY_SIZE 64
3087 static char *history[HISTORY_SIZE];
3088 int histIn = 0, histP = 0;
3091 SaveInHistory (char *cmd)
3093 if (history[histIn] != NULL) {
3094 free(history[histIn]);
3095 history[histIn] = NULL;
3097 if (*cmd == NULLCHAR) return;
3098 history[histIn] = StrSave(cmd);
3099 histIn = (histIn + 1) % HISTORY_SIZE;
3100 if (history[histIn] != NULL) {
3101 free(history[histIn]);
3102 history[histIn] = NULL;
3108 PrevInHistory (char *cmd)
3111 if (histP == histIn) {
3112 if (history[histIn] != NULL) free(history[histIn]);
3113 history[histIn] = StrSave(cmd);
3115 newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3116 if (newhp == histIn || history[newhp] == NULL) return NULL;
3118 return history[histP];
3124 if (histP == histIn) return NULL;
3125 histP = (histP + 1) % HISTORY_SIZE;
3126 return history[histP];
3128 // end of borrowed code
3130 #define Abs(n) ((n)<0 ? -(n) : (n))
3134 InsertPxlSize (char *pattern, int targetPxlSize)
3136 char *base_fnt_lst, strInt[12], *p, *q;
3137 int alternatives, i, len, strIntLen;
3140 * Replace the "*" (if present) in the pixel-size slot of each
3141 * alternative with the targetPxlSize.
3145 while ((p = strchr(p, ',')) != NULL) {
3149 snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
3150 strIntLen = strlen(strInt);
3151 base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
3155 while (alternatives--) {
3156 char *comma = strchr(p, ',');
3157 for (i=0; i<14; i++) {
3158 char *hyphen = strchr(p, '-');
3160 if (comma && hyphen > comma) break;
3161 len = hyphen + 1 - p;
3162 if (i == 7 && *p == '*' && len == 2) {
3164 memcpy(q, strInt, strIntLen);
3174 len = comma + 1 - p;
3181 return base_fnt_lst;
3185 CreateFontSet (char *base_fnt_lst)
3188 char **missing_list;
3192 fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
3193 &missing_list, &missing_count, &def_string);
3194 if (appData.debugMode) {
3196 XFontStruct **font_struct_list;
3197 char **font_name_list;
3198 fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
3200 fprintf(debugFP, " got list %s, locale %s\n",
3201 XBaseFontNameListOfFontSet(fntSet),
3202 XLocaleOfFontSet(fntSet));
3203 count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
3204 for (i = 0; i < count; i++) {
3205 fprintf(debugFP, " got charset %s\n", font_name_list[i]);
3208 for (i = 0; i < missing_count; i++) {
3209 fprintf(debugFP, " missing charset %s\n", missing_list[i]);
3212 if (fntSet == NULL) {
3213 fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
3218 #else // not ENABLE_NLS
3220 * Find a font that matches "pattern" that is as close as
3221 * possible to the targetPxlSize. Prefer fonts that are k
3222 * pixels smaller to fonts that are k pixels larger. The
3223 * pattern must be in the X Consortium standard format,
3224 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3225 * The return value should be freed with XtFree when no
3229 FindFont (char *pattern, int targetPxlSize)
3231 char **fonts, *p, *best, *scalable, *scalableTail;
3232 int i, j, nfonts, minerr, err, pxlSize;
3234 fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3236 fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3237 programName, pattern);
3244 for (i=0; i<nfonts; i++) {
3247 if (*p != '-') continue;
3249 if (*p == NULLCHAR) break;
3250 if (*p++ == '-') j++;
3252 if (j < 7) continue;
3255 scalable = fonts[i];
3258 err = pxlSize - targetPxlSize;
3259 if (Abs(err) < Abs(minerr) ||
3260 (minerr > 0 && err < 0 && -err == minerr)) {
3266 if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3267 /* If the error is too big and there is a scalable font,
3268 use the scalable font. */
3269 int headlen = scalableTail - scalable;
3270 p = (char *) XtMalloc(strlen(scalable) + 10);
3271 while (isdigit(*scalableTail)) scalableTail++;
3272 sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3274 p = (char *) XtMalloc(strlen(best) + 2);
3275 safeStrCpy(p, best, strlen(best)+1 );
3277 if (appData.debugMode) {
3278 fprintf(debugFP, _("resolved %s at pixel size %d\n to %s\n"),
3279 pattern, targetPxlSize, p);
3281 XFreeFontNames(fonts);
3288 { // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3289 // must be called before all non-first callse to CreateGCs()
3290 XtReleaseGC(shellWidget, highlineGC);
3291 XtReleaseGC(shellWidget, lightSquareGC);
3292 XtReleaseGC(shellWidget, darkSquareGC);
3293 XtReleaseGC(shellWidget, lineGC);
3294 if (appData.monoMode) {
3295 if (DefaultDepth(xDisplay, xScreen) == 1) {
3296 XtReleaseGC(shellWidget, wbPieceGC);
3298 XtReleaseGC(shellWidget, bwPieceGC);
3301 XtReleaseGC(shellWidget, prelineGC);
3302 XtReleaseGC(shellWidget, jailSquareGC);
3303 XtReleaseGC(shellWidget, wdPieceGC);
3304 XtReleaseGC(shellWidget, wlPieceGC);
3305 XtReleaseGC(shellWidget, wjPieceGC);
3306 XtReleaseGC(shellWidget, bdPieceGC);
3307 XtReleaseGC(shellWidget, blPieceGC);
3308 XtReleaseGC(shellWidget, bjPieceGC);
3313 CreateGCs (int redo)
3315 XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3316 | GCBackground | GCFunction | GCPlaneMask;
3317 XGCValues gc_values;
3320 gc_values.plane_mask = AllPlanes;
3321 gc_values.line_width = lineGap;
3322 gc_values.line_style = LineSolid;
3323 gc_values.function = GXcopy;
3326 DeleteGCs(); // called a second time; clean up old GCs first
3327 } else { // [HGM] grid and font GCs created on first call only
3328 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3329 gc_values.background = XWhitePixel(xDisplay, xScreen);
3330 coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3331 XSetFont(xDisplay, coordGC, coordFontID);
3333 // [HGM] make font for holdings counts (white on black)
3334 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3335 gc_values.background = XBlackPixel(xDisplay, xScreen);
3336 countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3337 XSetFont(xDisplay, countGC, countFontID);
3339 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3340 gc_values.background = XBlackPixel(xDisplay, xScreen);
3341 lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3343 if (appData.monoMode) {
3344 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3345 gc_values.background = XWhitePixel(xDisplay, xScreen);
3346 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3348 gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3349 gc_values.background = XBlackPixel(xDisplay, xScreen);
3350 lightSquareGC = wbPieceGC
3351 = XtGetGC(shellWidget, value_mask, &gc_values);
3353 gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3354 gc_values.background = XWhitePixel(xDisplay, xScreen);
3355 darkSquareGC = bwPieceGC
3356 = XtGetGC(shellWidget, value_mask, &gc_values);
3358 if (DefaultDepth(xDisplay, xScreen) == 1) {
3359 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3360 gc_values.function = GXcopyInverted;
3361 copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3362 gc_values.function = GXcopy;
3363 if (XBlackPixel(xDisplay, xScreen) == 1) {
3364 bwPieceGC = darkSquareGC;
3365 wbPieceGC = copyInvertedGC;
3367 bwPieceGC = copyInvertedGC;
3368 wbPieceGC = lightSquareGC;
3372 gc_values.foreground = highlightSquareColor;
3373 gc_values.background = highlightSquareColor;
3374 highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3376 gc_values.foreground = premoveHighlightColor;
3377 gc_values.background = premoveHighlightColor;
3378 prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3380 gc_values.foreground = lightSquareColor;
3381 gc_values.background = darkSquareColor;
3382 lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3384 gc_values.foreground = darkSquareColor;
3385 gc_values.background = lightSquareColor;
3386 darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3388 gc_values.foreground = jailSquareColor;
3389 gc_values.background = jailSquareColor;
3390 jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3392 gc_values.foreground = whitePieceColor;
3393 gc_values.background = darkSquareColor;
3394 wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3396 gc_values.foreground = whitePieceColor;
3397 gc_values.background = lightSquareColor;
3398 wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3400 gc_values.foreground = whitePieceColor;
3401 gc_values.background = jailSquareColor;
3402 wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3404 gc_values.foreground = blackPieceColor;
3405 gc_values.background = darkSquareColor;
3406 bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3408 gc_values.foreground = blackPieceColor;
3409 gc_values.background = lightSquareColor;
3410 blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3412 gc_values.foreground = blackPieceColor;
3413 gc_values.background = jailSquareColor;
3414 bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3419 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
3427 fp = fopen(filename, "rb");
3429 fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3436 for (y=0; y<h; ++y) {
3437 for (x=0; x<h; ++x) {
3442 XPutPixel(xim, x, y, blackPieceColor);
3444 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3447 XPutPixel(xim, x, y, darkSquareColor);
3449 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3452 XPutPixel(xim, x, y, whitePieceColor);
3454 XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3457 XPutPixel(xim, x, y, lightSquareColor);
3459 XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3467 /* create Pixmap of piece */
3468 *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3470 XPutImage(xDisplay, *dest, lightSquareGC, xim,
3473 /* create Pixmap of clipmask
3474 Note: We assume the white/black pieces have the same
3475 outline, so we make only 6 masks. This is okay
3476 since the XPM clipmask routines do the same. */
3478 temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3480 XPutImage(xDisplay, temp, lightSquareGC, xmask,
3483 /* now create the 1-bit version */
3484 *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3487 values.foreground = 1;
3488 values.background = 0;
3490 /* Don't use XtGetGC, not read only */
3491 maskGC = XCreateGC(xDisplay, *mask,
3492 GCForeground | GCBackground, &values);
3493 XCopyPlane(xDisplay, temp, *mask, maskGC,
3494 0, 0, squareSize, squareSize, 0, 0, 1);
3495 XFreePixmap(xDisplay, temp);
3500 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3508 static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3513 /* The XSynchronize calls were copied from CreatePieces.
3514 Not sure if needed, but can't hurt */
3515 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3518 /* temp needed by loadXIM() */
3519 ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3520 0, 0, ss, ss, AllPlanes, XYPixmap);
3522 if (strlen(appData.pixmapDirectory) == 0) {
3526 if (appData.monoMode) {
3527 DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3531 fprintf(stderr, _("\nLoading XIMs...\n"));
3533 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3534 fprintf(stderr, "%d", piece+1);
3535 for (kind=0; kind<4; kind++) {
3536 fprintf(stderr, ".");
3537 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3538 ExpandPathName(appData.pixmapDirectory),
3539 piece <= (int) WhiteKing ? "" : "w",
3540 pieceBitmapNames[piece],
3542 ximPieceBitmap[kind][piece] =
3543 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3544 0, 0, ss, ss, AllPlanes, XYPixmap);
3545 if (appData.debugMode)
3546 fprintf(stderr, _("(File:%s:) "), buf);
3547 loadXIM(ximPieceBitmap[kind][piece],
3549 &(xpmPieceBitmap2[kind][piece]),
3550 &(ximMaskPm2[piece]));
3551 if(piece <= (int)WhiteKing)
3552 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3554 fprintf(stderr," ");
3556 /* Load light and dark squares */
3557 /* If the LSQ and DSQ pieces don't exist, we will
3558 draw them with solid squares. */
3559 snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3560 if (access(buf, 0) != 0) {
3564 fprintf(stderr, _("light square "));
3566 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3567 0, 0, ss, ss, AllPlanes, XYPixmap);
3568 if (appData.debugMode)
3569 fprintf(stderr, _("(File:%s:) "), buf);
3571 loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3572 fprintf(stderr, _("dark square "));
3573 snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3574 ExpandPathName(appData.pixmapDirectory), ss);
3575 if (appData.debugMode)
3576 fprintf(stderr, _("(File:%s:) "), buf);
3578 XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3579 0, 0, ss, ss, AllPlanes, XYPixmap);
3580 loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3581 xpmJailSquare = xpmLightSquare;
3583 fprintf(stderr, _("Done.\n"));
3585 XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3588 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3592 CreateXPMBoard (char *s, int kind)
3596 if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3597 if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3598 useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3604 { // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3605 // thisroutine has to be called t free the old piece pixmaps
3607 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3608 for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3610 XFreePixmap(xDisplay, xpmLightSquare);
3611 XFreePixmap(xDisplay, xpmDarkSquare);
3620 u_int ss = squareSize;
3622 static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3623 XpmColorSymbol symbols[4];
3624 static int redo = False;
3626 if(redo) FreeXPMPieces(); else redo = 1;
3628 /* The XSynchronize calls were copied from CreatePieces.
3629 Not sure if needed, but can't hurt */
3630 XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3632 /* Setup translations so piece colors match square colors */
3633 symbols[0].name = "light_piece";
3634 symbols[0].value = appData.whitePieceColor;
3635 symbols[1].name = "dark_piece";
3636 symbols[1].value = appData.blackPieceColor;
3637 symbols[2].name = "light_square";
3638 symbols[2].value = appData.lightSquareColor;
3639 symbols[3].name = "dark_square";
3640 symbols[3].value = appData.darkSquareColor;
3642 attr.valuemask = XpmColorSymbols;
3643 attr.colorsymbols = symbols;
3644 attr.numsymbols = 4;
3646 if (appData.monoMode) {
3647 DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3651 if (strlen(appData.pixmapDirectory) == 0) {
3652 XpmPieces* pieces = builtInXpms;
3655 while (pieces->size != squareSize && pieces->size) pieces++;
3656 if (!pieces->size) {
3657 fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3660 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3661 for (kind=0; kind<4; kind++) {
3663 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3664 pieces->xpm[piece][kind],
3665 &(xpmPieceBitmap2[kind][piece]),
3666 NULL, &attr)) != 0) {
3667 fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3671 if(piece <= (int) WhiteKing)
3672 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3676 xpmJailSquare = xpmLightSquare;
3680 fprintf(stderr, _("\nLoading XPMs...\n"));
3683 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3684 fprintf(stderr, "%d ", piece+1);
3685 for (kind=0; kind<4; kind++) {
3686 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3687 ExpandPathName(appData.pixmapDirectory),
3688 piece > (int) WhiteKing ? "w" : "",
3689 pieceBitmapNames[piece],
3691 if (appData.debugMode) {
3692 fprintf(stderr, _("(File:%s:) "), buf);
3694 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3695 &(xpmPieceBitmap2[kind][piece]),
3696 NULL, &attr)) != 0) {
3697 if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3698 // [HGM] missing: read of unorthodox piece failed; substitute King.
3699 snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3700 ExpandPathName(appData.pixmapDirectory),
3702 if (appData.debugMode) {
3703 fprintf(stderr, _("(Replace by File:%s:) "), buf);
3705 r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3706 &(xpmPieceBitmap2[kind][piece]),
3710 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3715 if(piece <= (int) WhiteKing)
3716 xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3719 /* Load light and dark squares */
3720 /* If the LSQ and DSQ pieces don't exist, we will
3721 draw them with solid squares. */
3722 fprintf(stderr, _("light square "));
3723 snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3724 if (access(buf, 0) != 0) {
3728 if (appData.debugMode)
3729 fprintf(stderr, _("(File:%s:) "), buf);
3731 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3732 &xpmLightSquare, NULL, &attr)) != 0) {
3733 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3736 fprintf(stderr, _("dark square "));
3737 snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3738 ExpandPathName(appData.pixmapDirectory), ss);
3739 if (appData.debugMode) {
3740 fprintf(stderr, _("(File:%s:) "), buf);
3742 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3743 &xpmDarkSquare, NULL, &attr)) != 0) {
3744 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3748 xpmJailSquare = xpmLightSquare;
3749 fprintf(stderr, _("Done.\n"));
3751 oldVariant = -1; // kludge to force re-makig of animation masks
3752 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3755 #endif /* HAVE_LIBXPM */
3758 /* No built-in bitmaps */
3763 u_int ss = squareSize;
3765 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3768 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3769 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3770 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3771 pieceBitmapNames[piece],
3772 ss, kind == SOLID ? 's' : 'o');
3773 ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3774 if(piece <= (int)WhiteKing)
3775 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3779 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3783 /* With built-in bitmaps */
3787 BuiltInBits* bib = builtInBits;
3790 u_int ss = squareSize;
3792 XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3795 while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3797 for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3798 for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3799 snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3800 pieceBitmapNames[piece],
3801 ss, kind == SOLID ? 's' : 'o');
3802 ReadBitmap(&pieceBitmap2[kind][piece], buf,
3803 bib->bits[kind][piece], ss, ss);
3804 if(piece <= (int)WhiteKing)
3805 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3809 XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3815 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
3820 char msg[MSG_SIZ], fullname[MSG_SIZ];
3822 if (*appData.bitmapDirectory != NULLCHAR) {
3823 safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3824 strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3825 strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3826 errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3827 &w, &h, pm, &x_hot, &y_hot);
3828 fprintf(stderr, "load %s\n", name);
3829 if (errcode != BitmapSuccess) {
3831 case BitmapOpenFailed:
3832 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3834 case BitmapFileInvalid:
3835 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3837 case BitmapNoMemory:
3838 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3842 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3846 fprintf(stderr, _("%s: %s...using built-in\n"),
3848 } else if (w != wreq || h != hreq) {
3850 _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3851 programName, fullname, w, h, wreq, hreq);
3857 *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3867 if (lineGap == 0) return;
3869 /* [HR] Split this into 2 loops for non-square boards. */
3871 for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3872 gridSegments[i].x1 = 0;
3873 gridSegments[i].x2 =
3874 lineGap + BOARD_WIDTH * (squareSize + lineGap);
3875 gridSegments[i].y1 = gridSegments[i].y2
3876 = lineGap / 2 + (i * (squareSize + lineGap));
3879 for (j = 0; j < BOARD_WIDTH + 1; j++) {
3880 gridSegments[j + i].y1 = 0;
3881 gridSegments[j + i].y2 =
3882 lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3883 gridSegments[j + i].x1 = gridSegments[j + i].x2
3884 = lineGap / 2 + (j * (squareSize + lineGap));
3889 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3891 XtActionProc proc = (XtActionProc) addr;
3893 (proc)(NULL, NULL, NULL, NULL);
3897 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3899 RecentEngineEvent((int) addr);
3903 AppendEnginesToMenu (Widget menu, char *list)
3911 if(appData.recentEngines <= 0) return;
3912 recentEngines = strdup(list);
3914 XtSetArg(args[j], XtNleftMargin, 20); j++;
3915 XtSetArg(args[j], XtNrightMargin, 20); j++;
3917 p = strchr(list, '\n'); if(p == NULL) break;
3918 if(i == 0) XtCreateManagedWidget(_("----"), smeLineObjectClass, menu, args, j); // at least one valid item to add
3920 XtSetArg(args[j], XtNlabel, XtNewString(list));
3921 entry = XtCreateManagedWidget("engine", smeBSBObjectClass, menu, args, j+1);
3922 XtAddCallback(entry, XtNcallback,
3923 (XtCallbackProc) MenuEngineSelect,
3925 i++; *p = '\n'; list = p + 1;
3930 CreateMenuBarPopup (Widget parent, String name, Menu *mb)
3937 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3940 XtSetArg(args[j], XtNleftMargin, 20); j++;
3941 XtSetArg(args[j], XtNrightMargin, 20); j++;
3943 while (mi->string != NULL) {
3944 if (strcmp(mi->string, "----") == 0) {
3945 entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
3948 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3949 entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3951 XtAddCallback(entry, XtNcallback,
3952 (XtCallbackProc) MenuBarSelect,
3953 (caddr_t) mi->proc);
3957 if(!strcmp(mb->name, "Engine")) AppendEnginesToMenu(menu, appData.recentEngineList);
3961 CreateMenuBar (Menu *mb, int boardWidth)
3963 int i, j, nr = 0, wtot = 0, widths[10];
3966 char menuName[MSG_SIZ];
3971 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3972 XtSetArg(args[j], XtNvSpace, 0); j++;
3973 XtSetArg(args[j], XtNborderWidth, 0); j++;
3974 menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3975 formWidget, args, j);
3977 while (mb->name != NULL) {
3978 safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3979 strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3981 XtSetArg(args[j], XtNmenuName, XtNewString(menuName)); j++;
3982 XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3983 XtSetArg(args[j], XtNborderWidth, 0); j++;
3984 mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3986 CreateMenuBarPopup(menuBar, menuName, mb);
3988 XtSetArg(args[j], XtNwidth, &w); j++;
3989 XtGetValues(mb->subMenu, args, j);
3990 wtot += mb->textWidth = widths[nr++] = w;
3993 while(wtot > boardWidth - 40) {
3995 for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3999 for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
4001 XtSetArg(args[j], XtNwidth, widths[i]); j++;
4002 XtSetValues(ma[i].subMenu, args, j);
4008 CreateButtonBar (MenuItem *mi)
4011 Widget button, buttonBar;
4015 XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
4017 XtSetArg(args[j], XtNhSpace, 0); j++;
4019 XtSetArg(args[j], XtNborderWidth, 0); j++;
4020 XtSetArg(args[j], XtNvSpace, 0); j++;
4021 buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
4022 formWidget, args, j);
4024 while (mi->string != NULL) {
4027 XtSetArg(args[j], XtNinternalWidth, 2); j++;
4028 XtSetArg(args[j], XtNborderWidth, 0); j++;
4030 XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
4031 button = XtCreateManagedWidget(mi->string, commandWidgetClass,
4032 buttonBar, args, j);
4033 XtAddCallback(button, XtNcallback,
4034 (XtCallbackProc) MenuBarSelect,
4035 (caddr_t) mi->proc);
4042 CreatePieceMenu (char *name, int color)
4047 ChessSquare selection;
4049 menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
4050 boardWidget, args, 0);
4052 for (i = 0; i < PIECE_MENU_SIZE; i++) {
4053 String item = pieceMenuStrings[color][i];
4055 if (strcmp(item, "----") == 0) {
4056 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4059 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4060 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4062 selection = pieceMenuTranslation[color][i];
4063 XtAddCallback(entry, XtNcallback,
4064 (XtCallbackProc) PieceMenuSelect,
4065 (caddr_t) selection);
4066 if (selection == WhitePawn || selection == BlackPawn) {
4067 XtSetArg(args[0], XtNpopupOnEntry, entry);
4068 XtSetValues(menu, args, 1);
4081 ChessSquare selection;
4083 whitePieceMenu = CreatePieceMenu("menuW", 0);
4084 blackPieceMenu = CreatePieceMenu("menuB", 1);
4086 if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
4087 XtRegisterGrabAction(PieceMenuPopup, True,
4088 (unsigned)(ButtonPressMask|ButtonReleaseMask),
4089 GrabModeAsync, GrabModeAsync);
4091 XtSetArg(args[0], XtNlabel, _("Drop"));
4092 dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
4093 boardWidget, args, 1);
4094 for (i = 0; i < DROP_MENU_SIZE; i++) {
4095 String item = dropMenuStrings[i];
4097 if (strcmp(item, "----") == 0) {
4098 entry = XtCreateManagedWidget(item, smeLineObjectClass,
4101 XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
4102 entry = XtCreateManagedWidget(item, smeBSBObjectClass,
4104 selection = dropMenuTranslation[i];
4105 XtAddCallback(entry, XtNcallback,
4106 (XtCallbackProc) DropMenuSelect,
4107 (caddr_t) selection);
4121 for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
4122 entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
4123 p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
4124 dmEnables[i].piece);
4125 XtSetSensitive(entry, p != NULL || !appData.testLegality
4126 /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
4127 && !appData.icsActive));
4129 while (p && *p++ == dmEnables[i].piece) count++;
4130 snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count);
4132 XtSetArg(args[j], XtNlabel, label); j++;
4133 XtSetValues(entry, args, j);
4138 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
4140 String whichMenu; int menuNr = -2;
4141 shiftKey = strcmp(params[0], "menuW"); // used to indicate black
4142 if (event->type == ButtonRelease)
4143 menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4144 else if (event->type == ButtonPress)
4145 menuNr = RightClick(Press, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
4147 case 0: whichMenu = params[0]; break;
4148 case 1: SetupDropMenu(); whichMenu = "menuD"; break;
4150 case -1: if (errorUp) ErrorPopDown();
4153 XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
4157 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4159 if (pmFromX < 0 || pmFromY < 0) return;
4160 EditPositionMenuEvent(piece, pmFromX, pmFromY);
4164 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
4166 if (pmFromX < 0 || pmFromY < 0) return;
4167 DropMenuEvent(piece, pmFromX, pmFromY);
4171 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4173 shiftKey = prms[0][0] & 1;
4178 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4180 shiftKey = prms[0][0] & 1;
4186 * If the user selects on a border boundary, return -1; if off the board,
4187 * return -2. Otherwise map the event coordinate to the square.
4190 EventToSquare (int x, int limit)
4197 if ((x % (squareSize + lineGap)) >= squareSize)
4199 x /= (squareSize + lineGap);
4206 do_flash_delay (unsigned long msec)
4212 drawHighlight (int file, int rank, GC gc)
4216 if (lineGap == 0) return;
4219 x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4220 (squareSize + lineGap);
4221 y = lineGap/2 + rank * (squareSize + lineGap);
4223 x = lineGap/2 + file * (squareSize + lineGap);
4224 y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4225 (squareSize + lineGap);
4228 XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4229 squareSize+lineGap, squareSize+lineGap);
4232 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4233 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4236 SetHighlights (int fromX, int fromY, int toX, int toY)
4238 if (hi1X != fromX || hi1Y != fromY) {
4239 if (hi1X >= 0 && hi1Y >= 0) {
4240 drawHighlight(hi1X, hi1Y, lineGC);
4242 } // [HGM] first erase both, then draw new!
4243 if (hi2X != toX || hi2Y != toY) {
4244 if (hi2X >= 0 && hi2Y >= 0) {
4245 drawHighlight(hi2X, hi2Y, lineGC);
4248 if (hi1X != fromX || hi1Y != fromY) {
4249 if (fromX >= 0 && fromY >= 0) {
4250 drawHighlight(fromX, fromY, highlineGC);
4253 if (hi2X != toX || hi2Y != toY) {
4254 if (toX >= 0 && toY >= 0) {
4255 drawHighlight(toX, toY, highlineGC);
4258 if(toX<0) // clearing the highlights must have damaged arrow
4259 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
4269 SetHighlights(-1, -1, -1, -1);
4274 SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
4276 if (pm1X != fromX || pm1Y != fromY) {
4277 if (pm1X >= 0 && pm1Y >= 0) {
4278 drawHighlight(pm1X, pm1Y, lineGC);
4280 if (fromX >= 0 && fromY >= 0) {
4281 drawHighlight(fromX, fromY, prelineGC);
4284 if (pm2X != toX || pm2Y != toY) {
4285 if (pm2X >= 0 && pm2Y >= 0) {
4286 drawHighlight(pm2X, pm2Y, lineGC);
4288 if (toX >= 0 && toY >= 0) {
4289 drawHighlight(toX, toY, prelineGC);
4299 ClearPremoveHighlights ()
4301 SetPremoveHighlights(-1, -1, -1, -1);
4305 CutOutSquare (int x, int y, int *x0, int *y0, int kind)
4307 int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4308 int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4310 if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4311 if(textureW[kind] < W*squareSize)
4312 *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4314 *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4315 if(textureH[kind] < H*squareSize)
4316 *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4318 *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4323 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
4324 { // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4326 if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4327 XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4328 squareSize, squareSize, x*fac, y*fac);
4330 if (useImages && useImageSqs) {
4334 pm = xpmLightSquare;
4339 case 2: /* neutral */
4344 XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4345 squareSize, squareSize, x*fac, y*fac);
4355 case 2: /* neutral */
4360 XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4365 I split out the routines to draw a piece so that I could
4366 make a generic flash routine.
4369 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4371 /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4372 switch (square_color) {
4374 case 2: /* neutral */
4376 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4377 ? *pieceToOutline(piece)
4378 : *pieceToSolid(piece),
4379 dest, bwPieceGC, 0, 0,
4380 squareSize, squareSize, x, y);
4383 XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4384 ? *pieceToSolid(piece)
4385 : *pieceToOutline(piece),
4386 dest, wbPieceGC, 0, 0,
4387 squareSize, squareSize, x, y);
4393 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4395 switch (square_color) {
4397 case 2: /* neutral */
4399 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4400 ? *pieceToOutline(piece)
4401 : *pieceToSolid(piece),
4402 dest, bwPieceGC, 0, 0,
4403 squareSize, squareSize, x, y, 1);
4406 XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4407 ? *pieceToSolid(piece)
4408 : *pieceToOutline(piece),
4409 dest, wbPieceGC, 0, 0,
4410 squareSize, squareSize, x, y, 1);
4416 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4418 if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4419 switch (square_color) {
4421 XCopyPlane(xDisplay, *pieceToSolid(piece),
4422 dest, (int) piece < (int) BlackPawn
4423 ? wlPieceGC : blPieceGC, 0, 0,
4424 squareSize, squareSize, x, y, 1);
4427 XCopyPlane(xDisplay, *pieceToSolid(piece),
4428 dest, (int) piece < (int) BlackPawn
4429 ? wdPieceGC : bdPieceGC, 0, 0,
4430 squareSize, squareSize, x, y, 1);
4432 case 2: /* neutral */
4434 XCopyPlane(xDisplay, *pieceToSolid(piece),
4435 dest, (int) piece < (int) BlackPawn
4436 ? wjPieceGC : bjPieceGC, 0, 0,
4437 squareSize, squareSize, x, y, 1);
4443 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
4445 int kind, p = piece;
4447 switch (square_color) {
4449 case 2: /* neutral */
4451 if ((int)piece < (int) BlackPawn) {
4459 if ((int)piece < (int) BlackPawn) {
4467 if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4468 if(useTexture & square_color+1) {
4469 BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4470 XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4471 XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4472 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4473 XSetClipMask(xDisplay, wlPieceGC, None);
4474 XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4476 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4477 dest, wlPieceGC, 0, 0,
4478 squareSize, squareSize, x, y);
4481 typedef void (*DrawFunc)();
4486 if (appData.monoMode) {
4487 if (DefaultDepth(xDisplay, xScreen) == 1) {
4488 return monoDrawPiece_1bit;
4490 return monoDrawPiece;
4494 return colorDrawPieceImage;
4496 return colorDrawPiece;
4500 /* [HR] determine square color depending on chess variant. */
4502 SquareColor (int row, int column)
4506 if (gameInfo.variant == VariantXiangqi) {
4507 if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4509 } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4511 } else if (row <= 4) {
4517 square_color = ((column + row) % 2) == 1;
4520 /* [hgm] holdings: next line makes all holdings squares light */
4521 if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4523 return square_color;
4527 DrawSquare (int row, int column, ChessSquare piece, int do_flash)
4529 int square_color, x, y, direction, font_ascent, font_descent;
4532 XCharStruct overall;
4536 /* Calculate delay in milliseconds (2-delays per complete flash) */
4537 flash_delay = 500 / appData.flashRate;
4540 x = lineGap + ((BOARD_WIDTH-1)-column) *
4541 (squareSize + lineGap);
4542 y = lineGap + row * (squareSize + lineGap);
4544 x = lineGap + column * (squareSize + lineGap);
4545 y = lineGap + ((BOARD_HEIGHT-1)-row) *
4546 (squareSize + lineGap);
4549 if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4551 square_color = SquareColor(row, column);
4553 if ( // [HGM] holdings: blank out area between board and holdings
4554 column == BOARD_LEFT-1 || column == BOARD_RGHT
4555 || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4556 || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4557 BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4559 // [HGM] print piece counts next to holdings
4560 string[1] = NULLCHAR;
4561 if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4562 string[0] = '0' + piece;
4563 XTextExtents(countFontStruct, string, 1, &direction,
4564 &font_ascent, &font_descent, &overall);
4565 if (appData.monoMode) {
4566 XDrawImageString(xDisplay, xBoardWindow, countGC,
4567 x + squareSize - overall.width - 2,
4568 y + font_ascent + 1, string, 1);
4570 XDrawString(xDisplay, xBoardWindow, countGC,
4571 x + squareSize - overall.width - 2,
4572 y + font_ascent + 1, string, 1);
4575 if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4576 string[0] = '0' + piece;
4577 XTextExtents(countFontStruct, string, 1, &direction,
4578 &font_ascent, &font_descent, &overall);
4579 if (appData.monoMode) {
4580 XDrawImageString(xDisplay, xBoardWindow, countGC,
4581 x + 2, y + font_ascent + 1, string, 1);
4583 XDrawString(xDisplay, xBoardWindow, countGC,
4584 x + 2, y + font_ascent + 1, string, 1);
4588 if (piece == EmptySquare || appData.blindfold) {
4589 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4591 drawfunc = ChooseDrawFunc();
4593 if (do_flash && appData.flashCount > 0) {
4594 for (i=0; i<appData.flashCount; ++i) {
4595 drawfunc(piece, square_color, x, y, xBoardWindow);
4596 XSync(xDisplay, False);
4597 do_flash_delay(flash_delay);
4599 BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4600 XSync(xDisplay, False);
4601 do_flash_delay(flash_delay);
4604 drawfunc(piece, square_color, x, y, xBoardWindow);
4608 string[1] = NULLCHAR;
4609 if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4610 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4611 string[0] = 'a' + column - BOARD_LEFT;
4612 XTextExtents(coordFontStruct, string, 1, &direction,
4613 &font_ascent, &font_descent, &overall);
4614 if (appData.monoMode) {
4615 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4616 x + squareSize - overall.width - 2,
4617 y + squareSize - font_descent - 1, string, 1);
4619 XDrawString(xDisplay, xBoardWindow, coordGC,
4620 x + squareSize - overall.width - 2,
4621 y + squareSize - font_descent - 1, string, 1);
4624 if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4625 string[0] = ONE + row;
4626 XTextExtents(coordFontStruct, string, 1, &direction,
4627 &font_ascent, &font_descent, &overall);
4628 if (appData.monoMode) {
4629 XDrawImageString(xDisplay, xBoardWindow, coordGC,
4630 x + 2, y + font_ascent + 1, string, 1);
4632 XDrawString(xDisplay, xBoardWindow, coordGC,
4633 x + 2, y + font_ascent + 1, string, 1);
4636 if(!partnerUp && marker[row][column]) {
4637 if(appData.monoMode) {
4638 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
4639 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4640 XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
4641 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4643 XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4644 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4649 /* Why is this needed on some versions of X? */
4651 EventProc (Widget widget, caddr_t unused, XEvent *event)
4653 if (!XtIsRealized(widget))
4656 switch (event->type) {
4658 if (event->xexpose.count > 0) return; /* no clipping is done */
4659 XDrawPosition(widget, True, NULL);
4660 if(twoBoards) { // [HGM] dual: draw other board in other orientation
4661 flipView = !flipView; partnerUp = !partnerUp;
4662 XDrawPosition(widget, True, NULL);
4663 flipView = !flipView; partnerUp = !partnerUp;
4667 if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4675 DrawPosition (int fullRedraw, Board board)
4677 XDrawPosition(boardWidget, fullRedraw, board);
4680 /* Returns 1 if there are "too many" differences between b1 and b2
4681 (i.e. more than 1 move was made) */
4683 too_many_diffs (Board b1, Board b2)
4688 for (i=0; i<BOARD_HEIGHT; ++i) {
4689 for (j=0; j<BOARD_WIDTH; ++j) {
4690 if (b1[i][j] != b2[i][j]) {
4691 if (++c > 4) /* Castling causes 4 diffs */
4699 /* Matrix describing castling maneuvers */
4700 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4701 static int castling_matrix[4][5] = {
4702 { 0, 0, 4, 3, 2 }, /* 0-0-0, white */
4703 { 0, 7, 4, 5, 6 }, /* 0-0, white */
4704 { 7, 0, 4, 3, 2 }, /* 0-0-0, black */
4705 { 7, 7, 4, 5, 6 } /* 0-0, black */
4708 /* Checks whether castling occurred. If it did, *rrow and *rcol
4709 are set to the destination (row,col) of the rook that moved.
4711 Returns 1 if castling occurred, 0 if not.
4713 Note: Only handles a max of 1 castling move, so be sure
4714 to call too_many_diffs() first.
4717 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4722 /* For each type of castling... */
4723 for (i=0; i<4; ++i) {
4724 r = castling_matrix[i];
4726 /* Check the 4 squares involved in the castling move */
4728 for (j=1; j<=4; ++j) {
4729 if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4736 /* All 4 changed, so it must be a castling move */
4745 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4747 DrawSeekAxis (int x, int y, int xTo, int yTo)
4749 XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4753 DrawSeekBackground (int left, int top, int right, int bottom)
4755 XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4759 DrawSeekText (char *buf, int x, int y)
4761 XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4765 DrawSeekDot (int x, int y, int colorNr)
4767 int square = colorNr & 0x80;
4770 color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4772 XFillRectangle(xDisplay, xBoardWindow, color,
4773 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4775 XFillArc(xDisplay, xBoardWindow, color,
4776 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4779 static int damage[2][BOARD_RANKS][BOARD_FILES];
4782 * event handler for redrawing the board
4785 XDrawPosition (Widget w, int repaint, Board board)
4788 static int lastFlipView = 0;
4789 static int lastBoardValid[2] = {0, 0};
4790 static Board lastBoard[2];
4793 int nr = twoBoards*partnerUp;
4795 if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4797 if (board == NULL) {
4798 if (!lastBoardValid[nr]) return;
4799 board = lastBoard[nr];
4801 if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4802 XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4803 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4808 * It would be simpler to clear the window with XClearWindow()
4809 * but this causes a very distracting flicker.
4812 if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4814 if ( lineGap && IsDrawArrowEnabled())
4815 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4816 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4818 /* If too much changes (begin observing new game, etc.), don't
4820 do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4822 /* Special check for castling so we don't flash both the king
4823 and the rook (just flash the king). */
4825 if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4826 /* Draw rook with NO flashing. King will be drawn flashing later */
4827 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4828 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4832 /* First pass -- Draw (newly) empty squares and repair damage.
4833 This prevents you from having a piece show up twice while it
4834 is flashing on its new square */
4835 for (i = 0; i < BOARD_HEIGHT; i++)
4836 for (j = 0; j < BOARD_WIDTH; j++)
4837 if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4838 || damage[nr][i][j]) {
4839 DrawSquare(i, j, board[i][j], 0);
4840 damage[nr][i][j] = False;
4843 /* Second pass -- Draw piece(s) in new position and flash them */
4844 for (i = 0; i < BOARD_HEIGHT; i++)
4845 for (j = 0; j < BOARD_WIDTH; j++)
4846 if (board[i][j] != lastBoard[nr][i][j]) {
4847 DrawSquare(i, j, board[i][j], do_flash);
4851 XDrawSegments(xDisplay, xBoardWindow, lineGC,
4852 twoBoards & partnerUp ? secondSegments : // [HGM] dual
4853 gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4855 for (i = 0; i < BOARD_HEIGHT; i++)
4856 for (j = 0; j < BOARD_WIDTH; j++) {
4857 DrawSquare(i, j, board[i][j], 0);
4858 damage[nr][i][j] = False;
4862 CopyBoard(lastBoard[nr], board);
4863 lastBoardValid[nr] = 1;
4864 if(nr == 0) { // [HGM] dual: no highlights on second board yet
4865 lastFlipView = flipView;
4867 /* Draw highlights */
4868 if (pm1X >= 0 && pm1Y >= 0) {
4869 drawHighlight(pm1X, pm1Y, prelineGC);
4871 if (pm2X >= 0 && pm2Y >= 0) {
4872 drawHighlight(pm2X, pm2Y, prelineGC);
4874 if (hi1X >= 0 && hi1Y >= 0) {
4875 drawHighlight(hi1X, hi1Y, highlineGC);
4877 if (hi2X >= 0 && hi2Y >= 0) {
4878 drawHighlight(hi2X, hi2Y, highlineGC);
4880 DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4882 /* If piece being dragged around board, must redraw that too */
4885 XSync(xDisplay, False);
4890 * event handler for redrawing the board
4893 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4895 XDrawPosition(w, True, NULL);
4900 * event handler for parsing user moves
4902 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4903 // way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4904 // it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4905 // should be made to use the new way, of calling UserMoveTest early to determine the legality of the
4906 // move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4907 // and at the end FinishMove() to perform the move after optional promotion popups.
4908 // For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4910 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4912 if (w != boardWidget || errorExitStatus != -1) return;
4913 if(nprms) shiftKey = !strcmp(prms[0], "1");
4916 if (event->type == ButtonPress) {
4917 XtPopdown(promotionShell);
4918 XtDestroyWidget(promotionShell);
4919 promotionUp = False;
4927 // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4928 if(event->type == ButtonPress) LeftClick(Press, event->xbutton.x, event->xbutton.y);
4929 if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4933 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
4935 if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4936 DragPieceMove(event->xmotion.x, event->xmotion.y);
4940 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
4941 { // [HGM] pv: walk PV
4942 MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4945 static int savedIndex; /* gross that this is global */
4948 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4951 XawTextPosition index, dummy;
4954 XawTextGetSelectionPos(w, &index, &dummy);
4955 XtSetArg(arg, XtNstring, &val);
4956 XtGetValues(w, &arg, 1);
4957 ReplaceComment(savedIndex, val);
4958 if(savedIndex != currentMove) ToNrEvent(savedIndex);
4959 LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4963 EditCommentPopUp (int index, char *title, char *text)
4966 if (text == NULL) text = "";
4967 NewCommentPopup(title, text, index);
4976 extern Option boxOptions[];
4986 edit = boxOptions[0].handle;
4988 XtSetArg(args[j], XtNstring, &val); j++;
4989 XtGetValues(edit, args, j);
4991 SendMultiLineToICS(val);
4992 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4993 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4997 ICSInputBoxPopDown ()
5003 CommentPopUp (char *title, char *text)
5005 savedIndex = currentMove; // [HGM] vari
5006 NewCommentPopup(title, text, currentMove);
5015 static char *openName;
5021 (void) (*fileProc)(openFP, 0, openName);
5025 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
5027 fileProc = proc; /* I can't see a way not */
5028 fileOpenMode = openMode; /* to use globals here */
5029 { // [HGM] use file-selector dialog stolen from Ghostview
5030 int index; // this is not supported yet
5031 if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5032 (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5033 // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5034 ScheduleDelayedEvent(&DelayedLoad, 50);
5041 if (!filenameUp) return;
5042 XtPopdown(fileNameShell);
5043 XtDestroyWidget(fileNameShell);
5049 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5054 XtSetArg(args[0], XtNlabel, &name);
5055 XtGetValues(w, args, 1);
5057 if (strcmp(name, _("cancel")) == 0) {
5062 FileNameAction(w, NULL, NULL, NULL);
5066 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5074 name = XawDialogGetValueString(w = XtParent(w));
5076 if ((name != NULL) && (*name != NULLCHAR)) {
5077 safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5078 XtPopdown(w = XtParent(XtParent(w)));
5082 p = strrchr(buf, ' ');
5089 fullname = ExpandPathName(buf);
5091 ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5094 f = fopen(fullname, fileOpenMode);
5096 DisplayError(_("Failed to open file"), errno);
5098 (void) (*fileProc)(f, index, buf);
5105 XtPopdown(w = XtParent(XtParent(w)));
5115 Widget dialog, layout;
5117 Dimension bw_width, pw_width;
5119 char *PromoChars = "wglcqrbnkac+=\0";
5122 XtSetArg(args[j], XtNwidth, &bw_width); j++;
5123 XtGetValues(boardWidget, args, j);
5126 XtSetArg(args[j], XtNresizable, True); j++;
5127 XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5129 XtCreatePopupShell("Promotion", transientShellWidgetClass,
5130 shellWidget, args, j);
5132 XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5133 layoutArgs, XtNumber(layoutArgs));
5136 XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5137 XtSetArg(args[j], XtNborderWidth, 0); j++;
5138 dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5141 if(gameInfo.variant != VariantShogi) {
5142 if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5143 XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5144 XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5145 XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5146 XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5148 XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5149 XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5150 XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5151 XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5153 if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5154 gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5155 gameInfo.variant == VariantGiveaway) {
5156 XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5158 if(gameInfo.variant == VariantCapablanca ||
5159 gameInfo.variant == VariantGothic ||
5160 gameInfo.variant == VariantCapaRandom) {
5161 XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5162 XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5164 } else // [HGM] shogi
5166 XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5167 XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5169 XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5171 XtRealizeWidget(promotionShell);
5172 CatchDeleteWindow(promotionShell, "PromotionPopDown");
5175 XtSetArg(args[j], XtNwidth, &pw_width); j++;
5176 XtGetValues(promotionShell, args, j);
5178 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5179 lineGap + squareSize/3 +
5180 ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5181 0 : 6*(squareSize + lineGap)), &x, &y);
5184 XtSetArg(args[j], XtNx, x); j++;
5185 XtSetArg(args[j], XtNy, y); j++;
5186 XtSetValues(promotionShell, args, j);
5188 XtPopup(promotionShell, XtGrabNone);
5196 if (!promotionUp) return;
5197 XtPopdown(promotionShell);
5198 XtDestroyWidget(promotionShell);
5199 promotionUp = False;
5203 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5205 int promoChar = * (const char *) client_data;
5209 if (fromX == -1) return;
5216 UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5218 if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5219 if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5225 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5227 dialogError = errorUp = False;
5228 XtPopdown(w = XtParent(XtParent(XtParent(w))));
5230 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5237 if (!errorUp) return;
5238 dialogError = errorUp = False;
5239 XtPopdown(errorShell);
5240 XtDestroyWidget(errorShell);
5241 if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5245 ErrorPopUp (char *title, char *label, int modal)
5248 Widget dialog, layout;
5252 Dimension bw_width, pw_width;
5253 Dimension pw_height;
5257 XtSetArg(args[i], XtNresizable, True); i++;
5258 XtSetArg(args[i], XtNtitle, title); i++;
5260 XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5261 shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
5263 XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5264 layoutArgs, XtNumber(layoutArgs));
5267 XtSetArg(args[i], XtNlabel, label); i++;
5268 XtSetArg(args[i], XtNborderWidth, 0); i++;
5269 dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5272 XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5274 XtRealizeWidget(errorShell);
5275 CatchDeleteWindow(errorShell, "ErrorPopDown");
5278 XtSetArg(args[i], XtNwidth, &bw_width); i++;
5279 XtGetValues(boardWidget, args, i);
5281 XtSetArg(args[i], XtNwidth, &pw_width); i++;
5282 XtSetArg(args[i], XtNheight, &pw_height); i++;
5283 XtGetValues(errorShell, args, i);
5286 /* This code seems to tickle an X bug if it is executed too soon
5287 after xboard starts up. The coordinates get transformed as if
5288 the main window was positioned at (0, 0).
5290 XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5291 0 - pw_height + squareSize / 3, &x, &y);
5293 XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5294 RootWindowOfScreen(XtScreen(boardWidget)),
5295 (bw_width - pw_width) / 2,
5296 0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5300 if (y < 0) y = 0; /*avoid positioning top offscreen*/
5303 XtSetArg(args[i], XtNx, x); i++;
5304 XtSetArg(args[i], XtNy, y); i++;
5305 XtSetValues(errorShell, args, i);
5308 XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5311 /* Disable all user input other than deleting the window */
5312 static int frozen = 0;
5318 /* Grab by a widget that doesn't accept input */
5319 XtAddGrab(messageWidget, TRUE, FALSE);
5323 /* Undo a FreezeUI */
5327 if (!frozen) return;
5328 XtRemoveGrab(messageWidget);
5333 ModeToWidgetName (GameMode mode)
5336 case BeginningOfGame:
5337 if (appData.icsActive)
5338 return "menuMode.ICS Client";
5339 else if (appData.noChessProgram ||
5340 *appData.cmailGameName != NULLCHAR)
5341 return "menuMode.Edit Game";
5343 return "menuMode.Machine Black";
5344 case MachinePlaysBlack:
5345 return "menuMode.Machine Black";
5346 case MachinePlaysWhite:
5347 return "menuMode.Machine White";
5349 return "menuMode.Analysis Mode";
5351 return "menuMode.Analyze File";
5352 case TwoMachinesPlay:
5353 return "menuMode.Two Machines";
5355 return "menuMode.Edit Game";
5356 case PlayFromGameFile:
5357 return "menuFile.Load Game";
5359 return "menuMode.Edit Position";
5361 return "menuMode.Training";
5362 case IcsPlayingWhite:
5363 case IcsPlayingBlack:
5367 return "menuMode.ICS Client";
5378 static int oldPausing = FALSE;
5379 static GameMode oldmode = (GameMode) -1;
5382 if (!boardWidget || !XtIsRealized(boardWidget)) return;
5384 if (pausing != oldPausing) {
5385 oldPausing = pausing;
5387 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5389 XtSetArg(args[0], XtNleftBitmap, None);
5391 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5394 if (appData.showButtonBar) {
5395 /* Always toggle, don't set. Previous code messes up when
5396 invoked while the button is pressed, as releasing it
5397 toggles the state again. */
5400 XtSetArg(args[0], XtNbackground, &oldbg);
5401 XtSetArg(args[1], XtNforeground, &oldfg);
5402 XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5404 XtSetArg(args[0], XtNbackground, oldfg);
5405 XtSetArg(args[1], XtNforeground, oldbg);
5407 XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5411 wname = ModeToWidgetName(oldmode);
5412 if (wname != NULL) {
5413 XtSetArg(args[0], XtNleftBitmap, None);
5414 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5416 wname = ModeToWidgetName(gameMode);
5417 if (wname != NULL) {
5418 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5419 XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5422 XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5423 XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5425 /* Maybe all the enables should be handled here, not just this one */
5426 XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5427 gameMode == Training || gameMode == PlayFromGameFile);
5432 * Button/menu procedures
5435 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5441 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5443 cmailMsgLoaded = FALSE;
5444 if (gameNumber == 0) {
5445 int error = GameListBuild(f);
5447 DisplayError(_("Cannot build game list"), error);
5448 } else if (!ListEmpty(&gameList) &&
5449 ((ListGame *) gameList.tailPred)->number > 1) {
5450 GameListPopUp(f, title);
5456 return LoadGame(f, gameNumber, title, FALSE);
5460 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5462 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5465 FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5469 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5475 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5481 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5487 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5493 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5499 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5505 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5507 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5510 FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5514 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5516 FileNamePopUp(_("Save game file name?"),
5517 DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5518 appData.oldSaveStyle ? ".game" : ".pgn",
5523 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5525 FileNamePopUp(_("Save position file name?"),
5526 DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5527 appData.oldSaveStyle ? ".pos" : ".fen",
5532 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5534 ReloadCmailMsgEvent(FALSE);
5538 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5543 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5544 char *selected_fen_position=NULL;
5547 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5548 Atom *type_return, XtPointer *value_return,
5549 unsigned long *length_return, int *format_return)
5551 char *selection_tmp;
5553 if (!selected_fen_position) return False; /* should never happen */
5554 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5555 /* note: since no XtSelectionDoneProc was registered, Xt will
5556 * automatically call XtFree on the value returned. So have to
5557 * make a copy of it allocated with XtMalloc */
5558 selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5559 safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5561 *value_return=selection_tmp;
5562 *length_return=strlen(selection_tmp);
5563 *type_return=*target;
5564 *format_return = 8; /* bits per byte */
5566 } else if (*target == XA_TARGETS(xDisplay)) {
5567 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5568 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5569 targets_tmp[1] = XA_STRING;
5570 *value_return = targets_tmp;
5571 *type_return = XA_ATOM;
5574 // This code leads to a read of value_return out of bounds on 64-bit systems.
5575 // Other code which I have seen always sets *format_return to 32 independent of
5576 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5577 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5578 *format_return = 8 * sizeof(Atom);
5579 if (*format_return > 32) {
5580 *length_return *= *format_return / 32;
5581 *format_return = 32;
5584 *format_return = 32;
5592 /* note: when called from menu all parameters are NULL, so no clue what the
5593 * Widget which was clicked on was, or what the click event was
5596 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5599 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5600 * have a notion of a position that is selected but not copied.
5601 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5603 if(gameMode == EditPosition) EditPositionDone(TRUE);
5604 if (selected_fen_position) free(selected_fen_position);
5605 selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5606 if (!selected_fen_position) return;
5607 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5609 SendPositionSelection,
5610 NULL/* lose_ownership_proc */ ,
5611 NULL/* transfer_done_proc */);
5612 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5614 SendPositionSelection,
5615 NULL/* lose_ownership_proc */ ,
5616 NULL/* transfer_done_proc */);
5620 CopyFENToClipboard ()
5621 { // wrapper to make call from back-end possible
5622 CopyPositionProc(NULL, NULL, NULL, NULL);
5625 /* function called when the data to Paste is ready */
5627 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5628 Atom *type, XtPointer value, unsigned long *len, int *format)
5631 if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5632 fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5633 EditPositionPasteFEN(fenstr);
5637 /* called when Paste Position button is pressed,
5638 * all parameters will be NULL */
5639 void PastePositionProc(w, event, prms, nprms)
5645 XtGetSelectionValue(menuBarWidget,
5646 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5647 /* (XtSelectionCallbackProc) */ PastePositionCB,
5648 NULL, /* client_data passed to PastePositionCB */
5650 /* better to use the time field from the event that triggered the
5651 * call to this function, but that isn't trivial to get
5659 SendGameSelection (Widget w, Atom *selection, Atom *target,
5660 Atom *type_return, XtPointer *value_return,
5661 unsigned long *length_return, int *format_return)
5663 char *selection_tmp;
5665 if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5666 FILE* f = fopen(gameCopyFilename, "r");
5669 if (f == NULL) return False;
5673 selection_tmp = XtMalloc(len + 1);
5674 count = fread(selection_tmp, 1, len, f);
5677 XtFree(selection_tmp);
5680 selection_tmp[len] = NULLCHAR;
5681 *value_return = selection_tmp;
5682 *length_return = len;
5683 *type_return = *target;
5684 *format_return = 8; /* bits per byte */
5686 } else if (*target == XA_TARGETS(xDisplay)) {
5687 Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5688 targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5689 targets_tmp[1] = XA_STRING;
5690 *value_return = targets_tmp;
5691 *type_return = XA_ATOM;
5694 // This code leads to a read of value_return out of bounds on 64-bit systems.
5695 // Other code which I have seen always sets *format_return to 32 independent of
5696 // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5697 // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5698 *format_return = 8 * sizeof(Atom);
5699 if (*format_return > 32) {
5700 *length_return *= *format_return / 32;
5701 *format_return = 32;
5704 *format_return = 32;
5716 * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5717 * have a notion of a game that is selected but not copied.
5718 * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5720 XtOwnSelection(menuBarWidget, XA_PRIMARY,
5723 NULL/* lose_ownership_proc */ ,
5724 NULL/* transfer_done_proc */);
5725 XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5728 NULL/* lose_ownership_proc */ ,
5729 NULL/* transfer_done_proc */);
5732 /* note: when called from menu all parameters are NULL, so no clue what the
5733 * Widget which was clicked on was, or what the click event was
5736 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5740 ret = SaveGameToFile(gameCopyFilename, FALSE);
5747 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5749 if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5753 /* function called when the data to Paste is ready */
5755 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5756 Atom *type, XtPointer value, unsigned long *len, int *format)
5759 if (value == NULL || *len == 0) {
5760 return; /* nothing had been selected to copy */
5762 f = fopen(gamePasteFilename, "w");
5764 DisplayError(_("Can't open temp file"), errno);
5767 fwrite(value, 1, *len, f);
5770 LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5773 /* called when Paste Game button is pressed,
5774 * all parameters will be NULL */
5776 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5778 XtGetSelectionValue(menuBarWidget,
5779 appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5780 /* (XtSelectionCallbackProc) */ PasteGameCB,
5781 NULL, /* client_data passed to PasteGameCB */
5783 /* better to use the time field from the event that triggered the
5784 * call to this function, but that isn't trivial to get
5795 SaveGameProc(NULL, NULL, NULL, NULL);
5800 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5806 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5812 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5814 MachineBlackEvent();
5818 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5820 MachineWhiteEvent();
5824 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5828 if (!first.analysisSupport) {
5829 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5830 DisplayError(buf, 0);
5833 /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5834 if (appData.icsActive) {
5835 if (gameMode != IcsObserving) {
5836 snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5837 DisplayError(buf, 0);
5839 if (appData.icsEngineAnalyze) {
5840 if (appData.debugMode)
5841 fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5847 /* if enable, use want disable icsEngineAnalyze */
5848 if (appData.icsEngineAnalyze) {
5853 appData.icsEngineAnalyze = TRUE;
5854 if (appData.debugMode)
5855 fprintf(debugFP, _("ICS engine analyze starting... \n"));
5857 #ifndef OPTIONSDIALOG
5858 if (!appData.showThinking)
5859 ShowThinkingProc(w,event,prms,nprms);
5866 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5868 if (!first.analysisSupport) {
5870 snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5871 DisplayError(buf, 0);
5874 // Reset(FALSE, TRUE);
5875 #ifndef OPTIONSDIALOG
5876 if (!appData.showThinking)
5877 ShowThinkingProc(w,event,prms,nprms);
5880 // FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5881 AnalysisPeriodicEvent(1);
5885 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5891 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5897 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5903 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5909 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5911 EditPositionEvent();
5915 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5921 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5925 if (PopDown(1)) { // popdown succesful
5927 XtSetArg(args[j], XtNleftBitmap, None); j++;
5928 XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5929 XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5930 } else // was not up
5935 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5937 if (!PopDown(4)) ICSInputBoxPopUp();
5941 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5947 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5953 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5959 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5965 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5971 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5977 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5983 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5989 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5991 UserAdjudicationEvent(+1);
5995 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5997 UserAdjudicationEvent(-1);
6001 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6003 UserAdjudicationEvent(0);
6007 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6009 if (shellUp[4] == True)
6014 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6015 { // [HGM] input: let up-arrow recall previous line from history
6022 if (!shellUp[4]) return;
6023 edit = boxOptions[0].handle;
6025 XtSetArg(args[j], XtNstring, &val); j++;
6026 XtGetValues(edit, args, j);
6027 val = PrevInHistory(val);
6028 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6029 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6031 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6032 XawTextReplace(edit, 0, 0, &t);
6033 XawTextSetInsertionPoint(edit, 9999);
6038 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6039 { // [HGM] input: let down-arrow recall next line from history
6044 if (!shellUp[4]) return;
6045 edit = boxOptions[0].handle;
6046 val = NextInHistory();
6047 XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6048 XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6050 t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6051 XawTextReplace(edit, 0, 0, &t);
6052 XawTextSetInsertionPoint(edit, 9999);
6057 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6059 StopObservingEvent();
6063 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6065 StopExaminingEvent();
6069 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6076 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6083 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6089 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6091 if (!TempBackwardActive) {
6092 TempBackwardActive = True;
6098 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6100 /* Check to see if triggered by a key release event for a repeating key.
6101 * If so the next queued event will be a key press of the same key at the same time */
6102 if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6104 XPeekEvent(xDisplay, &next);
6105 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6106 next.xkey.keycode == event->xkey.keycode)
6110 TempBackwardActive = False;
6114 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6120 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6126 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6132 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6138 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6140 TruncateGameEvent();
6144 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6150 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6156 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6158 flipView = !flipView;
6159 DrawPosition(True, NULL);
6163 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6167 PonderNextMoveEvent(!appData.ponderNextMove);
6168 #ifndef OPTIONSDIALOG
6169 if (appData.ponderNextMove) {
6170 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6172 XtSetArg(args[0], XtNleftBitmap, None);
6174 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6179 #ifndef OPTIONSDIALOG
6181 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6185 appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6187 if (appData.alwaysPromoteToQueen) {
6188 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6190 XtSetArg(args[0], XtNleftBitmap, None);
6192 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6197 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6201 appData.animateDragging = !appData.animateDragging;
6203 if (appData.animateDragging) {
6204 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6207 XtSetArg(args[0], XtNleftBitmap, None);
6209 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6214 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6218 appData.animate = !appData.animate;
6220 if (appData.animate) {
6221 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6224 XtSetArg(args[0], XtNleftBitmap, None);
6226 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6231 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6235 appData.autoCallFlag = !appData.autoCallFlag;
6237 if (appData.autoCallFlag) {
6238 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6240 XtSetArg(args[0], XtNleftBitmap, None);
6242 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6247 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6251 appData.autoFlipView = !appData.autoFlipView;
6253 if (appData.autoFlipView) {
6254 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6256 XtSetArg(args[0], XtNleftBitmap, None);
6258 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6263 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6267 appData.blindfold = !appData.blindfold;
6269 if (appData.blindfold) {
6270 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6272 XtSetArg(args[0], XtNleftBitmap, None);
6274 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6277 DrawPosition(True, NULL);
6281 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6285 appData.testLegality = !appData.testLegality;
6287 if (appData.testLegality) {
6288 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6290 XtSetArg(args[0], XtNleftBitmap, None);
6292 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6298 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6302 if (appData.flashCount == 0) {
6303 appData.flashCount = 3;
6305 appData.flashCount = -appData.flashCount;
6308 if (appData.flashCount > 0) {
6309 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6311 XtSetArg(args[0], XtNleftBitmap, None);
6313 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6319 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6323 appData.highlightDragging = !appData.highlightDragging;
6325 if (appData.highlightDragging) {
6326 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6328 XtSetArg(args[0], XtNleftBitmap, None);
6330 XtSetValues(XtNameToWidget(menuBarWidget,
6331 "menuOptions.Highlight Dragging"), args, 1);
6336 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6340 appData.highlightLastMove = !appData.highlightLastMove;
6342 if (appData.highlightLastMove) {
6343 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6345 XtSetArg(args[0], XtNleftBitmap, None);
6347 XtSetValues(XtNameToWidget(menuBarWidget,
6348 "menuOptions.Highlight Last Move"), args, 1);
6352 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6356 appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6358 if (appData.highlightMoveWithArrow) {
6359 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6361 XtSetArg(args[0], XtNleftBitmap, None);
6363 XtSetValues(XtNameToWidget(menuBarWidget,
6364 "menuOptions.Arrow"), args, 1);
6369 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6373 appData.icsAlarm = !appData.icsAlarm;
6375 if (appData.icsAlarm) {
6376 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6378 XtSetArg(args[0], XtNleftBitmap, None);
6380 XtSetValues(XtNameToWidget(menuBarWidget,
6381 "menuOptions.ICS Alarm"), args, 1);
6386 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6390 appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6392 if (appData.ringBellAfterMoves) {
6393 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6395 XtSetArg(args[0], XtNleftBitmap, None);
6397 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6402 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6406 appData.oneClick = !appData.oneClick;
6408 if (appData.oneClick) {
6409 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6411 XtSetArg(args[0], XtNleftBitmap, None);
6413 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6418 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6422 PeriodicUpdatesEvent(!appData.periodicUpdates);
6424 if (appData.periodicUpdates) {
6425 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6427 XtSetArg(args[0], XtNleftBitmap, None);
6429 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6434 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6438 appData.popupExitMessage = !appData.popupExitMessage;
6440 if (appData.popupExitMessage) {
6441 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6443 XtSetArg(args[0], XtNleftBitmap, None);
6445 XtSetValues(XtNameToWidget(menuBarWidget,
6446 "menuOptions.Popup Exit Message"), args, 1);
6450 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6454 appData.popupMoveErrors = !appData.popupMoveErrors;
6456 if (appData.popupMoveErrors) {
6457 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6459 XtSetArg(args[0], XtNleftBitmap, None);
6461 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6467 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6471 appData.premove = !appData.premove;
6473 if (appData.premove) {
6474 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6476 XtSetArg(args[0], XtNleftBitmap, None);
6478 XtSetValues(XtNameToWidget(menuBarWidget,
6479 "menuOptions.Premove"), args, 1);
6484 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6488 appData.showCoords = !appData.showCoords;
6490 if (appData.showCoords) {
6491 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6493 XtSetArg(args[0], XtNleftBitmap, None);
6495 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6498 DrawPosition(True, NULL);
6502 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6504 appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6505 ShowThinkingEvent();
6509 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6513 appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6514 ShowThinkingEvent();
6516 if (appData.hideThinkingFromHuman) {
6517 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6519 XtSetArg(args[0], XtNleftBitmap, None);
6521 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6527 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6531 saveSettingsOnExit = !saveSettingsOnExit;
6533 if (saveSettingsOnExit) {
6534 XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6536 XtSetArg(args[0], XtNleftBitmap, None);
6538 XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6543 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6545 SaveSettings(settingsFileName);
6549 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6552 snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6558 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6562 if (nprms && *nprms > 0)
6566 snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6571 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6577 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6583 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6587 char *zippy = _(" (with Zippy code)");
6591 snprintf(buf, sizeof(buf),
6593 "Copyright 1991 Digital Equipment Corporation\n"
6594 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6595 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6596 "%s is free software and carries NO WARRANTY;"
6597 "see the file COPYING for more information."),
6598 programVersion, zippy, PACKAGE);
6599 ErrorPopUp(_("About XBoard"), buf, FALSE);
6603 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6605 appData.debugMode = !appData.debugMode;
6609 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6615 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6621 DisplayMessage (char *message, char *extMessage)
6623 /* display a message in the message widget */
6632 snprintf(buf, sizeof(buf), "%s %s", message, extMessage);
6637 message = extMessage;
6641 safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6643 /* need to test if messageWidget already exists, since this function
6644 can also be called during the startup, if for example a Xresource
6645 is not set up correctly */
6648 XtSetArg(arg, XtNlabel, message);
6649 XtSetValues(messageWidget, &arg, 1);
6656 DisplayTitle (char *text)
6660 char title[MSG_SIZ];
6663 if (text == NULL) text = "";
6665 if (appData.titleInWindow) {
6667 XtSetArg(args[i], XtNlabel, text); i++;
6668 XtSetValues(titleWidget, args, i);
6671 if (*text != NULLCHAR) {
6672 safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6673 safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6674 } else if (appData.icsActive) {
6675 snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6676 snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6677 } else if (appData.cmailGameName[0] != NULLCHAR) {
6678 snprintf(icon, sizeof(icon), "%s", "CMail");
6679 snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6681 // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6682 } else if (gameInfo.variant == VariantGothic) {
6683 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6684 safeStrCpy(title, GOTHIC, sizeof(title)/sizeof(title[0]) );
6687 } else if (gameInfo.variant == VariantFalcon) {
6688 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6689 safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6691 } else if (appData.noChessProgram) {
6692 safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6693 safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6695 safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6696 snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6699 XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++;
6700 XtSetArg(args[i], XtNtitle, (XtArgVal) title); i++;
6701 XtSetValues(shellWidget, args, i);
6702 XSync(xDisplay, False);
6707 DisplayError (String message, int error)
6712 if (appData.debugMode || appData.matchMode) {
6713 fprintf(stderr, "%s: %s\n", programName, message);
6716 if (appData.debugMode || appData.matchMode) {
6717 fprintf(stderr, "%s: %s: %s\n",
6718 programName, message, strerror(error));
6720 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6723 ErrorPopUp(_("Error"), message, FALSE);
6728 DisplayMoveError (String message)
6732 DrawPosition(FALSE, NULL);
6733 if (appData.debugMode || appData.matchMode) {
6734 fprintf(stderr, "%s: %s\n", programName, message);
6736 if (appData.popupMoveErrors) {
6737 ErrorPopUp(_("Error"), message, FALSE);
6739 DisplayMessage(message, "");
6745 DisplayFatalError (String message, int error, int status)
6749 errorExitStatus = status;
6751 fprintf(stderr, "%s: %s\n", programName, message);
6753 fprintf(stderr, "%s: %s: %s\n",
6754 programName, message, strerror(error));
6755 snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6758 if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6759 ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6766 DisplayInformation (String message)
6769 ErrorPopUp(_("Information"), message, TRUE);
6773 DisplayNote (String message)
6776 ErrorPopUp(_("Note"), message, FALSE);
6780 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6786 DisplayIcsInteractionTitle (String message)
6788 if (oldICSInteractionTitle == NULL) {
6789 /* Magic to find the old window title, adapted from vim */
6790 char *wina = getenv("WINDOWID");
6792 Window win = (Window) atoi(wina);
6793 Window root, parent, *children;
6794 unsigned int nchildren;
6795 int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6797 if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6798 if (!XQueryTree(xDisplay, win, &root, &parent,
6799 &children, &nchildren)) break;
6800 if (children) XFree((void *)children);
6801 if (parent == root || parent == 0) break;
6804 XSetErrorHandler(oldHandler);
6806 if (oldICSInteractionTitle == NULL) {
6807 oldICSInteractionTitle = "xterm";
6810 printf("\033]0;%s\007", message);
6814 char pendingReplyPrefix[MSG_SIZ];
6815 ProcRef pendingReplyPR;
6818 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6821 fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6825 AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6829 AskQuestionPopDown ()
6831 if (!askQuestionUp) return;
6832 XtPopdown(askQuestionShell);
6833 XtDestroyWidget(askQuestionShell);
6834 askQuestionUp = False;
6838 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6844 reply = XawDialogGetValueString(w = XtParent(w));
6845 safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6846 if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6847 strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6848 strncat(buf, "\n", MSG_SIZ - strlen(buf) - 1);
6849 OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6850 AskQuestionPopDown();
6852 if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6856 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6861 XtSetArg(args[0], XtNlabel, &name);
6862 XtGetValues(w, args, 1);
6864 if (strcmp(name, _("cancel")) == 0) {
6865 AskQuestionPopDown();
6867 AskQuestionReplyAction(w, NULL, NULL, NULL);
6872 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6875 Widget popup, layout, dialog, edit;
6881 safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6882 pendingReplyPR = pr;
6885 XtSetArg(args[i], XtNresizable, True); i++;
6886 XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6887 askQuestionShell = popup =
6888 XtCreatePopupShell(title, transientShellWidgetClass,
6889 shellWidget, args, i);
6892 XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6893 layoutArgs, XtNumber(layoutArgs));
6896 XtSetArg(args[i], XtNlabel, question); i++;
6897 XtSetArg(args[i], XtNvalue, ""); i++;
6898 XtSetArg(args[i], XtNborderWidth, 0); i++;
6899 dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6902 XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
6903 (XtPointer) dialog);
6904 XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
6905 (XtPointer) dialog);
6907 XtRealizeWidget(popup);
6908 CatchDeleteWindow(popup, "AskQuestionPopDown");
6910 XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6911 &x, &y, &win_x, &win_y, &mask);
6913 XtSetArg(args[0], XtNx, x - 10);
6914 XtSetArg(args[1], XtNy, y - 30);
6915 XtSetValues(popup, args, 2);
6917 XtPopup(popup, XtGrabExclusive);
6918 askQuestionUp = True;
6920 edit = XtNameToWidget(dialog, "*value");
6921 XtSetKeyboardFocus(popup, edit);
6926 PlaySound (char *name)
6928 if (*name == NULLCHAR) {
6930 } else if (strcmp(name, "$") == 0) {
6931 putc(BELLCHAR, stderr);
6934 char *prefix = "", *sep = "";
6935 if(appData.soundProgram[0] == NULLCHAR) return;
6936 if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
6937 snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
6945 PlaySound(appData.soundMove);
6951 PlaySound(appData.soundIcsWin);
6957 PlaySound(appData.soundIcsLoss);
6963 PlaySound(appData.soundIcsDraw);
6967 PlayIcsUnfinishedSound ()
6969 PlaySound(appData.soundIcsUnfinished);
6975 PlaySound(appData.soundIcsAlarm);
6981 PlaySound(appData.soundTell);
6987 system("stty echo");
6994 system("stty -echo");
6999 RunCommand (char *buf)
7005 Colorize (ColorClass cc, int continuation)
7008 int count, outCount, error;
7010 if (textColors[(int)cc].bg > 0) {
7011 if (textColors[(int)cc].fg > 0) {
7012 snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7013 textColors[(int)cc].fg, textColors[(int)cc].bg);
7015 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7016 textColors[(int)cc].bg);
7019 if (textColors[(int)cc].fg > 0) {
7020 snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7021 textColors[(int)cc].fg);
7023 snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7026 count = strlen(buf);
7027 outCount = OutputToProcess(NoProc, buf, count, &error);
7028 if (outCount < count) {
7029 DisplayFatalError(_("Error writing to display"), error, 1);
7032 if (continuation) return;
7035 PlaySound(appData.soundShout);
7038 PlaySound(appData.soundSShout);
7041 PlaySound(appData.soundChannel1);
7044 PlaySound(appData.soundChannel);
7047 PlaySound(appData.soundKibitz);
7050 PlaySound(appData.soundTell);
7052 case ColorChallenge:
7053 PlaySound(appData.soundChallenge);
7056 PlaySound(appData.soundRequest);
7059 PlaySound(appData.soundSeek);
7071 return getpwuid(getuid())->pw_name;
7075 ExpandPathName (char *path)
7077 static char static_buf[4*MSG_SIZ];
7078 char *d, *s, buf[4*MSG_SIZ];
7084 while (*s && isspace(*s))
7093 if (*(s+1) == '/') {
7094 safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7098 safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7099 { char *p; if(p = strchr(buf, '/')) *p = 0; }
7100 pwd = getpwnam(buf);
7103 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7107 safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7108 strcat(d, strchr(s+1, '/'));
7112 safeStrCpy(d, s, 4*MSG_SIZ );
7120 static char host_name[MSG_SIZ];
7122 #if HAVE_GETHOSTNAME
7123 gethostname(host_name, MSG_SIZ);
7125 #else /* not HAVE_GETHOSTNAME */
7126 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7127 sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7129 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7131 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7132 #endif /* not HAVE_GETHOSTNAME */
7135 XtIntervalId delayedEventTimerXID = 0;
7136 DelayedEventCallback delayedEventCallback = 0;
7141 delayedEventTimerXID = 0;
7142 delayedEventCallback();
7146 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7148 if(delayedEventTimerXID && delayedEventCallback == cb)
7149 // [HGM] alive: replace, rather than add or flush identical event
7150 XtRemoveTimeOut(delayedEventTimerXID);
7151 delayedEventCallback = cb;
7152 delayedEventTimerXID =
7153 XtAppAddTimeOut(appContext, millisec,
7154 (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7157 DelayedEventCallback
7160 if (delayedEventTimerXID) {
7161 return delayedEventCallback;
7168 CancelDelayedEvent ()
7170 if (delayedEventTimerXID) {
7171 XtRemoveTimeOut(delayedEventTimerXID);
7172 delayedEventTimerXID = 0;
7176 XtIntervalId loadGameTimerXID = 0;
7179 LoadGameTimerRunning ()
7181 return loadGameTimerXID != 0;
7185 StopLoadGameTimer ()
7187 if (loadGameTimerXID != 0) {
7188 XtRemoveTimeOut(loadGameTimerXID);
7189 loadGameTimerXID = 0;
7197 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7199 loadGameTimerXID = 0;
7204 StartLoadGameTimer (long millisec)
7207 XtAppAddTimeOut(appContext, millisec,
7208 (XtTimerCallbackProc) LoadGameTimerCallback,
7212 XtIntervalId analysisClockXID = 0;
7215 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7217 if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7218 || appData.icsEngineAnalyze) { // [DM]
7219 AnalysisPeriodicEvent(0);
7220 StartAnalysisClock();
7225 StartAnalysisClock ()
7228 XtAppAddTimeOut(appContext, 2000,
7229 (XtTimerCallbackProc) AnalysisClockCallback,
7233 XtIntervalId clockTimerXID = 0;
7236 ClockTimerRunning ()
7238 return clockTimerXID != 0;
7244 if (clockTimerXID != 0) {
7245 XtRemoveTimeOut(clockTimerXID);
7254 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7261 StartClockTimer (long millisec)
7264 XtAppAddTimeOut(appContext, millisec,
7265 (XtTimerCallbackProc) ClockTimerCallback,
7270 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7275 /* check for low time warning */
7276 Pixel foregroundOrWarningColor = timerForegroundPixel;
7279 appData.lowTimeWarning &&
7280 (timer / 1000) < appData.icsAlarmTime)
7281 foregroundOrWarningColor = lowTimeWarningColor;
7283 if (appData.clockMode) {
7284 snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7285 XtSetArg(args[0], XtNlabel, buf);
7287 snprintf(buf, MSG_SIZ, "%s ", color);
7288 XtSetArg(args[0], XtNlabel, buf);
7293 XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7294 XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7296 XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7297 XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7300 XtSetValues(w, args, 3);
7304 DisplayWhiteClock (long timeRemaining, int highlight)
7308 if(appData.noGUI) return;
7309 DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7310 if (highlight && iconPixmap == bIconPixmap) {
7311 iconPixmap = wIconPixmap;
7312 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7313 XtSetValues(shellWidget, args, 1);
7318 DisplayBlackClock (long timeRemaining, int highlight)
7322 if(appData.noGUI) return;
7323 DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7324 if (highlight && iconPixmap == wIconPixmap) {
7325 iconPixmap = bIconPixmap;
7326 XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7327 XtSetValues(shellWidget, args, 1);
7346 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7350 int to_prog[2], from_prog[2];
7354 if (appData.debugMode) {
7355 fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7358 /* We do NOT feed the cmdLine to the shell; we just
7359 parse it into blank-separated arguments in the
7360 most simple-minded way possible.
7363 safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7366 while(*p == ' ') p++;
7368 if(*p == '"' || *p == '\'')
7369 p = strchr(++argv[i-1], *p);
7370 else p = strchr(p, ' ');
7371 if (p == NULL) break;
7376 SetUpChildIO(to_prog, from_prog);
7378 if ((pid = fork()) == 0) {
7380 // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7381 close(to_prog[1]); // first close the unused pipe ends
7382 close(from_prog[0]);
7383 dup2(to_prog[0], 0); // to_prog was created first, nd is the only one to use 0 or 1
7384 dup2(from_prog[1], 1);
7385 if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7386 close(from_prog[1]); // and closing again loses one of the pipes!
7387 if(fileno(stderr) >= 2) // better safe than sorry...
7388 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7390 if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7395 nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7397 execvp(argv[0], argv);
7399 /* If we get here, exec failed */
7404 /* Parent process */
7406 close(from_prog[1]);
7408 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7411 cp->fdFrom = from_prog[0];
7412 cp->fdTo = to_prog[1];
7417 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7419 AlarmCallBack (int n)
7425 DestroyChildProcess (ProcRef pr, int signalType)
7427 ChildProc *cp = (ChildProc *) pr;
7429 if (cp->kind != CPReal) return;
7431 if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7432 signal(SIGALRM, AlarmCallBack);
7434 if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7435 kill(cp->pid, SIGKILL); // kill it forcefully
7436 wait((int *) 0); // and wait again
7440 kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7442 /* Process is exiting either because of the kill or because of
7443 a quit command sent by the backend; either way, wait for it to die.
7452 InterruptChildProcess (ProcRef pr)
7454 ChildProc *cp = (ChildProc *) pr;
7456 if (cp->kind != CPReal) return;
7457 (void) kill(cp->pid, SIGINT); /* stop it thinking */
7461 OpenTelnet (char *host, char *port, ProcRef *pr)
7463 char cmdLine[MSG_SIZ];
7465 if (port[0] == NULLCHAR) {
7466 snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7468 snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7470 return StartChildProcess(cmdLine, "", pr);
7474 OpenTCP (char *host, char *port, ProcRef *pr)
7477 DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7478 #else /* !OMIT_SOCKETS */
7479 struct addrinfo hints;
7480 struct addrinfo *ais, *ai;
7485 memset(&hints, 0, sizeof(hints));
7486 hints.ai_family = AF_UNSPEC;
7487 hints.ai_socktype = SOCK_STREAM;
7489 error = getaddrinfo(host, port, &hints, &ais);
7491 /* a getaddrinfo error is not an errno, so can't return it */
7492 fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7493 host, port, gai_strerror(error));
7497 for (ai = ais; ai != NULL; ai = ai->ai_next) {
7498 if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7502 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7515 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7521 #endif /* !OMIT_SOCKETS */
7527 OpenCommPort (char *name, ProcRef *pr)
7532 fd = open(name, 2, 0);
7533 if (fd < 0) return errno;
7535 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7546 OpenLoopback (ProcRef *pr)
7551 SetUpChildIO(to, from);
7553 cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7556 cp->fdFrom = to[0]; /* note not from[0]; we are doing a loopback */
7564 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7566 DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7570 #define INPUT_SOURCE_BUF_SIZE 8192
7579 char buf[INPUT_SOURCE_BUF_SIZE];
7584 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7586 InputSource *is = (InputSource *) closure;
7591 if (is->lineByLine) {
7592 count = read(is->fd, is->unused,
7593 INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7595 (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7598 is->unused += count;
7600 while (p < is->unused) {
7601 q = memchr(p, '\n', is->unused - p);
7602 if (q == NULL) break;
7604 (is->func)(is, is->closure, p, q - p, 0);
7608 while (p < is->unused) {
7613 count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7618 (is->func)(is, is->closure, is->buf, count, error);
7623 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7626 ChildProc *cp = (ChildProc *) pr;
7628 is = (InputSource *) calloc(1, sizeof(InputSource));
7629 is->lineByLine = lineByLine;
7633 is->fd = fileno(stdin);
7635 is->kind = cp->kind;
7636 is->fd = cp->fdFrom;
7639 is->unused = is->buf;
7642 is->xid = XtAppAddInput(appContext, is->fd,
7643 (XtPointer) (XtInputReadMask),
7644 (XtInputCallbackProc) DoInputCallback,
7646 is->closure = closure;
7647 return (InputSourceRef) is;
7651 RemoveInputSource (InputSourceRef isr)
7653 InputSource *is = (InputSource *) isr;
7655 if (is->xid == 0) return;
7656 XtRemoveInput(is->xid);
7661 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7663 static int line = 0;
7664 ChildProc *cp = (ChildProc *) pr;
7669 if (appData.noJoin || !appData.useInternalWrap)
7670 outCount = fwrite(message, 1, count, stdout);
7673 int width = get_term_width();
7674 int len = wrap(NULL, message, count, width, &line);
7675 char *msg = malloc(len);
7679 outCount = fwrite(message, 1, count, stdout);
7682 dbgchk = wrap(msg, message, count, width, &line);
7683 if (dbgchk != len && appData.debugMode)
7684 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7685 outCount = fwrite(msg, 1, dbgchk, stdout);
7691 outCount = write(cp->fdTo, message, count);
7701 /* Output message to process, with "ms" milliseconds of delay
7702 between each character. This is needed when sending the logon
7703 script to ICC, which for some reason doesn't like the
7704 instantaneous send. */
7706 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7708 ChildProc *cp = (ChildProc *) pr;
7713 r = write(cp->fdTo, message++, 1);
7726 /**** Animation code by Hugh Fisher, DCS, ANU.
7728 Known problem: if a window overlapping the board is
7729 moved away while a piece is being animated underneath,
7730 the newly exposed area won't be updated properly.
7731 I can live with this.
7733 Known problem: if you look carefully at the animation
7734 of pieces in mono mode, they are being drawn as solid
7735 shapes without interior detail while moving. Fixing
7736 this would be a major complication for minimal return.
7739 /* Masks for XPM pieces. Black and white pieces can have
7740 different shapes, but in the interest of retaining my
7741 sanity pieces must have the same outline on both light
7742 and dark squares, and all pieces must use the same
7743 background square colors/images. */
7745 static int xpmDone = 0;
7748 CreateAnimMasks (int pieceDepth)
7754 unsigned long plane;
7757 /* Need a bitmap just to get a GC with right depth */
7758 buf = XCreatePixmap(xDisplay, xBoardWindow,
7760 values.foreground = 1;
7761 values.background = 0;
7762 /* Don't use XtGetGC, not read only */
7763 maskGC = XCreateGC(xDisplay, buf,
7764 GCForeground | GCBackground, &values);
7765 XFreePixmap(xDisplay, buf);
7767 buf = XCreatePixmap(xDisplay, xBoardWindow,
7768 squareSize, squareSize, pieceDepth);
7769 values.foreground = XBlackPixel(xDisplay, xScreen);
7770 values.background = XWhitePixel(xDisplay, xScreen);
7771 bufGC = XCreateGC(xDisplay, buf,
7772 GCForeground | GCBackground, &values);
7774 for (piece = WhitePawn; piece <= BlackKing; piece++) {
7775 /* Begin with empty mask */
7776 if(!xpmDone) // [HGM] pieces: keep using existing
7777 xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7778 squareSize, squareSize, 1);
7779 XSetFunction(xDisplay, maskGC, GXclear);
7780 XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7781 0, 0, squareSize, squareSize);
7783 /* Take a copy of the piece */
7788 XSetFunction(xDisplay, bufGC, GXcopy);
7789 XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7791 0, 0, squareSize, squareSize, 0, 0);
7793 /* XOR the background (light) over the piece */
7794 XSetFunction(xDisplay, bufGC, GXxor);
7796 XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7797 0, 0, squareSize, squareSize, 0, 0);
7799 XSetForeground(xDisplay, bufGC, lightSquareColor);
7800 XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7803 /* We now have an inverted piece image with the background
7804 erased. Construct mask by just selecting all the non-zero
7805 pixels - no need to reconstruct the original image. */
7806 XSetFunction(xDisplay, maskGC, GXor);
7808 /* Might be quicker to download an XImage and create bitmap
7809 data from it rather than this N copies per piece, but it
7810 only takes a fraction of a second and there is a much
7811 longer delay for loading the pieces. */
7812 for (n = 0; n < pieceDepth; n ++) {
7813 XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7814 0, 0, squareSize, squareSize,
7820 XFreePixmap(xDisplay, buf);
7821 XFreeGC(xDisplay, bufGC);
7822 XFreeGC(xDisplay, maskGC);
7826 InitAnimState (AnimState *anim, XWindowAttributes *info)
7831 /* Each buffer is square size, same depth as window */
7832 anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7833 squareSize, squareSize, info->depth);
7834 anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7835 squareSize, squareSize, info->depth);
7837 /* Create a plain GC for blitting */
7838 mask = GCForeground | GCBackground | GCFunction |
7839 GCPlaneMask | GCGraphicsExposures;
7840 values.foreground = XBlackPixel(xDisplay, xScreen);
7841 values.background = XWhitePixel(xDisplay, xScreen);
7842 values.function = GXcopy;
7843 values.plane_mask = AllPlanes;
7844 values.graphics_exposures = False;
7845 anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7847 /* Piece will be copied from an existing context at
7848 the start of each new animation/drag. */
7849 anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7851 /* Outline will be a read-only copy of an existing */
7852 anim->outlineGC = None;
7858 XWindowAttributes info;
7860 if (xpmDone && gameInfo.variant == oldVariant) return;
7861 if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7862 XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7864 InitAnimState(&game, &info);
7865 InitAnimState(&player, &info);
7867 /* For XPM pieces, we need bitmaps to use as masks. */
7869 CreateAnimMasks(info.depth), xpmDone = 1;
7874 static Boolean frameWaiting;
7877 FrameAlarm (int sig)
7879 frameWaiting = False;
7880 /* In case System-V style signals. Needed?? */
7881 signal(SIGALRM, FrameAlarm);
7885 FrameDelay (int time)
7887 struct itimerval delay;
7889 XSync(xDisplay, False);
7892 frameWaiting = True;
7893 signal(SIGALRM, FrameAlarm);
7894 delay.it_interval.tv_sec =
7895 delay.it_value.tv_sec = time / 1000;
7896 delay.it_interval.tv_usec =
7897 delay.it_value.tv_usec = (time % 1000) * 1000;
7898 setitimer(ITIMER_REAL, &delay, NULL);
7899 while (frameWaiting) pause();
7900 delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7901 delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7902 setitimer(ITIMER_REAL, &delay, NULL);
7909 FrameDelay (int time)
7911 XSync(xDisplay, False);
7913 usleep(time * 1000);
7924 /* Convert board position to corner of screen rect and color */
7927 ScreenSquare (int column, int row, XPoint *pt, int *color)
7930 pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
7931 pt->y = lineGap + row * (squareSize + lineGap);
7933 pt->x = lineGap + column * (squareSize + lineGap);
7934 pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
7936 *color = SquareColor(row, column);
7939 /* Convert window coords to square */
7942 BoardSquare (int x, int y, int *column, int *row)
7944 *column = EventToSquare(x, BOARD_WIDTH);
7945 if (flipView && *column >= 0)
7946 *column = BOARD_WIDTH - 1 - *column;
7947 *row = EventToSquare(y, BOARD_HEIGHT);
7948 if (!flipView && *row >= 0)
7949 *row = BOARD_HEIGHT - 1 - *row;
7954 #undef Max /* just in case */
7956 #define Max(a, b) ((a) > (b) ? (a) : (b))
7957 #define Min(a, b) ((a) < (b) ? (a) : (b))
7960 SetRect (XRectangle *rect, int x, int y, int width, int height)
7964 rect->width = width;
7965 rect->height = height;
7968 /* Test if two frames overlap. If they do, return
7969 intersection rect within old and location of
7970 that rect within new. */
7973 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
7975 if (old->x > new->x + size || new->x > old->x + size ||
7976 old->y > new->y + size || new->y > old->y + size) {
7979 SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
7980 size - abs(old->x - new->x), size - abs(old->y - new->y));
7981 pt->x = Max(old->x - new->x, 0);
7982 pt->y = Max(old->y - new->y, 0);
7987 /* For two overlapping frames, return the rect(s)
7988 in the old that do not intersect with the new. */
7991 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
7995 /* If old = new (shouldn't happen) then nothing to draw */
7996 if (old->x == new->x && old->y == new->y) {
8000 /* Work out what bits overlap. Since we know the rects
8001 are the same size we don't need a full intersect calc. */
8003 /* Top or bottom edge? */
8004 if (new->y > old->y) {
8005 SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8007 } else if (old->y > new->y) {
8008 SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8009 size, old->y - new->y);
8012 /* Left or right edge - don't overlap any update calculated above. */
8013 if (new->x > old->x) {
8014 SetRect(&(update[count]), old->x, Max(new->y, old->y),
8015 new->x - old->x, size - abs(new->y - old->y));
8017 } else if (old->x > new->x) {
8018 SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8019 old->x - new->x, size - abs(new->y - old->y));
8026 /* Generate a series of frame coords from start->mid->finish.
8027 The movement rate doubles until the half way point is
8028 reached, then halves back down to the final destination,
8029 which gives a nice slow in/out effect. The algorithmn
8030 may seem to generate too many intermediates for short
8031 moves, but remember that the purpose is to attract the
8032 viewers attention to the piece about to be moved and
8033 then to where it ends up. Too few frames would be less
8037 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8039 int fraction, n, count;
8043 /* Slow in, stepping 1/16th, then 1/8th, ... */
8045 for (n = 0; n < factor; n++)
8047 for (n = 0; n < factor; n++) {
8048 frames[count].x = start->x + (mid->x - start->x) / fraction;
8049 frames[count].y = start->y + (mid->y - start->y) / fraction;
8051 fraction = fraction / 2;
8055 frames[count] = *mid;
8058 /* Slow out, stepping 1/2, then 1/4, ... */
8060 for (n = 0; n < factor; n++) {
8061 frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8062 frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8064 fraction = fraction * 2;
8069 /* Draw a piece on the screen without disturbing what's there */
8072 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8076 /* Bitmap for piece being moved. */
8077 if (appData.monoMode) {
8078 *mask = *pieceToSolid(piece);
8079 } else if (useImages) {
8081 *mask = xpmMask[piece];
8083 *mask = ximMaskPm[piece];
8086 *mask = *pieceToSolid(piece);
8089 /* GC for piece being moved. Square color doesn't matter, but
8090 since it gets modified we make a copy of the original. */
8092 if (appData.monoMode)
8097 if (appData.monoMode)
8102 XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8104 /* Outline only used in mono mode and is not modified */
8106 *outline = bwPieceGC;
8108 *outline = wbPieceGC;
8112 OverlayPiece (ChessSquare piece, GC clip, GC outline, Drawable dest)
8117 /* Draw solid rectangle which will be clipped to shape of piece */
8118 XFillRectangle(xDisplay, dest, clip,
8119 0, 0, squareSize, squareSize);
8120 if (appData.monoMode)
8121 /* Also draw outline in contrasting color for black
8122 on black / white on white cases */
8123 XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8124 0, 0, squareSize, squareSize, 0, 0, 1);
8126 /* Copy the piece */
8131 if(appData.upsideDown && flipView) kind ^= 2;
8132 XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8134 0, 0, squareSize, squareSize,
8139 /* Animate the movement of a single piece */
8142 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8146 if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8147 /* The old buffer is initialised with the start square (empty) */
8148 BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8149 anim->prevFrame = *start;
8151 /* The piece will be drawn using its own bitmap as a matte */
8152 SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8153 XSetClipMask(xDisplay, anim->pieceGC, mask);
8157 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8159 XRectangle updates[4];
8164 /* Save what we are about to draw into the new buffer */
8165 XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8166 frame->x, frame->y, squareSize, squareSize,
8169 /* Erase bits of the previous frame */
8170 if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8171 /* Where the new frame overlapped the previous,
8172 the contents in newBuf are wrong. */
8173 XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8174 overlap.x, overlap.y,
8175 overlap.width, overlap.height,
8177 /* Repaint the areas in the old that don't overlap new */
8178 CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8179 for (i = 0; i < count; i++)
8180 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8181 updates[i].x - anim->prevFrame.x,
8182 updates[i].y - anim->prevFrame.y,
8183 updates[i].width, updates[i].height,
8184 updates[i].x, updates[i].y);
8186 /* Easy when no overlap */
8187 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8188 0, 0, squareSize, squareSize,
8189 anim->prevFrame.x, anim->prevFrame.y);
8192 /* Save this frame for next time round */
8193 XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8194 0, 0, squareSize, squareSize,
8196 anim->prevFrame = *frame;
8198 /* Draw piece over original screen contents, not current,
8199 and copy entire rect. Wipes out overlapping piece images. */
8200 OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8201 XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8202 0, 0, squareSize, squareSize,
8203 frame->x, frame->y);
8207 EndAnimation (AnimState *anim, XPoint *finish)
8209 XRectangle updates[4];
8214 /* The main code will redraw the final square, so we
8215 only need to erase the bits that don't overlap. */
8216 if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8217 CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8218 for (i = 0; i < count; i++)
8219 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8220 updates[i].x - anim->prevFrame.x,
8221 updates[i].y - anim->prevFrame.y,
8222 updates[i].width, updates[i].height,
8223 updates[i].x, updates[i].y);
8225 XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8226 0, 0, squareSize, squareSize,
8227 anim->prevFrame.x, anim->prevFrame.y);
8232 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8236 BeginAnimation(anim, piece, startColor, start);
8237 for (n = 0; n < nFrames; n++) {
8238 AnimationFrame(anim, &(frames[n]), piece);
8239 FrameDelay(appData.animSpeed);
8241 EndAnimation(anim, finish);
8245 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8248 ChessSquare piece = board[fromY][toY];
8249 board[fromY][toY] = EmptySquare;
8250 DrawPosition(FALSE, board);
8252 x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8253 y = lineGap + toY * (squareSize + lineGap);
8255 x = lineGap + toX * (squareSize + lineGap);
8256 y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8258 for(i=1; i<4*kFactor; i++) {
8259 int r = squareSize * 9 * i/(20*kFactor - 5);
8260 XFillArc(xDisplay, xBoardWindow, highlineGC,
8261 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8262 FrameDelay(appData.animSpeed);
8264 board[fromY][toY] = piece;
8267 /* Main control logic for deciding what to animate and how */
8270 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8274 XPoint start, finish, mid;
8275 XPoint frames[kFactor * 2 + 1];
8276 int nFrames, startColor, endColor;
8278 /* Are we animating? */
8279 if (!appData.animate || appData.blindfold)
8282 if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8283 board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8284 return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8286 if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8287 piece = board[fromY][fromX];
8288 if (piece >= EmptySquare) return;
8293 hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8296 ScreenSquare(fromX, fromY, &start, &startColor);
8297 ScreenSquare(toX, toY, &finish, &endColor);
8300 /* Knight: make straight movement then diagonal */
8301 if (abs(toY - fromY) < abs(toX - fromX)) {
8302 mid.x = start.x + (finish.x - start.x) / 2;
8306 mid.y = start.y + (finish.y - start.y) / 2;
8309 mid.x = start.x + (finish.x - start.x) / 2;
8310 mid.y = start.y + (finish.y - start.y) / 2;
8313 /* Don't use as many frames for very short moves */
8314 if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8315 Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8317 Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8318 FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8319 if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8321 for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8322 if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8325 /* Be sure end square is redrawn */
8326 damage[0][toY][toX] = True;
8330 DragPieceBegin (int x, int y, Boolean instantly)
8332 int boardX, boardY, color;
8335 /* Are we animating? */
8336 if (!appData.animateDragging || appData.blindfold)
8339 /* Figure out which square we start in and the
8340 mouse position relative to top left corner. */
8341 BoardSquare(x, y, &boardX, &boardY);
8342 player.startBoardX = boardX;
8343 player.startBoardY = boardY;
8344 ScreenSquare(boardX, boardY, &corner, &color);
8345 player.startSquare = corner;
8346 player.startColor = color;
8347 /* As soon as we start dragging, the piece will jump slightly to
8348 be centered over the mouse pointer. */
8349 player.mouseDelta.x = squareSize/2;
8350 player.mouseDelta.y = squareSize/2;
8351 /* Initialise animation */
8352 player.dragPiece = PieceForSquare(boardX, boardY);
8354 if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8355 player.dragActive = True;
8356 BeginAnimation(&player, player.dragPiece, color, &corner);
8357 /* Mark this square as needing to be redrawn. Note that
8358 we don't remove the piece though, since logically (ie
8359 as seen by opponent) the move hasn't been made yet. */
8360 if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8361 boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8362 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8363 corner.x, corner.y, squareSize, squareSize,
8364 0, 0); // [HGM] zh: unstack in stead of grab
8365 if(gatingPiece != EmptySquare) {
8366 /* Kludge alert: When gating we want the introduced
8367 piece to appear on the from square. To generate an
8368 image of it, we draw it on the board, copy the image,
8369 and draw the original piece again. */
8370 ChessSquare piece = boards[currentMove][boardY][boardX];
8371 DrawSquare(boardY, boardX, gatingPiece, 0);
8372 XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8373 corner.x, corner.y, squareSize, squareSize, 0, 0);
8374 DrawSquare(boardY, boardX, piece, 0);
8376 damage[0][boardY][boardX] = True;
8378 player.dragActive = False;
8383 ChangeDragPiece (ChessSquare piece)
8386 player.dragPiece = piece;
8387 /* The piece will be drawn using its own bitmap as a matte */
8388 SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8389 XSetClipMask(xDisplay, player.pieceGC, mask);
8393 DragPieceMove (int x, int y)
8397 /* Are we animating? */
8398 if (!appData.animateDragging || appData.blindfold)
8402 if (! player.dragActive)
8404 /* Move piece, maintaining same relative position
8405 of mouse within square */
8406 corner.x = x - player.mouseDelta.x;
8407 corner.y = y - player.mouseDelta.y;
8408 AnimationFrame(&player, &corner, player.dragPiece);
8410 if (appData.highlightDragging) {
8412 BoardSquare(x, y, &boardX, &boardY);
8413 SetHighlights(fromX, fromY, boardX, boardY);
8419 DragPieceEnd (int x, int y)
8421 int boardX, boardY, color;
8424 /* Are we animating? */
8425 if (!appData.animateDragging || appData.blindfold)
8429 if (! player.dragActive)
8431 /* Last frame in sequence is square piece is
8432 placed on, which may not match mouse exactly. */
8433 BoardSquare(x, y, &boardX, &boardY);
8434 ScreenSquare(boardX, boardY, &corner, &color);
8435 EndAnimation(&player, &corner);
8437 /* Be sure end square is redrawn */
8438 damage[0][boardY][boardX] = True;
8440 /* This prevents weird things happening with fast successive
8441 clicks which on my Sun at least can cause motion events
8442 without corresponding press/release. */
8443 player.dragActive = False;
8446 /* Handle expose event while piece being dragged */
8451 if (!player.dragActive || appData.blindfold)
8454 /* What we're doing: logically, the move hasn't been made yet,
8455 so the piece is still in it's original square. But visually
8456 it's being dragged around the board. So we erase the square
8457 that the piece is on and draw it at the last known drag point. */
8458 BlankSquare(player.startSquare.x, player.startSquare.y,
8459 player.startColor, EmptySquare, xBoardWindow, 1);
8460 AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8461 damage[0][player.startBoardY][player.startBoardX] = TRUE;
8464 #include <sys/ioctl.h>
8468 int fd, default_width;
8471 default_width = 79; // this is FICS default anyway...
8473 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8475 if (!ioctl(fd, TIOCGSIZE, &win))
8476 default_width = win.ts_cols;
8477 #elif defined(TIOCGWINSZ)
8479 if (!ioctl(fd, TIOCGWINSZ, &win))
8480 default_width = win.ws_col;
8482 return default_width;
8488 static int old_width = 0;
8489 int new_width = get_term_width();
8491 if (old_width != new_width)
8492 ics_printf("set width %d\n", new_width);
8493 old_width = new_width;
8497 NotifyFrontendLogin ()
8502 /* [AS] Arrow highlighting support */
8504 static double A_WIDTH = 5; /* Width of arrow body */
8506 #define A_HEIGHT_FACTOR 6 /* Length of arrow "point", relative to body width */
8507 #define A_WIDTH_FACTOR 3 /* Width of arrow "point", relative to body width */
8518 return (int) (x + 0.5);
8522 SquareToPos (int rank, int file, int *x, int *y)
8525 *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8526 *y = lineGap + rank * (squareSize + lineGap);
8528 *x = lineGap + file * (squareSize + lineGap);
8529 *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8533 /* Draw an arrow between two points using current settings */
8535 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8538 double dx, dy, j, k, x, y;
8541 int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8543 arrow[0].x = s_x + A_WIDTH + 0.5;
8546 arrow[1].x = s_x + A_WIDTH + 0.5;
8547 arrow[1].y = d_y - h;
8549 arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8550 arrow[2].y = d_y - h;
8555 arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8556 arrow[5].y = d_y - h;
8558 arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8559 arrow[4].y = d_y - h;
8561 arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8564 else if( d_y == s_y ) {
8565 int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8568 arrow[0].y = s_y + A_WIDTH + 0.5;
8570 arrow[1].x = d_x - w;
8571 arrow[1].y = s_y + A_WIDTH + 0.5;
8573 arrow[2].x = d_x - w;
8574 arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8579 arrow[5].x = d_x - w;
8580 arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8582 arrow[4].x = d_x - w;
8583 arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8586 arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8589 /* [AS] Needed a lot of paper for this! :-) */
8590 dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8591 dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8593 j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8595 k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8600 arrow[0].x = Round(x - j);
8601 arrow[0].y = Round(y + j*dx);
8603 arrow[1].x = Round(arrow[0].x + 2*j); // [HGM] prevent width to be affected by rounding twice
8604 arrow[1].y = Round(arrow[0].y - 2*j*dx);
8607 x = (double) d_x - k;
8608 y = (double) d_y - k*dy;
8611 x = (double) d_x + k;
8612 y = (double) d_y + k*dy;
8615 x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8617 arrow[6].x = Round(x - j);
8618 arrow[6].y = Round(y + j*dx);
8620 arrow[2].x = Round(arrow[6].x + 2*j);
8621 arrow[2].y = Round(arrow[6].y - 2*j*dx);
8623 arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8624 arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8629 arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8630 arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8633 XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8634 if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8635 // Polygon( hdc, arrow, 7 );
8639 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8642 hor = 64*s_col + 32; vert = 64*s_row + 32;
8643 for(i=0; i<= 64; i++) {
8644 damage[0][vert+6>>6][hor+6>>6] = True;
8645 damage[0][vert-6>>6][hor+6>>6] = True;
8646 damage[0][vert+6>>6][hor-6>>6] = True;
8647 damage[0][vert-6>>6][hor-6>>6] = True;
8648 hor += d_col - s_col; vert += d_row - s_row;
8652 /* [AS] Draw an arrow between two squares */
8654 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8656 int s_x, s_y, d_x, d_y;
8658 if( s_col == d_col && s_row == d_row ) {
8662 /* Get source and destination points */
8663 SquareToPos( s_row, s_col, &s_x, &s_y);
8664 SquareToPos( d_row, d_col, &d_x, &d_y);
8667 d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8669 else if( d_y < s_y ) {
8670 d_y += squareSize / 2 + squareSize / 4;
8673 d_y += squareSize / 2;
8677 d_x += squareSize / 2 - squareSize / 4;
8679 else if( d_x < s_x ) {
8680 d_x += squareSize / 2 + squareSize / 4;
8683 d_x += squareSize / 2;
8686 s_x += squareSize / 2;
8687 s_y += squareSize / 2;
8690 A_WIDTH = squareSize / 14.; //[HGM] make float
8692 DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8693 ArrowDamage(s_col, s_row, d_col, d_row);
8697 IsDrawArrowEnabled ()
8699 return appData.highlightMoveWithArrow && squareSize >= 32;
8703 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8705 if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8706 DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8710 UpdateLogos (int displ)
8712 return; // no logos in XBoard yet