X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=xboard.c;h=8031466ad0d0af4393e804da4e7a589e3b800d74;hb=ca87afb87619efbaa94fe045670bc916e9ffe3ec;hp=d47a15d0ac74b6d61bc1fd1b2959e463e7851fd4;hpb=d5e1ced63182849f6c1fcaf3abb5d15d437545c5;p=xboard.git diff --git a/xboard.c b/xboard.c index d47a15d..8031466 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: @@ -495,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) @@ -696,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 @@ -1013,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 }, @@ -1220,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" }, @@ -1335,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}, @@ -1389,6 +1401,15 @@ XtResource clientResources[] = { { "delayAfterQuit", "delayAfterQuit", XtRInt, sizeof(int), XtOffset(AppDataPtr, delayAfterQuit), XtRImmediate, (XtPointer) 0}, + { "keepAlive", "keepAlive", XtRInt, + sizeof(int), XtOffset(AppDataPtr, keepAlive), + XtRImmediate, (XtPointer) 0}, + { "forceIllegalMoves", "forceIllegalMoves", XtRBoolean, + sizeof(Boolean), XtOffset(AppDataPtr, forceIllegal), + XtRImmediate, (XtPointer) False}, + { "keepLineBreaksICS", "keepLineBreaksICS", XtRBoolean, + sizeof(Boolean), XtOffset(AppDataPtr, noJoin), + XtRImmediate, (XtPointer) True}, }; XrmOptionDescRec shellOptions[] = { @@ -1688,6 +1709,7 @@ XrmOptionDescRec shellOptions[] = { { "-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 }, @@ -1755,6 +1777,9 @@ XrmOptionDescRec shellOptions[] = { { "-secondOptions", "secondOptions", XrmoptionSepArg, NULL }, { "-firstNeedsNoncompliantFEN", "firstNeedsNoncompliantFEN", XrmoptionSepArg, NULL }, { "-secondNeedsNoncompliantFEN", "secondNeedsNoncompliantFEN", XrmoptionSepArg, NULL }, + { "-keepAlive", "keepAlive", XrmoptionSepArg, NULL }, + { "-forceIllegalMoves", "forceIllegalMoves", XrmoptionNoArg, "True" }, + { "-keepLineBreaksICS", "keepLineBreaksICS", XrmoptionSepArg, NULL }, }; @@ -2244,9 +2269,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; @@ -2298,13 +2396,7 @@ main(argc, argv) argvCopy[j] = NULL; argv = argvCopy; argc = j; -#if 0 - if(appData.debugMode,1) { // OK, appData is not initialized here yet... - for(i=0; i 1) { + if (argc > 1) + { /* left over command line arguments, print out help and exit. + * Use two columns to print help + */ 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++) + { + /* print first column */ + j = fprintf(stderr, " %s%s", shellOptions[i].option, + (shellOptions[i].argKind == XrmoptionSepArg + ? " ARG" : "")); + /* print second column and end line */ + 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"); if (p == NULL) p = "/tmp"; @@ -2354,6 +2460,28 @@ main(argc, argv) 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) { @@ -2380,19 +2508,6 @@ main(argc, argv) gameInfo.variant = StringToVariant(appData.variant); InitPosition(FALSE); -#if 0 - /* - * Determine boardSize - */ - gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] boardsize: make sure we start as 8x8 - -//#ifndef IDSIZE - // [HGM] as long as we have not created the possibility to change size while running, start with requested size - gameInfo.boardWidth = appData.NrFiles > 0 ? appData.NrFiles : 8; - gameInfo.boardHeight = appData.NrRanks > 0 ? appData.NrRanks : 8; - gameInfo.holdingsWidth = appData.holdingsSize > 0 ? 2 : 0; -#endif - #ifdef IDSIZE InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine @@ -2586,6 +2701,10 @@ 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) { @@ -3074,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 @@ -3157,6 +3277,11 @@ ResetFrontEnd() return; } +void NotifyFrontendLogin() +{ + // placeholder +} + typedef struct { char *name; Boolean value; @@ -3686,6 +3811,9 @@ void loadXIM(xim, xmask, filename, dest, mask) } } + +char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl"; + void CreateXIMPieces() { int piece, kind; @@ -3716,13 +3844,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, "."); - snprintf(buf, sizeof(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), @@ -3731,8 +3860,10 @@ 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," "); } @@ -3778,14 +3909,6 @@ void CreateXPMPieces() static char *xpmkind[] = { "ll", "ld", "dl", "dd" }; XpmColorSymbol symbols[4]; -#if 0 - /* Apparently some versions of Xpm don't define XpmFormat at all --tpm */ - if (appData.debugMode) { - fprintf(stderr, "XPM Library Version: %d.%d%c\n", - XpmFormat, XpmVersion, (char)('a' + XpmRevision - 1)); - } -#endif - /* The XSynchronize calls were copied from CreatePieces. Not sure if needed, but can't hurt */ XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */ @@ -3818,17 +3941,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; @@ -3839,23 +3964,40 @@ 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++) { - snprintf(buf, sizeof(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 */ @@ -3907,10 +4049,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]; } } @@ -3932,11 +4077,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]; } } @@ -3962,6 +4110,7 @@ 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: @@ -3989,14 +4138,7 @@ void ReadBitmap(pm, name, bits, wreq, hreq) return; } } - if (bits == NULL) { -#if 0 - fprintf(stderr, _("%s: No built-in bitmap for %s; giving up\n"), - programName, name); - exit(1); -#endif - ; // [HGM] bitmaps: make it non-fatal if we have no bitmap; - } else { + if (bits != NULL) { *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits, wreq, hreq); } @@ -4651,17 +4793,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; @@ -4990,10 +5121,40 @@ void HandleUserMove(w, event, prms, nprms) { int x, y; Boolean saveAnimate; - static int second = 0; + static int second = 0, promotionChoice = 0; + ChessMove moveType; if (w != boardWidget || errorExitStatus != -1) return; + x = EventToSquare(event->xbutton.x, BOARD_WIDTH); + y = EventToSquare(event->xbutton.y, BOARD_HEIGHT); + if (!flipView && y >= 0) { + y = BOARD_HEIGHT - 1 - y; + } + if (flipView && x >= 0) { + x = BOARD_WIDTH - 1 - x; + } + + if(promotionChoice) { // we are waiting for a click to indicate promotion piece + if(event->type == ButtonRelease) return; // ignore upclick of click-click destination + promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel + if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y); + if(gameInfo.holdingsWidth && + (WhiteOnMove(currentMove) + ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0 + : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) { + // click in right holdings, for determining promotion piece + ChessSquare p = boards[currentMove][y][x]; + if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p); + if(p != EmptySquare) { + FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p))); + fromX = fromY = -1; + return; + } + } + DrawPosition(FALSE, boards[currentMove]); + return; + } if (event->type == ButtonPress) ErrorPopDown(); if (promotionUp) { @@ -5008,15 +5169,6 @@ void HandleUserMove(w, event, prms, nprms) } } - x = EventToSquare(event->xbutton.x, BOARD_WIDTH); - y = EventToSquare(event->xbutton.y, BOARD_HEIGHT); - if (!flipView && y >= 0) { - y = BOARD_HEIGHT - 1 - y; - } - if (flipView && x >= 0) { - x = BOARD_WIDTH - 1 - x; - } - /* [HGM] holdings: next 5 lines: ignore all clicks between board and holdings */ if(event->type == ButtonPress && ( x == BOARD_LEFT-1 || x == BOARD_RGHT @@ -5026,7 +5178,7 @@ void HandleUserMove(w, event, prms, nprms) if (fromX == -1) { if (event->type == ButtonPress) { - /* First square */ + /* First square, prepare to drag */ if (OKToStartUserMove(x, y)) { fromX = x; fromY = y; @@ -5041,35 +5193,8 @@ void HandleUserMove(w, event, prms, nprms) } /* fromX != -1 */ - if (event->type == ButtonPress && gameMode != EditPosition && - x >= 0 && y >= 0) { - ChessSquare fromP; - ChessSquare toP; - - /* 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)) { - /* Clicked again on same color piece -- changed his mind */ - second = (x == fromX && y == fromY); - if (appData.highlightDragging) { - SetHighlights(x, y, -1, -1); - } else { - ClearHighlights(); - } - if (OKToStartUserMove(x, y)) { - fromX = x; - fromY = y; - DragPieceBegin(event->xbutton.x, event->xbutton.y); - } - return; - } - } - if (event->type == ButtonRelease && x == fromX && y == fromY) { + /* Click on single square in stead of drag-drop */ DragPieceEnd(event->xbutton.x, event->xbutton.y); if (appData.animateDragging) { /* Undo animation damage if any */ @@ -5089,7 +5214,34 @@ void HandleUserMove(w, event, prms, nprms) return; } - /* Completed move */ + moveType = UserMoveTest(fromX, fromY, x, y, NULLCHAR, event->type == ButtonRelease); + + if (moveType == Comment) { // kludge for indicating capture-own on Press + /* Clicked again on same color piece -- changed his mind */ + /* note that re-clicking same square always hits same color piece */ + second = (x == fromX && y == fromY); + if (appData.highlightDragging) { + SetHighlights(x, y, -1, -1); + } else { + ClearHighlights(); + } + if (OKToStartUserMove(x, y)) { + fromX = x; + fromY = y; + DragPieceBegin(event->xbutton.x, event->xbutton.y); + } + return; + } + + if(moveType == AmbiguousMove) { // kludge to indicate edit-position move + fromX = fromY = -1; + ClearHighlights(); + DragPieceEnd(event->xbutton.x, event->xbutton.y); + DrawPosition(FALSE, boards[currentMove]); + return; + } + + /* Complete move; (x,y) is now different from (fromX, fromY) on both Press and Release */ toX = x; toY = y; saveAnimate = appData.animate; @@ -5111,22 +5263,38 @@ void HandleUserMove(w, event, prms, nprms) /* Don't animate move and drag both */ appData.animate = FALSE; } - if (IsPromotion(fromX, fromY, toX, toY)) { - if (appData.alwaysPromoteToQueen) { - UserMoveEvent(fromX, fromY, toX, toY, 'q'); + if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight || + (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) && + appData.alwaysPromoteToQueen) { // promotion, but no choice + FinishMove(moveType, fromX, fromY, toX, toY, 'q'); + } else + if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) { + SetHighlights(fromX, fromY, toX, toY); + if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { + // [HGM] super: promotion to captured piece selected from holdings + ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX]; + promotionChoice = TRUE; + // kludge follows to temporarily execute move on display, without promoting yet + boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank + boards[currentMove][toY][toX] = p; + DrawPosition(FALSE, boards[currentMove]); + boards[currentMove][fromY][fromX] = p; // take back, but display stays + boards[currentMove][toY][toX] = q; + DisplayMessage("Click in holdings to choose piece", ""); + return; + } + PromotionPopUp(); + goto skipClearingFrom; // the skipped stuff is done asynchronously by PromotionCallback + } else + if(moveType != ImpossibleMove) { // valid move, but no promotion + FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR); + } else { // invalid move; could have set premove + ClearHighlights(); + } if (!appData.highlightLastMove || gotPremove) ClearHighlights(); if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); fromX = fromY = -1; - } else { - SetHighlights(fromX, fromY, toX, toY); - PromotionPopUp(); - } - } else { - UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR); - if (!appData.highlightLastMove || gotPremove) ClearHighlights(); - if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); - fromX = fromY = -1; - } +skipClearingFrom: appData.animate = saveAnimate; if (appData.animate || appData.animateDragging) { /* Undo animation damage if needed */ @@ -5185,12 +5353,8 @@ Widget CommentCreate(name, text, mutable, callback, lines) XtSetArg(args[j], XtNright, XtChainRight); j++; XtSetArg(args[j], XtNresizable, True); j++; XtSetArg(args[j], XtNwidth, bw_width); j++; /*force wider than buttons*/ -#if 0 - XtSetArg(args[j], XtNscrollVertical, XawtextScrollWhenNeeded); j++; -#else /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */ XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++; -#endif XtSetArg(args[j], XtNautoFill, True); j++; XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++; edit = @@ -5343,12 +5507,8 @@ Widget MiscCreate(name, text, mutable, callback, lines) XtSetArg(args[j], XtNleft, XtChainLeft); j++; XtSetArg(args[j], XtNright, XtChainRight); j++; XtSetArg(args[j], XtNresizable, True); j++; -#if 0 - XtSetArg(args[j], XtNscrollVertical, XawtextScrollWhenNeeded); j++; -#else /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */ XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways); j++; -#endif XtSetArg(args[j], XtNautoFill, True); j++; XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++; edit = @@ -5815,11 +5975,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, @@ -5833,6 +5994,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); @@ -5887,11 +6063,15 @@ 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]); } - UserMoveEvent(fromX, fromY, toX, toY, promoChar); + FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar); if (!appData.highlightLastMove || gotPremove) ClearHighlights(); if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY); @@ -6067,15 +6247,6 @@ void ModeHighlight() args, 1); if (appData.showButtonBar) { -#if 0 - if (pausing) { - XtSetArg(args[0], XtNbackground, buttonForegroundPixel); - XtSetArg(args[1], XtNforeground, buttonBackgroundPixel); - } else { - XtSetArg(args[0], XtNbackground, buttonBackgroundPixel); - XtSetArg(args[1], XtNforeground, buttonForegroundPixel); - } -#else /* Always toggle, don't set. Previous code messes up when invoked while the button is pressed, as releasing it toggles the state again. */ @@ -6088,7 +6259,6 @@ void ModeHighlight() XtSetArg(args[0], XtNbackground, oldfg); XtSetArg(args[1], XtNforeground, oldbg); } -#endif XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2); } } @@ -7333,16 +7503,6 @@ void ShowThinkingProc(w, event, prms, nprms) appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent ShowThinkingEvent(); -#if 0 - // [HGM] thinking: currently no suc menu item; replaced by Hide Thinking (From Human) - if (appData.showThinking) { - XtSetArg(args[0], XtNleftBitmap, xMarkPixmap); - } else { - XtSetArg(args[0], XtNleftBitmap, None); - } - XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Thinking"), - args, 1); -#endif } void HideThinkingProc(w, event, prms, nprms) @@ -7428,7 +7588,7 @@ void AboutProc(w, event, prms, nprms) "Copyright 1991 Digital Equipment Corporation", "Enhancements Copyright 1992-2009 Free Software Foundation", "Enhancements Copyright 2005 Alessandro Scotti", - 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); } @@ -7476,19 +7636,34 @@ void Iconify(w, event, prms, nprms) void DisplayMessage(message, extMessage) char *message, *extMessage; { - char buf[MSG_SIZ]; - Arg arg; - - if (extMessage) { - if (*message) { - snprintf(buf, sizeof(buf), "%s %s", message, extMessage); - message = buf; - } else { - message = extMessage; - } - } - XtSetArg(arg, XtNlabel, message); - XtSetValues(messageWidget, &arg, 1); + /* display a message in the message widget */ + + char buf[MSG_SIZ]; + Arg arg; + + if (extMessage) + { + if (*message) + { + snprintf(buf, sizeof(buf), "%s %s", message, extMessage); + message = buf; + } + else + { + message = extMessage; + }; + }; + + /* need to test if messageWidget already exists, since this function + can also be called during the startup, if for example a Xresource + is not set up correctly */ + if(messageWidget) + { + XtSetArg(arg, XtNlabel, message); + XtSetValues(messageWidget, &arg, 1); + }; + + return; } void DisplayTitle(text) @@ -7512,7 +7687,7 @@ void DisplayTitle(text) strcpy(title, text); } else if (appData.icsActive) { snprintf(icon, sizeof(icon), "%s", appData.icsHost); - snprintf(title, sizeof(title),"%s: %s", programName, appData.icsHost); + snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost); } else if (appData.cmailGameName[0] != NULLCHAR) { snprintf(icon, sizeof(icon), "%s", "CMail"); snprintf(title,sizeof(title), "%s: %s", programName, "CMail"); @@ -7556,7 +7731,7 @@ void DisplayError(message, error) fprintf(stderr, "%s: %s: %s\n", programName, message, strerror(error)); } - snprintf(buf,sizeof(buf), "%s: %s", message, strerror(error)); + snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error)); message = buf; } ErrorPopUp(_("Error"), message, FALSE); @@ -7979,6 +8154,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, @@ -8111,12 +8289,12 @@ 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) - + if (timer > 0 && + appData.lowTimeWarning && + (timer / 1000) < appData.icsAlarmTime) foregroundOrWarningColor = lowTimeWarningColor; if (appData.clockMode) { @@ -8311,7 +8489,7 @@ int OpenTelnet(host, port, pr) if (port[0] == NULLCHAR) { snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host); } else { - snprintf(cmdLine,sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port); + snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port); } return StartChildProcess(cmdLine, "", pr); } @@ -8595,6 +8773,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; @@ -8625,6 +8805,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); @@ -8708,11 +8889,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); @@ -8721,6 +8902,7 @@ CreateAnimVars () /* For XPM pieces, we need bitmaps to use as masks. */ if (useImages) CreateAnimMasks(info.depth); + xpmDone = 1; } #ifndef HAVE_USLEEP @@ -8751,12 +8933,7 @@ FrameDelay (time) delay.it_interval.tv_usec = delay.it_value.tv_usec = (time % 1000) * 1000; setitimer(ITIMER_REAL, &delay, NULL); -#if 0 - /* Ugh -- busy-wait! --tpm */ - while (frameWaiting); -#else while (frameWaiting) pause(); -#endif delay.it_interval.tv_sec = delay.it_value.tv_sec = 0; delay.it_interval.tv_usec = delay.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &delay, NULL); @@ -8946,7 +9123,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); @@ -8995,7 +9172,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); @@ -9139,6 +9316,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; @@ -9201,19 +9382,10 @@ DragPieceBegin(x, y) ScreenSquare(boardX, boardY, &corner, &color); player.startSquare = corner; player.startColor = color; -#if 0 - /* Start from exactly where the piece is. This can be confusing - if you start dragging far from the center of the square; most - or all of the piece can be over a different square from the one - the mouse pointer is in. */ - player.mouseDelta.x = x - corner.x; - player.mouseDelta.y = y - corner.y; -#else /* As soon as we start dragging, the piece will jump slightly to be centered over the mouse pointer. */ player.mouseDelta.x = squareSize/2; player.mouseDelta.y = squareSize/2; -#endif /* Initialise animation */ player.dragPiece = PieceForSquare(boardX, boardY); /* Sanity check */