worked on premove bug
[xboard.git] / xboard.c
index ed688da..0e8b132 100644 (file)
--- 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,12 +1229,18 @@ 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" },
     { "defaultFrcPosition", "defaultFrcPositon", XtRInt,
        sizeof(int), XtOffset(AppDataPtr, defaultFrcPosition),
        XtRImmediate, (XtPointer) -1},
+    { "gameListTags", "gameListTags", XtRString,
+        sizeof(String), XtOffset(AppDataPtr, gameListTags),
+       XtRImmediate, (XtPointer) GLT_DEFAULT_TAGS },
 
     // [HGM] 4.3.xx options
     { "boardWidth", "boardWidth", XtRInt,
@@ -1322,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},
@@ -1332,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,
@@ -1370,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[] = {
@@ -1592,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" },
@@ -1660,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 },
@@ -1684,6 +1722,7 @@ XrmOptionDescRec shellOptions[] = {
     { "-defaultCacheSizeEGTB", "defaultCacheSizeEGTB", XrmoptionSepArg, NULL },
     { "-defaultPathEGTB", "defaultPathEGTB", XrmoptionSepArg, NULL },
     { "-defaultFrcPosition", "defaultFrcPosition", XrmoptionSepArg, NULL },
+    { "-gameListTags", "gameListTags", XrmoptionSepArg, NULL },
     // [HGM] I am sure AS added many more options, but we have to fish them out, from the list in winboard.c
 
     /* [HGM,HR] User-selectable board size */
@@ -1730,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 },
 };
 
 
@@ -2132,7 +2174,7 @@ CatchDeleteWindow(Widget w, String procname)
 {
   char buf[MSG_SIZ];
   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
-  sprintf(buf, "<Message>WM_PROTOCOLS: %s() \n", procname);
+  snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
   XtAugmentTranslations(w, XtParseTranslationTable(buf));
 }
 
@@ -2219,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;
@@ -2280,7 +2395,6 @@ main(argc, argv)
 #endif
 #endif
 
-
     setbuf(stdout, NULL);
     setbuf(stderr, NULL);
     debugFP = stderr;
@@ -2293,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 =
@@ -2304,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");
@@ -2322,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)  {
@@ -2561,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) {
@@ -3039,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
@@ -3651,6 +3806,9 @@ void loadXIM(xim, xmask, filename, dest, mask)
     }
 }
 
+
+char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
+
 void CreateXIMPieces()
 {
     int piece, kind;
@@ -3681,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),
@@ -3696,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 {
@@ -3718,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);
@@ -3783,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;
@@ -3804,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 {
@@ -3841,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);
@@ -3872,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];
        }
     }
 
@@ -3897,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];
        }
     }
 
@@ -3927,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;
            }
@@ -4196,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);
@@ -4616,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;
 
@@ -5010,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) {
@@ -5780,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,
@@ -5798,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);
 
@@ -5852,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]);
     }
@@ -6268,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,
@@ -6459,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;
     }
@@ -6501,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;
     }
@@ -7337,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);
 }
@@ -7354,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);
 }
 
@@ -7388,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);
 }
@@ -7447,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;
@@ -7477,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) {
@@ -7498,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++;
@@ -7522,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);
@@ -7558,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)) {
@@ -7747,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);
   }
 }
@@ -7945,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,
@@ -8077,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);
@@ -8086,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);
@@ -8266,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);
 }
@@ -8552,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;
@@ -8582,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);
@@ -8665,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);
@@ -8678,6 +8892,7 @@ CreateAnimVars ()
   /* For XPM pieces, we need bitmaps to use as masks. */
   if (useImages)
     CreateAnimMasks(info.depth);
+   xpmDone = 1;
 }
 
 #ifndef HAVE_USLEEP
@@ -8903,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);
@@ -8952,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);
@@ -9096,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;
@@ -9184,7 +9403,7 @@ DragPieceBegin(x, y)
               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
                     corner.x, corner.y, squareSize, squareSize,
-                    0, 0); // [HGM] xh: unstack in stead of grab
+                    0, 0); // [HGM] zh: unstack in stead of grab
        damage[boardY][boardX] = True;
     } else {
        player.dragActive = False;