X-Git-Url: http://winboard.nl/cgi-bin?p=xboard.git;a=blobdiff_plain;f=xboard.c;h=0e8b132d0e9d7b9c9f4f07632fd5af69c1379f6f;hp=ba45445da954ae2f91d822459f1c551ad14bef9d;hb=d016fb202fe45795e630e22ba516e754cf694ea6;hpb=36594bc0f99d1a28476d3d5213a4dae0de1a5785 diff --git a/xboard.c b/xboard.c index ba45445..0e8b132 100644 --- a/xboard.c +++ b/xboard.c @@ -1,11 +1,11 @@ /* * xboard.c -- X front end for XBoard - * $Id: xboard.c,v 2.2 2003/11/06 07:22:14 mann Exp $ * * Copyright 1991 by Digital Equipment Corporation, Maynard, - * Massachusetts. Enhancements Copyright - * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software - * Foundation, Inc. + * Massachusetts. + * + * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006, + * 2007, 2008, 2009 Free Software Foundation, Inc. * * The following terms apply to Digital Equipment Corporation's copyright * interest in XBoard: @@ -456,6 +456,7 @@ Display *xDisplay; Window xBoardWindow; Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor, jailSquareColor, highlightSquareColor, premoveHighlightColor; +Pixel lowTimeWarningColor; GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC, bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC, wjPieceGC, bjPieceGC, prelineGC, countGC; @@ -494,16 +495,19 @@ char *chessDir, *programName, *programVersion, #define SOLID 0 #define OUTLINE 1 Pixmap pieceBitmap[2][(int)BlackPawn]; -Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD */ +Pixmap pieceBitmap2[2][(int)BlackPawn+4]; /* [HGM] pieces */ +Pixmap xpmPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD actually used*/ +Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4]; /* LL, LD, DL, DD set to select from */ Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare; int useImages, useImageSqs; -XImage *ximPieceBitmap[4][(int)BlackPawn]; /* LL, LD, DL, DD */ -Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */ +XImage *ximPieceBitmap[4][(int)BlackPawn+4]; /* LL, LD, DL, DD */ +Pixmap ximMaskPm[(int)BlackPawn]; /* clipmasks, used for XIM pieces */ +Pixmap ximMaskPm2[(int)BlackPawn+4]; /* clipmasks, used for XIM pieces */ XImage *ximLightSquare, *ximDarkSquare; XImage *xim_Cross; -#define pieceToSolid(piece) &pieceBitmap[SOLID][((int)(piece)) % (int)BlackPawn] -#define pieceToOutline(piece) &pieceBitmap[OUTLINE][((int)(piece)) % (int)BlackPawn] +#define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn] +#define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn] #define White(piece) ((int)(piece) < (int)BlackPawn) @@ -695,21 +699,27 @@ MenuItem buttonBar[] = { {NULL, NULL} }; -#define PIECE_MENU_SIZE 11 +#define PIECE_MENU_SIZE 18 String pieceMenuStrings[2][PIECE_MENU_SIZE] = { { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), - N_("Queen"), N_("King"), "----", N_("Empty square"), N_("Clear board") }, + N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), + N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), + N_("Empty square"), N_("Clear board") }, { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), - N_("Queen"), N_("King"), "----", N_("Empty square"), N_("Clear board") }, + N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), + N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), + N_("Empty square"), N_("Clear board") } }; /* must be in same order as PieceMenuStrings! */ ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = { { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop, - WhiteRook, WhiteQueen, WhiteKing, - (ChessSquare) 0, EmptySquare, ClearBoard }, + WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil, + WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0, + PromotePiece, DemotePiece, EmptySquare, ClearBoard }, { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop, - BlackRook, BlackQueen, BlackKing, - (ChessSquare) 0, EmptySquare, ClearBoard }, + BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil, + BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0, + PromotePiece, DemotePiece, EmptySquare, ClearBoard }, }; #define DROP_MENU_SIZE 6 @@ -1012,7 +1022,7 @@ XtResource clientResources[] = { { "localLineEditing", "localLineEditing", XtRBoolean, sizeof(Boolean), XtOffset(AppDataPtr, localLineEditing), XtRImmediate, (XtPointer) True }, /* not implemented, must be True */ -#ifdef ZIPPY +#if ZIPPY { "zippyTalk", "zippyTalk", XtRBoolean, sizeof(Boolean), XtOffset(AppDataPtr, zippyTalk), XtRImmediate, (XtPointer) ZIPPY_TALK }, @@ -1059,6 +1069,9 @@ XtResource clientResources[] = { { "zippyReplayTimeout", "zippyReplayTimeout", XtRInt, sizeof(int), XtOffset(AppDataPtr, zippyReplayTimeout), XtRImmediate, (XtPointer) ZIPPY_REPLAY_TIMEOUT }, + { "zippyShortGame", "zippyShortGame", XtRInt, sizeof(int), + XtOffset(AppDataPtr, zippyShortGame), XtRImmediate, + (XtPointer) 0 }, #endif { "flashCount", "flashCount", XtRInt, sizeof(int), XtOffset(AppDataPtr, flashCount), XtRImmediate, @@ -1192,6 +1205,12 @@ XtResource clientResources[] = { { "showButtonBar", "showButtonBar", XtRBoolean, sizeof(Boolean), XtOffset(AppDataPtr, showButtonBar), XtRImmediate, (XtPointer) True }, + { "lowTimeWarningColor", "lowTimeWarningColor", XtRString, + sizeof(String), XtOffset(AppDataPtr, lowTimeWarningColor), + XtRString, COLOR_LOWTIMEWARNING }, + { "lowTimeWarning", "lowTimeWarning", XtRBoolean, + sizeof(Boolean), XtOffset(AppDataPtr, lowTimeWarning), + XtRImmediate, (XtPointer) False }, {"icsEngineAnalyze", "icsEngineAnalyze", XtRBoolean, /* [DM] icsEngineAnalyze */ sizeof(Boolean), XtOffset(AppDataPtr, icsEngineAnalyze), XtRImmediate, (XtPointer) False }, @@ -1210,6 +1229,9 @@ XtResource clientResources[] = { { "adjudicateLossThreshold", "adjudicateLossThreshold", XtRInt, sizeof(int), XtOffset(AppDataPtr, adjudicateLossThreshold), XtRImmediate, (XtPointer) 0}, + { "adjudicateDrawMoves", "adjudicateDrawMoves", XtRInt, + sizeof(int), XtOffset(AppDataPtr, adjudicateDrawMoves), + XtRImmediate, (XtPointer) 0}, { "pgnEventHeader", "pgnEventHeader", XtRString, sizeof(String), XtOffset(AppDataPtr, pgnEventHeader), XtRImmediate, (XtPointer) "Computer Chess Game" }, @@ -1325,7 +1347,7 @@ XtResource clientResources[] = { XtRImmediate, (XtPointer) "xboard.debug"}, { "engineDebugOutput", "engineDebugOutput", XtRInt, sizeof(int), XtOffset(AppDataPtr, engineComments), - XtRImmediate, (XtPointer) 0}, + XtRImmediate, (XtPointer) 1}, { "noGUI", "noGUI", XtRBoolean, sizeof(Boolean), XtOffset(AppDataPtr, noGUI), XtRImmediate, (XtPointer) 0}, @@ -1335,6 +1357,12 @@ XtResource clientResources[] = { { "secondOptions", "secondOptions", XtRString, sizeof(String), XtOffset(AppDataPtr, secondOptions), XtRImmediate, (XtPointer) "" }, + { "firstNeedsNoncompliantFEN", "firstNeedsNoncompliantFEN", XtRString, + sizeof(String), XtOffset(AppDataPtr, fenOverride1), + XtRImmediate, (XtPointer) 0 }, + { "secondNeedsNoncompliantFEN", "secondNeedsNoncompliantFEN", XtRString, + sizeof(String), XtOffset(AppDataPtr, fenOverride2), + XtRImmediate, (XtPointer) 0 }, // [HGM] Winboard_x UCI options { "firstIsUCI", "firstIsUCI", XtRBoolean, @@ -1373,6 +1401,9 @@ XtResource clientResources[] = { { "delayAfterQuit", "delayAfterQuit", XtRInt, sizeof(int), XtOffset(AppDataPtr, delayAfterQuit), XtRImmediate, (XtPointer) 0}, + { "keepAlive", "keepAlive", XtRInt, + sizeof(int), XtOffset(AppDataPtr, keepAlive), + XtRImmediate, (XtPointer) 0}, }; XrmOptionDescRec shellOptions[] = { @@ -1595,6 +1626,7 @@ XrmOptionDescRec shellOptions[] = { { "-zippyVariants", "zippyVariants", XrmoptionSepArg, NULL }, { "-zippyMaxGames", "zippyMaxGames", XrmoptionSepArg, NULL }, { "-zippyReplayTimeout", "zippyReplayTimeout", XrmoptionSepArg, NULL }, + { "-zippyShortGame", "zippyShortGame", XrmoptionSepArg, NULL }, #endif { "-flashCount", "flashCount", XrmoptionSepArg, NULL }, { "-flash", "flashCount", XrmoptionNoArg, "3" }, @@ -1663,12 +1695,15 @@ XrmOptionDescRec shellOptions[] = { { "-showButtonBar", "showButtonBar", XrmoptionSepArg, NULL }, { "-buttons", "showButtonBar", XrmoptionNoArg, "True" }, { "-xbuttons", "showButtonBar", XrmoptionNoArg, "False" }, + { "-lowTimeWarningColor", "lowTimeWarningColor", XrmoptionSepArg, NULL }, + { "-lowTimeWarning", "lowTimeWarning", XrmoptionSepArg, NULL }, /* [AS,HR] New features */ { "-firstScoreAbs", "firstScoreAbs", XrmoptionSepArg, NULL }, { "-secondScoreAbs", "secondScoreAbs", XrmoptionSepArg, NULL }, { "-pgnExtendedInfo", "pgnExtendedInfo", XrmoptionSepArg, NULL }, { "-hideThinkingFromHuman", "hideThinkingFromHuman", XrmoptionSepArg, NULL }, { "-adjudicateLossThreshold", "adjudicateLossThreshold", XrmoptionSepArg, NULL }, + { "-adjudicateDrawMoves", "adjudicateDrawMoves", XrmoptionSepArg, NULL }, { "-pgnEventHeader", "pgnEventHeader", XrmoptionSepArg, NULL }, { "-firstIsUCI", "firstIsUCI", XrmoptionSepArg, NULL }, { "-secondIsUCI", "secondIsUCI", XrmoptionSepArg, NULL }, @@ -1734,6 +1769,9 @@ XrmOptionDescRec shellOptions[] = { { "-noGUI", "noGUI", XrmoptionNoArg, "True" }, { "-firstOptions", "firstOptions", XrmoptionSepArg, NULL }, { "-secondOptions", "secondOptions", XrmoptionSepArg, NULL }, + { "-firstNeedsNoncompliantFEN", "firstNeedsNoncompliantFEN", XrmoptionSepArg, NULL }, + { "-secondNeedsNoncompliantFEN", "secondNeedsNoncompliantFEN", XrmoptionSepArg, NULL }, + { "-keepAlive", "keepAlive", XrmoptionSepArg, NULL }, }; @@ -2136,7 +2174,7 @@ CatchDeleteWindow(Widget w, String procname) { char buf[MSG_SIZ]; XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1); - sprintf(buf, "WM_PROTOCOLS: %s() \n", procname); + snprintf(buf, sizeof(buf), "WM_PROTOCOLS: %s() \n", procname); XtAugmentTranslations(w, XtParseTranslationTable(buf)); } @@ -2223,9 +2261,82 @@ void InitDrawingSizes(BoardSize boardSize, int flags) shellArgs[4].value = shellArgs[2].value = w; shellArgs[5].value = shellArgs[3].value = h; XtSetValues(shellWidget, &shellArgs[0], 6); + + // [HGM] pieces: tailor piece bitmaps to needs of specific variant + // (only for xpm) + if(useImages) { + for(i=0; i<4; i++) { + int p; + for(p=0; p<=(int)WhiteKing; p++) + xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults + if(gameInfo.variant == VariantShogi) { + xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1]; + xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2]; + xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3]; + xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4]; + xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance]; + } +#ifdef GOTHIC + if(gameInfo.variant == VariantGothic) { + xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver]; + } +#endif +#if !HAVE_LIBXPM + // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used! + for(p=0; p<=(int)WhiteKing; p++) + ximMaskPm[p] = ximMaskPm2[p]; // defaults + if(gameInfo.variant == VariantShogi) { + ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1]; + ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2]; + ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3]; + ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4]; + ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance]; + } +#ifdef GOTHIC + if(gameInfo.variant == VariantGothic) { + ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver]; + } +#endif +#endif + } + } else { + for(i=0; i<2; i++) { + int p; + for(p=0; p<=(int)WhiteKing; p++) + pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults + if(gameInfo.variant == VariantShogi) { + pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1]; + pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2]; + pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3]; + pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4]; + pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance]; + } +#ifdef GOTHIC + if(gameInfo.variant == VariantGothic) { + pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver]; + } +#endif + } + } +#if HAVE_LIBXPM + CreateAnimVars(); +#endif } #endif +void EscapeExpand(char *p, char *q) +{ // [HGM] initstring: routine to shape up string arguments + while(*p++ = *q++) if(p[-1] == '\\') + switch(*q++) { + case 'n': p[-1] = '\n'; break; + case 'r': p[-1] = '\r'; break; + case 't': p[-1] = '\t'; break; + case '\\': p[-1] = '\\'; break; + case 0: *p = 0; return; + default: p[-1] = q[-1]; break; + } +} + int main(argc, argv) int argc; @@ -2284,7 +2395,6 @@ main(argc, argv) #endif #endif - setbuf(stdout, NULL); setbuf(stderr, NULL); debugFP = stderr; @@ -2297,8 +2407,8 @@ main(argc, argv) #ifdef ENABLE_NLS XtSetLanguageProc(NULL, NULL, NULL); - bindtextdomain(PRODUCT, LOCALEDIR); - textdomain(PRODUCT); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); #endif shellWidget = @@ -2308,17 +2418,21 @@ main(argc, argv) if (argc > 1) { fprintf(stderr, _("%s: unrecognized argument %s\n"), programName, argv[1]); - exit(2); - } - - if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) { - chessDir = "."; - } else { - if (chdir(chessDir) != 0) { - fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName); - perror(chessDir); - exit(1); + fprintf(stderr, "Recognized options:\n"); + for(i = 0; i < XtNumber(shellOptions); i++) { + j = fprintf(stderr, " %s%s", shellOptions[i].option, + (shellOptions[i].argKind == XrmoptionSepArg + ? " ARG" : "")); + if (i++ < XtNumber(shellOptions)) { + fprintf(stderr, "%*c%s%s\n", 40 - j, ' ', + shellOptions[i].option, + (shellOptions[i].argKind == XrmoptionSepArg + ? " ARG" : "")); + } else { + fprintf(stderr, "\n"); + } } + exit(2); } p = getenv("HOME"); @@ -2326,13 +2440,35 @@ main(argc, argv) i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1; gameCopyFilename = (char*) malloc(i); gamePasteFilename = (char*) malloc(i); - sprintf(gameCopyFilename, "%s/.xboard%05uc.pgn", p, getpid()); - sprintf(gamePasteFilename, "%s/.xboard%05up.pgn", p, getpid()); + snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid()); + snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid()); XtGetApplicationResources(shellWidget, (XtPointer) &appData, clientResources, XtNumber(clientResources), NULL, 0); + { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string. + static char buf[MSG_SIZ]; + EscapeExpand(buf, appData.initString); + appData.initString = strdup(buf); + EscapeExpand(buf, appData.secondInitString); + appData.secondInitString = strdup(buf); + EscapeExpand(buf, appData.firstComputerString); + appData.firstComputerString = strdup(buf); + EscapeExpand(buf, appData.secondComputerString); + appData.secondComputerString = strdup(buf); + } + + if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) { + chessDir = "."; + } else { + if (chdir(chessDir) != 0) { + fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName); + perror(chessDir); + exit(1); + } + } + if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) { /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */ if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL) { @@ -2565,6 +2701,20 @@ XBoard square size (hint): %d\n\ if (forceMono) { fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"), programName); + + if (appData.bitmapDirectory == NULL || + appData.bitmapDirectory[0] == NULLCHAR) + appData.bitmapDirectory = DEF_BITMAP_DIR; + } + + if (appData.lowTimeWarning && !appData.monoMode) { + vFrom.addr = (caddr_t) appData.lowTimeWarningColor; + vFrom.size = strlen(appData.lowTimeWarningColor); + XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo); + if (vTo.addr == NULL) + appData.monoMode = True; + else + lowTimeWarningColor = *(Pixel *) vTo.addr; } if (appData.monoMode && appData.debugMode) { @@ -3043,7 +3193,8 @@ XBoard square size (hint): %d\n\ signal(SIGUSR1, CmailSigHandler); } } - InitPosition(TRUE); + gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes() + InitPosition(TRUE); XtAppMainLoop(appContext); if (appData.debugMode) fclose(debugFP); // [DM] debug @@ -3655,6 +3806,9 @@ void loadXIM(xim, xmask, filename, dest, mask) } } + +char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl"; + void CreateXIMPieces() { int piece, kind; @@ -3685,13 +3839,14 @@ void CreateXIMPieces() } fprintf(stderr, _("\nLoading XIMs...\n")); /* Load pieces */ - for (piece = (int) WhitePawn; piece <= (int) WhiteKing; piece++) { + for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) { fprintf(stderr, "%d", piece+1); for (kind=0; kind<4; kind++) { fprintf(stderr, "."); - sprintf(buf, "%s/%c%s%u.xim", + snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim", ExpandPathName(appData.pixmapDirectory), - ToLower(PieceToChar((ChessSquare)piece)), + piece <= (int) WhiteKing ? "" : "w", + pieceBitmapNames[piece], ximkind[kind], ss); ximPieceBitmap[kind][piece] = XGetImage(xDisplay, DefaultRootWindow(xDisplay), @@ -3700,15 +3855,17 @@ void CreateXIMPieces() fprintf(stderr, _("(File:%s:) "), buf); loadXIM(ximPieceBitmap[kind][piece], ximtemp, buf, - &(xpmPieceBitmap[kind][piece]), - &(ximMaskPm[piece%(int)BlackPawn])); + &(xpmPieceBitmap2[kind][piece]), + &(ximMaskPm2[piece])); + if(piece <= (int)WhiteKing) + xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece]; } fprintf(stderr," "); } /* Load light and dark squares */ /* If the LSQ and DSQ pieces don't exist, we will draw them with solid squares. */ - sprintf(buf, "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss); + snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss); if (access(buf, 0) != 0) { useImageSqs = 0; } else { @@ -3722,7 +3879,7 @@ void CreateXIMPieces() loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL); fprintf(stderr, _("dark square ")); - sprintf(buf, "%s/dsq%u.xim", + snprintf(buf,sizeof(buf), "%s/dsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss); if (appData.debugMode) fprintf(stderr, _("(File:%s:) "), buf); @@ -3787,17 +3944,19 @@ void CreateXPMPieces() fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize); exit(1); } - for (piece = (int) WhitePawn; piece <= (int) WhiteKing; piece++) { + for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) { for (kind=0; kind<4; kind++) { if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow, pieces->xpm[piece][kind], - &(xpmPieceBitmap[kind][piece]), + &(xpmPieceBitmap2[kind][piece]), NULL, &attr)) != 0) { fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"), r, buf); exit(1); } + if(piece <= (int) WhiteKing) + xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece]; } } useImageSqs = 0; @@ -3808,30 +3967,47 @@ void CreateXPMPieces() fprintf(stderr, _("\nLoading XPMs...\n")); /* Load pieces */ - for (piece = (int) WhitePawn; piece <= (int) WhiteKing; piece++) { + for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) { fprintf(stderr, "%d ", piece+1); for (kind=0; kind<4; kind++) { - sprintf(buf, "%s/%c%s%u.xpm", + snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm", ExpandPathName(appData.pixmapDirectory), - ToLower(PieceToChar((ChessSquare)piece)), + piece > (int) WhiteKing ? "w" : "", + pieceBitmapNames[piece], xpmkind[kind], ss); if (appData.debugMode) { fprintf(stderr, _("(File:%s:) "), buf); } if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf, - &(xpmPieceBitmap[kind][piece]), + &(xpmPieceBitmap2[kind][piece]), NULL, &attr)) != 0) { - fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), - r, buf); - exit(1); + if(piece != (int)WhiteKing && piece > (int)WhiteQueen) { + // [HGM] missing: read of unorthodox piece failed; substitute King. + snprintf(buf, sizeof(buf), "%s/k%s%u.xpm", + ExpandPathName(appData.pixmapDirectory), + xpmkind[kind], ss); + if (appData.debugMode) { + fprintf(stderr, _("(Replace by File:%s:) "), buf); + } + r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf, + &(xpmPieceBitmap2[kind][piece]), + NULL, &attr); + } + if (r != 0) { + fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), + r, buf); + exit(1); + } } + if(piece <= (int) WhiteKing) + xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece]; } } /* Load light and dark squares */ /* If the LSQ and DSQ pieces don't exist, we will draw them with solid squares. */ fprintf(stderr, _("light square ")); - sprintf(buf, "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss); + snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss); if (access(buf, 0) != 0) { useImageSqs = 0; } else { @@ -3845,7 +4021,7 @@ void CreateXPMPieces() exit(1); } fprintf(stderr, _("dark square ")); - sprintf(buf, "%s/dsq%u.xpm", + snprintf(buf, sizeof(buf), "%s/dsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss); if (appData.debugMode) { fprintf(stderr, _("(File:%s:) "), buf); @@ -3876,10 +4052,13 @@ void CreatePieces() buffering bug */ for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) { - for (piece = (int) WhitePawn; piece <= (int) WhiteKing; piece++) { - sprintf(buf, "%c%u%c.bm", ToLower(PieceToChar((ChessSquare)piece)), + for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) { + sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "", + pieceBitmapNames[piece], ss, kind == SOLID ? 's' : 'o'); - ReadBitmap(&pieceBitmap[kind][piece], buf, NULL, ss, ss); + ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss); + if(piece <= (int)WhiteKing) + pieceBitmap[kind][piece] = pieceBitmap2[kind][piece]; } } @@ -3901,11 +4080,14 @@ void CreatePieces() while (bib->squareSize != ss && bib->squareSize != 0) bib++; for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) { - for (piece = (int) WhitePawn; piece <= (int) WhiteKing; piece++) { - sprintf(buf, "%c%u%c.bm", ToLower(PieceToChar((ChessSquare)piece)), + for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) { + sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "", + pieceBitmapNames[piece], ss, kind == SOLID ? 's' : 'o'); - ReadBitmap(&pieceBitmap[kind][piece], buf, + ReadBitmap(&pieceBitmap2[kind][piece], buf, bib->bits[kind][piece], ss, ss); + if(piece <= (int)WhiteKing) + pieceBitmap[kind][piece] = pieceBitmap2[kind][piece]; } } @@ -3931,20 +4113,21 @@ void ReadBitmap(pm, name, bits, wreq, hreq) strcat(fullname, name); errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname, &w, &h, pm, &x_hot, &y_hot); + fprintf(stderr, "load %s\n", name); if (errcode != BitmapSuccess) { switch (errcode) { case BitmapOpenFailed: - sprintf(msg, _("Can't open bitmap file %s"), fullname); + snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname); break; case BitmapFileInvalid: - sprintf(msg, _("Invalid bitmap in file %s"), fullname); + snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname); break; case BitmapNoMemory: - sprintf(msg, _("Ran out of memory reading bitmap file %s"), + snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"), fullname); break; default: - sprintf(msg, _("Unknown XReadBitmapFile error %d on file %s"), + snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"), errcode, fullname); break; } @@ -4200,7 +4383,7 @@ void SetupDropMenu() && !appData.icsActive)); count = 0; while (p && *p++ == dmEnables[i].piece) count++; - sprintf(label, "%s %d", dmEnables[i].widget, count); + snprintf(label, sizeof(label), "%s %d", dmEnables[i].widget, count); j = 0; XtSetArg(args[j], XtNlabel, label); j++; XtSetValues(entry, args, j); @@ -4620,17 +4803,6 @@ void DrawSquare(row, column, piece, do_flash) DrawFunc drawfunc; int flash_delay; - if(gameInfo.variant == VariantShogi) { // [HGM] shogi: in shogi Q is used for Lance - if(piece == WhiteQueen) piece = WhiteLance; else - if(piece == BlackQueen) piece = BlackLance; - } -#ifdef GOTHIC - else if(gameInfo.variant == VariantGothic) { // [HGM] shogi: in Gothic Chancelor has alternative look - if(piece == WhiteMarshall) piece = WhiteSilver; else - if(piece == BlackMarshall) piece = BlackSilver; - } -#endif - /* Calculate delay in milliseconds (2-delays per complete flash) */ flash_delay = 500 / appData.flashRate; @@ -5014,14 +5186,18 @@ void HandleUserMove(w, event, prms, nprms) x >= 0 && y >= 0) { ChessSquare fromP; ChessSquare toP; + int frc; /* Check if clicking again on the same color piece */ fromP = boards[currentMove][fromY][fromX]; toP = boards[currentMove][y][x]; - if ((WhitePawn <= fromP && fromP < WhiteKing && // [HGM] this test should go, as UserMoveTest now does it. - WhitePawn <= toP && toP <= WhiteKing) || // For now I made it less critical by exempting King - (BlackPawn <= fromP && fromP < BlackKing && // moves, to not interfere with FRC castlings. - BlackPawn <= toP && toP <= BlackKing)) { + frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom; + if ((WhitePawn <= fromP && fromP <= WhiteKing && // [HGM] this test should go, as UserMoveTest now does it. + WhitePawn <= toP && toP <= WhiteKing && + !(fromP == WhiteKing && toP == WhiteRook && frc)) || + (BlackPawn <= fromP && fromP <= BlackKing && + BlackPawn <= toP && toP <= BlackKing && + !(fromP == BlackKing && toP == BlackRook && frc))) { /* Clicked again on same color piece -- changed his mind */ second = (x == fromX && y == fromY); if (appData.highlightDragging) { @@ -5784,11 +5960,12 @@ void PromotionPopUp() layoutArgs, XtNumber(layoutArgs)); j = 0; - XtSetArg(args[j], XtNlabel, _("Promote pawn to what?")); j++; + XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++; XtSetArg(args[j], XtNborderWidth, 0); j++; dialog = XtCreateManagedWidget("promotion", dialogWidgetClass, layout, args, j); + if(gameInfo.variant != VariantShogi) { XawDialogAddButton(dialog, _("Queen"), PromotionCallback, (XtPointer) dialog); XawDialogAddButton(dialog, _("Rook"), PromotionCallback, @@ -5802,6 +5979,21 @@ void PromotionPopUp() XawDialogAddButton(dialog, _("King"), PromotionCallback, (XtPointer) dialog); } + if(gameInfo.variant == VariantCapablanca || + gameInfo.variant == VariantGothic || + gameInfo.variant == VariantCapaRandom) { + XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, + (XtPointer) dialog); + XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, + (XtPointer) dialog); + } + } else // [HGM] shogi + { + XawDialogAddButton(dialog, _("Promote"), PromotionCallback, + (XtPointer) dialog); + XawDialogAddButton(dialog, _("Defer"), PromotionCallback, + (XtPointer) dialog); + } XawDialogAddButton(dialog, _("cancel"), PromotionCallback, (XtPointer) dialog); @@ -5856,6 +6048,10 @@ void PromotionCallback(w, client_data, call_data) return; } else if (strcmp(name, _("Knight")) == 0) { promoChar = 'n'; + } else if (strcmp(name, _("Promote")) == 0) { + promoChar = '+'; + } else if (strcmp(name, _("Defer")) == 0) { + promoChar = '='; } else { promoChar = ToLower(name[0]); } @@ -6272,7 +6468,7 @@ void CopyPositionProc(w, event, prms, nprms) int ret; if (selected_fen_position) free(selected_fen_position); - selected_fen_position = (char *)PositionToFEN(currentMove,1); + selected_fen_position = (char *)PositionToFEN(currentMove, NULL); if (!selected_fen_position) return; ret = XtOwnSelection(menuBarWidget, XA_PRIMARY, CurrentTime, @@ -6463,7 +6659,7 @@ void AnalyzeModeProc(w, event, prms, nprms) char buf[MSG_SIZ]; if (!first.analysisSupport) { - sprintf(buf, _("%s does not support analysis"), first.tidy); + snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy); DisplayError(buf, 0); return; } @@ -6505,7 +6701,7 @@ void AnalyzeFileProc(w, event, prms, nprms) { if (!first.analysisSupport) { char buf[MSG_SIZ]; - sprintf(buf, _("%s does not support analysis"), first.tidy); + snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy); DisplayError(buf, 0); return; } @@ -7341,7 +7537,7 @@ void InfoProc(w, event, prms, nprms) Cardinal *nprms; { char buf[MSG_SIZ]; - sprintf(buf, "xterm -e info --directory %s --directory . -f %s &", + snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &", INFODIR, INFOFILE); system(buf); } @@ -7358,7 +7554,7 @@ void ManProc(w, event, prms, nprms) name = prms[0]; else name = "xboard"; - sprintf(buf, "xterm -e man %s &", name); + snprintf(buf, sizeof(buf), "xterm -e man %s &", name); system(buf); } @@ -7392,13 +7588,12 @@ void AboutProc(w, event, prms, nprms) #else char *zippy = ""; #endif - sprintf(buf, "%s%s\n\n%s\n%s\n%s\n%s\n\n%s%s\n%s", + snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s", programVersion, zippy, "Copyright 1991 Digital Equipment Corporation", - "Enhancements Copyright 1992-2001 Free Software Foundation", + "Enhancements Copyright 1992-2009 Free Software Foundation", "Enhancements Copyright 2005 Alessandro Scotti", - "Enhancements Copyright 2007-2008 H.G.Muller", - PRODUCT, " is free software and carries NO WARRANTY;", + PACKAGE, " is free software and carries NO WARRANTY;", "see the file COPYING for more information."); ErrorPopUp(_("About XBoard"), buf, FALSE); } @@ -7451,7 +7646,7 @@ void DisplayMessage(message, extMessage) if (extMessage) { if (*message) { - sprintf(buf, "%s %s", message, extMessage); + snprintf(buf, sizeof(buf), "%s %s", message, extMessage); message = buf; } else { message = extMessage; @@ -7481,11 +7676,11 @@ void DisplayTitle(text) strcpy(icon, text); strcpy(title, text); } else if (appData.icsActive) { - sprintf(icon, "%s", appData.icsHost); - sprintf(title, "%s: %s", programName, appData.icsHost); + snprintf(icon, sizeof(icon), "%s", appData.icsHost); + snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost); } else if (appData.cmailGameName[0] != NULLCHAR) { - sprintf(icon, "%s", "CMail"); - sprintf(title, "%s: %s", programName, "CMail"); + snprintf(icon, sizeof(icon), "%s", "CMail"); + snprintf(title,sizeof(title), "%s: %s", programName, "CMail"); #ifdef GOTHIC // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it } else if (gameInfo.variant == VariantGothic) { @@ -7502,7 +7697,7 @@ void DisplayTitle(text) strcpy(title, programName); } else { strcpy(icon, first.tidy); - sprintf(title, "%s: %s", programName, first.tidy); + snprintf(title,sizeof(title), "%s: %s", programName, first.tidy); } i = 0; XtSetArg(args[i], XtNiconName, (XtArgVal) icon); i++; @@ -7526,7 +7721,7 @@ void DisplayError(message, error) fprintf(stderr, "%s: %s: %s\n", programName, message, strerror(error)); } - sprintf(buf, "%s: %s", message, strerror(error)); + snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error)); message = buf; } ErrorPopUp(_("Error"), message, FALSE); @@ -7562,7 +7757,7 @@ void DisplayFatalError(message, error, status) } else { fprintf(stderr, "%s: %s: %s\n", programName, message, strerror(error)); - sprintf(buf, "%s: %s", message, strerror(error)); + snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error)); message = buf; } if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) { @@ -7751,7 +7946,7 @@ PlaySound(name) putc(BELLCHAR, stderr); } else { char buf[2048]; - sprintf(buf, "%s '%s' &", appData.soundProgram, name); + snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name); system(buf); } } @@ -7949,6 +8144,9 @@ void ScheduleDelayedEvent(cb, millisec) DelayedEventCallback cb; long millisec; { + if(delayedEventTimerXID && delayedEventCallback == cb) + // [HGM] alive: replace, rather than add or flush identical event + XtRemoveTimeOut(delayedEventTimerXID); delayedEventCallback = cb; delayedEventTimerXID = XtAppAddTimeOut(appContext, millisec, @@ -8081,6 +8279,14 @@ DisplayTimerLabel(w, color, timer, highlight) char buf[MSG_SIZ]; Arg args[16]; + /* check for low time warning */ + Pixel foregroundOrWarningColor = timerForegroundPixel; + + if (timer > 0 && + appData.lowTimeWarning && + (timer / 1000) < appData.icsAlarmTime) + foregroundOrWarningColor = lowTimeWarningColor; + if (appData.clockMode) { sprintf(buf, "%s: %s", color, TimeString(timer)); XtSetArg(args[0], XtNlabel, buf); @@ -8090,11 +8296,12 @@ DisplayTimerLabel(w, color, timer, highlight) } if (highlight) { - XtSetArg(args[1], XtNbackground, timerForegroundPixel); + + XtSetArg(args[1], XtNbackground, foregroundOrWarningColor); XtSetArg(args[2], XtNforeground, timerBackgroundPixel); } else { XtSetArg(args[1], XtNbackground, timerBackgroundPixel); - XtSetArg(args[2], XtNforeground, timerForegroundPixel); + XtSetArg(args[2], XtNforeground, foregroundOrWarningColor); } XtSetValues(w, args, 3); @@ -8270,9 +8477,9 @@ int OpenTelnet(host, port, pr) char cmdLine[MSG_SIZ]; if (port[0] == NULLCHAR) { - sprintf(cmdLine, "%s %s", appData.telnetProgram, host); + snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host); } else { - sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port); + snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port); } return StartChildProcess(cmdLine, "", pr); } @@ -8556,6 +8763,8 @@ int OutputToProcessDelayed(pr, message, count, outError, msdelay) and dark squares, and all pieces must use the same background square colors/images. */ +static int xpmDone = 0; + static void CreateAnimMasks (pieceDepth) int pieceDepth; @@ -8586,6 +8795,7 @@ CreateAnimMasks (pieceDepth) for (piece = WhitePawn; piece <= BlackKing; piece++) { /* Begin with empty mask */ + if(!xpmDone) // [HGM] pieces: keep using existing xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow, squareSize, squareSize, 1); XSetFunction(xDisplay, maskGC, GXclear); @@ -8669,11 +8879,11 @@ InitAnimState (anim, info) static void CreateAnimVars () { - static int done = 0; + static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes XWindowAttributes info; - if (done) return; - done = 1; + if (xpmDone && gameInfo.variant == old) return; + if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet XGetWindowAttributes(xDisplay, xBoardWindow, &info); InitAnimState(&game, &info); @@ -8682,6 +8892,7 @@ CreateAnimVars () /* For XPM pieces, we need bitmaps to use as masks. */ if (useImages) CreateAnimMasks(info.depth); + xpmDone = 1; } #ifndef HAVE_USLEEP @@ -8907,7 +9118,7 @@ SelectGCMask(piece, clip, outline, mask) #if HAVE_LIBXPM *mask = xpmMask[piece]; #else - *mask = ximMaskPm[piece%(int)BlackPawn]; + *mask = ximMaskPm[piece]; #endif } else { *mask = *pieceToSolid(piece); @@ -8956,7 +9167,7 @@ OverlayPiece(piece, clip, outline, dest) kind = 0; else kind = 2; - XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn], + XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, clip, 0, 0, squareSize, squareSize, 0, 0); @@ -9100,6 +9311,10 @@ AnimateMove(board, fromX, fromY, toX, toY) if (!appData.animate || appData.blindfold) return; + if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing || + board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing) + return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square + if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return; piece = board[fromY][fromX]; if (piece >= EmptySquare) return;