Revert "adding gnu-readline support"
[xboard.git] / xboard.c
index d049062..1246013 100644 (file)
--- a/xboard.c
+++ b/xboard.c
@@ -2,7 +2,7 @@
  * xboard.c -- X front end for XBoard
  *
  * Copyright 1991 by Digital Equipment Corporation, Maynard,
- * Massachusetts. 
+ * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
  * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
@@ -191,6 +191,7 @@ extern char *getenv();
 
 #include "frontend.h"
 #include "backend.h"
+#include "backendz.h"
 #include "moves.h"
 #include "xboard.h"
 #include "childio.h"
@@ -241,6 +242,7 @@ RETSIGTYPE TermSizeSigHandler P((int sig));
 void CreateGCs P((void));
 void CreateXIMPieces P((void));
 void CreateXPMPieces P((void));
+void CreateXPMBoard P((char *s, int n));
 void CreatePieces P((void));
 void CreatePieceMenus P((void));
 Widget CreateMenuBar P((Menu *mb));
@@ -262,6 +264,10 @@ void AnimateUserMove P((Widget w, XEvent * event,
                     String * params, Cardinal * nParams));
 void HandlePV P((Widget w, XEvent * event,
                     String * params, Cardinal * nParams));
+void SelectPV P((Widget w, XEvent * event,
+                    String * params, Cardinal * nParams));
+void StopPV P((Widget w, XEvent * event,
+                    String * params, Cardinal * nParams));
 void WhiteClock P((Widget w, XEvent *event,
                   String *prms, Cardinal *nprms));
 void BlackClock P((Widget w, XEvent *event,
@@ -270,6 +276,8 @@ void DrawPositionProc P((Widget w, XEvent *event,
                     String *prms, Cardinal *nprms));
 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
                     Board board));
+void CommentClick P((Widget w, XEvent * event,
+                  String * params, Cardinal * nParams));
 void CommentPopUp P((char *title, char *label));
 void CommentPopDown P((void));
 void CommentCallback P((Widget w, XtPointer client_data,
@@ -537,7 +545,8 @@ 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;
+Pixmap xpmBoardBitmap[2];
+int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
 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 */
@@ -746,23 +755,23 @@ MenuItem buttonBar[] = {
 #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_("Elephant"), N_("Cannon"), 
-      N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
+      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_("Elephant"), N_("Cannon"), 
-      N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
+      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, WhiteAlfil,
-       WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0, 
+       WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
        PromotePiece, DemotePiece, EmptySquare, ClearBoard },
     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
        BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
-       BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0, 
+       BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
        PromotePiece, DemotePiece, EmptySquare, ClearBoard },
 };
 
@@ -850,7 +859,8 @@ XtActionsRec boardActions[] = {
     { "HandleUserMove", HandleUserMove },
     { "AnimateUserMove", AnimateUserMove },
     { "HandlePV", HandlePV },
-    { "UnLoadPV", UnLoadPV },
+    { "SelectPV", SelectPV },
+    { "StopPV", StopPV },
     { "FileNameAction", FileNameAction },
     { "AskQuestionProc", AskQuestionProc },
     { "AskQuestionReplyAction", AskQuestionReplyAction },
@@ -859,6 +869,7 @@ XtActionsRec boardActions[] = {
     { "BlackClock", BlackClock },
     { "Iconify", Iconify },
     { "ResetProc", ResetProc },
+    { "NewVariantProc", NewVariantProc },
     { "LoadGameProc", LoadGameProc },
     { "LoadNextGameProc", LoadNextGameProc },
     { "LoadPrevGameProc", LoadPrevGameProc },
@@ -922,6 +933,9 @@ XtActionsRec boardActions[] = {
     { "TruncateGameProc", TruncateGameProc },
     { "MoveNowProc", MoveNowProc },
     { "RetractMoveProc", RetractMoveProc },
+    { "EngineMenuProc", (XtActionProc) EngineMenuProc },
+    { "UciMenuProc", (XtActionProc) UciMenuProc },
+    { "TimeControlProc", (XtActionProc) TimeControlProc },
     { "AlwaysQueenProc", AlwaysQueenProc },
     { "AnimateDraggingProc", AnimateDraggingProc },
     { "AnimateMovingProc", AnimateMovingProc },
@@ -961,6 +975,7 @@ XtActionsRec boardActions[] = {
     { "AboutProc", AboutProc },
     { "DebugProc", DebugProc },
     { "NothingProc", NothingProc },
+    { "CommentClick", (XtActionProc) CommentClick },
     { "CommentPopDown", (XtActionProc) CommentPopDown },
     { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
     { "TagsPopDown", (XtActionProc) TagsPopDown },
@@ -984,34 +999,73 @@ XtActionsRec boardActions[] = {
 };
 
 char globalTranslations[] =
-  ":<Key>R: ResignProc() \n \
-   :<Key>r: ResetProc() \n \
-   :<Key>g: LoadGameProc() \n \
-   :<Key>N: LoadNextGameProc() \n \
-   :<Key>P: LoadPrevGameProc() \n \
-   :<Key>Q: QuitProc() \n \
-   :<Key>F: ToEndProc() \n \
-   :<Key>f: ForwardProc() \n \
-   :<Key>B: ToStartProc() \n \
-   :<Key>b: BackwardProc() \n \
-   :<Key>p: PauseProc() \n \
-   :<Key>d: DrawProc() \n \
-   :<Key>t: CallFlagProc() \n \
-   :<Key>i: Iconify() \n \
-   :<Key>c: Iconify() \n \
-   :<Key>v: FlipViewProc() \n \
-   <KeyDown>Control_L: BackwardProc() \n \
-   <KeyUp>Control_L: ForwardProc() \n \
-   <KeyDown>Control_R: BackwardProc() \n \
-   <KeyUp>Control_R: ForwardProc() \n \
+  ":<Key>F9: ResignProc() \n \
+   :Ctrl<Key>n: ResetProc() \n \
+   :Meta<Key>V: NewVariantProc() \n \
+   :Ctrl<Key>o: LoadGameProc() \n \
+   :Meta<Key>Next: LoadNextGameProc() \n \
+   :Meta<Key>Prior: LoadPrevGameProc() \n \
+   :Ctrl<Key>s: SaveGameProc() \n \
+   :Ctrl<Key>c: CopyGameProc() \n \
+   :Ctrl<Key>v: PasteGameProc() \n \
+   :Ctrl<Key>O: LoadPositionProc() \n \
+   :Shift Meta<Key>Next: LoadNextPositionProc() \n \
+   :Shift Meta<Key>Prior: LoadPrevPositionProc() \n \
+   :Ctrl<Key>S: SavePositionProc() \n \
+   :Ctrl<Key>C: CopyPositionProc() \n \
+   :Ctrl<Key>V: PastePositionProc() \n \
+   :Ctrl<Key>q: QuitProc() \n \
+   :Ctrl<Key>w: MachineWhiteProc() \n \
+   :Ctrl<Key>b: MachineBlackProc() \n \
+   :Ctrl<Key>t: TwoMachinesProc() \n \
+   :Ctrl<Key>a: AnalysisModeProc() \n \
+   :Ctrl<Key>f: AnalyzeFileProc() \n \
+   :Ctrl<Key>e: EditGameProc() \n \
+   :Ctrl<Key>E: EditPositionProc() \n \
+   :Meta<Key>O: EngineOutputProc() \n \
+   :Meta<Key>E: EvalGraphProc() \n \
+   :Meta<Key>G: ShowGameListProc() \n \
+   :Meta<Key>H: ShowMoveListProc() \n \
+   :<Key>Pause: PauseProc() \n \
+   :<Key>F3: AcceptProc() \n \
+   :<Key>F4: DeclineProc() \n \
+   :<Key>F12: RematchProc() \n \
+   :<Key>F5: CallFlagProc() \n \
+   :<Key>F6: DrawProc() \n \
+   :<Key>F7: AdjournProc() \n \
+   :<Key>F8: AbortProc() \n \
+   :<Key>F10: StopObservingProc() \n \
+   :<Key>F11: StopExaminingProc() \n \
+   :Meta Ctrl<Key>F12: DebugProc() \n \
+   :Meta<Key>End: ToEndProc() \n \
+   :Meta<Key>Right: ForwardProc() \n \
+   :Meta<Key>Home: ToStartProc() \n \
+   :Meta<Key>Left: BackwardProc() \n \
+   :Ctrl<Key>m: MoveNowProc() \n \
+   :Ctrl<Key>x: RetractMoveProc() \n \
+   :Meta<Key>J: EngineMenuProc() \n \
+   :Meta<Key>U: UciMenuProc() \n \
+   :Meta<Key>T: TimeControlProc() \n \
+   :Ctrl<Key>Q: AlwaysQueenProc() \n \
+   :Ctrl<Key>F: AutoflagProc() \n \
+   :Ctrl<Key>A: AnimateMovingProc() \n \
+   :Ctrl<Key>P: PonderNextMoveProc() \n \
+   :Ctrl<Key>L: TestLegalityProc() \n \
+   :Ctrl<Key>H: HideThinkingProc() \n \
+   :<Key>-: Iconify() \n \
+   :<Key>F1: ManProc() \n \
+   :<Key>F2: FlipViewProc() \n \
+   <KeyDown>.: BackwardProc() \n \
+   <KeyUp>.: ForwardProc() \n \
    Shift<Key>1: AskQuestionProc(\"Direct command\",\
                                 \"Send to chess program:\",,1) \n \
    Shift<Key>2: AskQuestionProc(\"Direct command\",\
                                 \"Send to second chess program:\",,2) \n";
 
 char boardTranslations[] =
-   "<Btn1Down>: HandleUserMove() \n \
-   <Btn1Up>: HandleUserMove() \n \
+   "<Btn1Down>: HandleUserMove(0) \n \
+   Shift<Btn1Up>: HandleUserMove(1) \n \
+   <Btn1Up>: HandleUserMove(0) \n \
    <Btn1Motion>: AnimateUserMove() \n \
    <Btn3Motion>: HandlePV() \n \
    <Btn3Up>: PieceMenuPopup(menuB) \n \
@@ -1032,6 +1086,10 @@ char ICSInputTranslations[] =
     "<Key>Down: DownKeyProc() \n "
     "<Key>Return: EnterKeyProc() \n";
 
+// [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
+//             as the widget is destroyed before the up-click can call extend-end
+char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
+
 String xboardResources[] = {
     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
@@ -1299,10 +1357,10 @@ extern char *crWhite, * crBlack;
 
 void *
 colorVariable[] = {
-  &appData.whitePieceColor, 
-  &appData.blackPieceColor, 
+  &appData.whitePieceColor,
+  &appData.blackPieceColor,
   &appData.lightSquareColor,
-  &appData.darkSquareColor, 
+  &appData.darkSquareColor,
   &appData.highlightSquareColor,
   &appData.premoveHighlightColor,
   &appData.lowTimeWarningColor,
@@ -1372,7 +1430,7 @@ ParseColor(int n, char *name)
 
 void
 ParseTextAttribs(ColorClass cc, char *s)
-{   
+{
     (&appData.colorShout)[cc] = strdup(s);
 }
 
@@ -1396,7 +1454,7 @@ SetCommPortDefaults()
 void
 SaveFontArg(FILE *f, ArgDescriptor *ad)
 {
-  char *name, buf[MSG_SIZ];
+  char *name;
   int i, n = (int)ad->argLoc;
   switch(n) {
     case 0: // CLOCK_FONT
@@ -1418,7 +1476,7 @@ SaveFontArg(FILE *f, ArgDescriptor *ad)
        break;
   }
   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
-    fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]); 
+    fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
 }
 
 void
@@ -1461,7 +1519,7 @@ GetActualPlacement(Widget wg, WindowPlacement *wp)
   int i;
 
   if(!wg) return;
-  
+
     i = 0;
     XtSetArg(args[i], XtNx, &x); i++;
     XtSetArg(args[i], XtNy, &y); i++;
@@ -1498,7 +1556,9 @@ int
 MySearchPath(char *installDir, char *name, char *fullname)
 { // just append installDir and name. Perhaps ExpandPath should be used here?
   name = ExpandPathName(name);
-  if(name && name[0] == '/') strcpy(fullname, name); else {
+  if(name && name[0] == '/')
+    safeStrCpy(fullname, name, MSG_SIZ );
+  else {
     sprintf(fullname, "%s%c%s", installDir, '/', name);
   }
   return 1;
@@ -1508,7 +1568,7 @@ int
 MyGetFullPathName(char *name, char *fullname)
 { // should use ExpandPath?
   name = ExpandPathName(name);
-  strcpy(fullname, name);
+  safeStrCpy(fullname, name, MSG_SIZ );
   return 1;
 }
 
@@ -1528,6 +1588,7 @@ void
 PopUpStartupDialog()
 {  // start menu not implemented in XBoard
 }
+
 char *
 ConvertToLine(int argc, char **argv)
 {
@@ -1535,14 +1596,17 @@ ConvertToLine(int argc, char **argv)
   int i;
 
   line[0] = NULLCHAR;
-  for(i=1; i<argc; i++) {
-    if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
-       && argv[i][0] != '{' )
-         sprintf(buf, "{%s} ", argv[i]);
-    else sprintf(buf, "%s ", argv[i]);
-    strcat(line, buf);
-  }
-    line[strlen(line)-1] = NULLCHAR;
+  for(i=1; i<argc; i++)
+    {
+      if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
+         && argv[i][0] != '{' )
+       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
+      else
+       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
+      strncat(line, buf, 128*1024 - strlen(line) - 1 );
+    }
+
+  line[strlen(line)-1] = NULLCHAR;
   return line;
 }
 
@@ -1649,6 +1713,10 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
           xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
        }
 #endif
+       if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
+          xpmPieceBitmap[i][(int)WhiteAngel]    = xpmPieceBitmap2[i][(int)WhiteFalcon];
+          xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
+       }
 #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++)
@@ -1665,6 +1733,10 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
        }
 #endif
+       if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
+           ximMaskPm[(int)WhiteAngel]    = ximMaskPm2[(int)WhiteFalcon];
+           ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
+       }
 #endif
       }
     } else {
@@ -1684,6 +1756,10 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
           pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
        }
 #endif
+       if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
+          pieceBitmap[i][(int)WhiteAngel]    = pieceBitmap2[i][(int)WhiteFalcon];
+          pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
+       }
       }
     }
 #if HAVE_LIBXPM
@@ -1692,19 +1768,6 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
 }
 #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;
@@ -1894,6 +1957,7 @@ XBoard square size (hint): %d\n\
            fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
        }
     }
+    if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
 
     /* [HR] height treated separately (hacked) */
     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
@@ -2010,7 +2074,7 @@ 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;
@@ -2020,7 +2084,7 @@ XBoard square size (hint): %d\n\
       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
       vFrom.size = strlen(appData.lowTimeWarningColor);
       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
-      if (vTo.addr == NULL) 
+      if (vTo.addr == NULL)
                appData.monoMode = True;
       else
                lowTimeWarningColor = *(Pixel *) vTo.addr;
@@ -2470,6 +2534,8 @@ XBoard square size (hint): %d\n\
       CreatePieces();
     } else {
       CreateXPMPieces();
+      CreateXPMBoard(appData.liteBackTextureFile, 1);
+      CreateXPMBoard(appData.darkBackTextureFile, 0);
     }
 #else
     CreateXIMPieces();
@@ -2502,11 +2568,11 @@ XBoard square size (hint): %d\n\
       HistoryPopUp();
     }
 
-    if( wpEvalGraph.visible ) 
+    if( wpEvalGraph.visible )
       {
        EvalGraphPopUp();
       };
-    
+
     if( wpEngineOutput.visible ) {
       EngineOutputPopUp();
     }
@@ -2532,6 +2598,7 @@ XBoard square size (hint): %d\n\
     }
     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
     InitPosition(TRUE);
+    XtSetKeyboardFocus(shellWidget, formWidget);
 
     XtAppMainLoop(appContext);
     if (appData.debugMode) fclose(debugFP); // [DM] debug
@@ -2593,22 +2660,33 @@ CmailSigHandlerCallBack(isr, closure, message, count, error)
 void
 ICSInitScript()
 {
-    FILE *f;
-    char buf[MSG_SIZ];
-    char *p;
+  /* try to open the icsLogon script, either in the location given
+   * or in the users HOME directory
+   */
+
+  FILE *f;
+  char buf[MSG_SIZ];
+  char *homedir;
 
-    f = fopen(appData.icsLogon, "r");
-    if (f == NULL) {
-       p = getenv("HOME");
-       if (p != NULL) {
-           strcpy(buf, p);
-           strcat(buf, "/");
-           strcat(buf, appData.icsLogon);
-           f = fopen(buf, "r");
+  f = fopen(appData.icsLogon, "r");
+  if (f == NULL)
+    {
+      homedir = getenv("HOME");
+      if (homedir != NULL)
+       {
+         safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
+         strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
+         strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
+         f = fopen(buf, "r");
        }
     }
-    if (f != NULL)
-      ProcessICSInitScript(f);
+
+  if (f != NULL)
+    ProcessICSInitScript(f);
+  else
+    printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
+
+  return;
 }
 
 void
@@ -2819,7 +2897,7 @@ void SetICSMode()
 {
   SetMenuEnables(icsEnables);
 
-#ifdef ZIPPY
+#if ZIPPY
   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
 #endif
@@ -2942,7 +3020,8 @@ NextInHistory()
  * The return value should be freed with XtFree when no
  * longer needed.
  */
-char *FindFont(pattern, targetPxlSize)
+char *
+FindFont(pattern, targetPxlSize)
      char *pattern;
      int targetPxlSize;
 {
@@ -2957,7 +3036,7 @@ char *FindFont(pattern, targetPxlSize)
     XFontStruct **fnt_list;
 
     base_fnt_lst = calloc(1, strlen(pattern) + 3);
-    sprintf(strInt, "%d", targetPxlSize);
+    snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
     p = strstr(pattern, "--");
     strncpy(base_fnt_lst, pattern, p - pattern + 2);
     strcat(base_fnt_lst, strInt);
@@ -3016,8 +3095,8 @@ char *FindFont(pattern, targetPxlSize)
        while (isdigit(*scalableTail)) scalableTail++;
        sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
     } else {
-        p = (char *) XtMalloc(strlen(best) + 1);
-        strcpy(p, best);
+        p = (char *) XtMalloc(strlen(best) + 2);
+        safeStrCpy(p, best, strlen(best)+1 );
     }
     if (appData.debugMode) {
         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
@@ -3307,6 +3386,16 @@ void CreateXIMPieces()
 }
 
 #if HAVE_LIBXPM
+void CreateXPMBoard(char *s, int kind)
+{
+    XpmAttributes attr;
+    attr.valuemask = 0;
+    if(s == NULL || *s == 0 || *s == '*') return;
+    if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
+       useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
+    }
+}
+
 void CreateXPMPieces()
 {
     int piece, kind, r;
@@ -3403,7 +3492,7 @@ void CreateXPMPieces()
                        exit(1);
                    }
                }
-               if(piece <= (int) WhiteKing) 
+               if(piece <= (int) WhiteKing)
                    xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
            }
        }
@@ -3457,12 +3546,12 @@ void CreatePieces()
 
     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
        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(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
-           if(piece <= (int)WhiteKing)
-               pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
+         snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
+                  pieceBitmapNames[piece],
+                  ss, kind == SOLID ? 's' : 'o');
+         ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
+         if(piece <= (int)WhiteKing)
+           pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
        }
     }
 
@@ -3485,13 +3574,13 @@ void CreatePieces()
 
     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
        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(&pieceBitmap2[kind][piece], buf,
-                      bib->bits[kind][piece], ss, ss);
-           if(piece <= (int)WhiteKing)
-               pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
+         snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
+                  pieceBitmapNames[piece],
+                  ss, kind == SOLID ? 's' : 'o');
+         ReadBitmap(&pieceBitmap2[kind][piece], buf,
+                    bib->bits[kind][piece], ss, ss);
+         if(piece <= (int)WhiteKing)
+           pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
        }
     }
 
@@ -3512,12 +3601,12 @@ void ReadBitmap(pm, name, bits, wreq, hreq)
     char msg[MSG_SIZ], fullname[MSG_SIZ];
 
     if (*appData.bitmapDirectory != NULLCHAR) {
-        strcpy(fullname, appData.bitmapDirectory);
-       strcat(fullname, "/");
-       strcat(fullname, name);
-       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
-                                 &w, &h, pm, &x_hot, &y_hot);
-    fprintf(stderr, "load %s\n", name);
+      safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
+      strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
+      strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
+      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:
@@ -3634,8 +3723,8 @@ Widget CreateMenuBar(mb)
                             formWidget, args, j);
 
     while (mb->name != NULL) {
-       strcpy(menuName, "menu");
-       strcat(menuName, mb->name);
+        safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
+       strncat(menuName, mb->name, MSG_SIZ - strlen(menuName) - 1);
        j = 0;
        XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
        if (tinyLayout) {
@@ -3795,7 +3884,7 @@ void PieceMenuPopup(w, event, params, num_params)
 {
     String whichMenu; int menuNr;
     if (event->type == ButtonRelease)
-        menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY); 
+        menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
     else if (event->type == ButtonPress)
         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
     switch(menuNr) {
@@ -3973,11 +4062,34 @@ ClearPremoveHighlights()
   SetPremoveHighlights(-1, -1, -1, -1);
 }
 
-static void BlankSquare(x, y, color, piece, dest)
-     int x, y, color;
+static int CutOutSquare(x, y, x0, y0, kind)
+     int x, y, *x0, *y0, kind;
+{
+    int W = BOARD_WIDTH, H = BOARD_HEIGHT;
+    int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
+    *x0 = 0; *y0 = 0;
+    if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
+    if(textureW[kind] < W*squareSize)
+       *x0 = (textureW[kind] - squareSize) * nx/(W-1);
+    else
+       *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
+    if(textureH[kind] < H*squareSize)
+       *y0 = (textureH[kind] - squareSize) * ny/(H-1);
+    else
+       *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
+    return 1;
+}
+
+static void BlankSquare(x, y, color, piece, dest, fac)
+     int x, y, color, fac;
      ChessSquare piece;
      Drawable dest;
-{
+{   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
+    int x0, y0;
+    if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
+       XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
+                 squareSize, squareSize, x*fac, y*fac);
+    } else
     if (useImages && useImageSqs) {
        Pixmap pm;
        switch (color) {
@@ -3993,7 +4105,7 @@ static void BlankSquare(x, y, color, piece, dest)
            break;
        }
        XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
-                 squareSize, squareSize, x, y);
+                 squareSize, squareSize, x*fac, y*fac);
     } else {
        GC gc;
        switch (color) {
@@ -4008,7 +4120,7 @@ static void BlankSquare(x, y, color, piece, dest)
            gc = jailSquareGC;
            break;
        }
-       XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
+       XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
     }
 }
 
@@ -4101,7 +4213,7 @@ static void colorDrawPieceImage(piece, square_color, x, y, dest)
      int square_color, x, y;
      Drawable dest;
 {
-    int kind;
+    int kind, p = piece;
 
     switch (square_color) {
       case 1: /* light */
@@ -4123,6 +4235,15 @@ static void colorDrawPieceImage(piece, square_color, x, y, dest)
        }
        break;
     }
+    if(appData.upsideDown && flipView) kind ^= 2; // swap white and black pieces
+    if(useTexture & square_color+1) {
+        BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
+       XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
+       XSetClipOrigin(xDisplay, wlPieceGC, x, y);
+       XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
+       XSetClipMask(xDisplay, wlPieceGC, None);
+       XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
+    } else
     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
              dest, wlPieceGC, 0, 0,
              squareSize, squareSize, x, y);
@@ -4204,7 +4325,7 @@ void DrawSquare(row, column, piece, do_flash)
                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
                  || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
-                       BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
+                       BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
 
                        // [HGM] print piece counts next to holdings
                        string[1] = NULLCHAR;
@@ -4236,7 +4357,7 @@ void DrawSquare(row, column, piece, do_flash)
                        }
     } else {
            if (piece == EmptySquare || appData.blindfold) {
-                       BlankSquare(x, y, square_color, piece, xBoardWindow);
+                       BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
            } else {
                        drawfunc = ChooseDrawFunc();
                        if (do_flash && appData.flashCount > 0) {
@@ -4246,7 +4367,7 @@ void DrawSquare(row, column, piece, do_flash)
                                        XSync(xDisplay, False);
                                        do_flash_delay(flash_delay);
 
-                                       BlankSquare(x, y, square_color, piece, xBoardWindow);
+                                       BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
                                        XSync(xDisplay, False);
                                        do_flash_delay(flash_delay);
                            }
@@ -4284,7 +4405,7 @@ void DrawSquare(row, column, piece, do_flash)
        }
     }
     if(!partnerUp && marker[row][column]) {
-       XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC, 
+       XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
                x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
     }
 }
@@ -4391,7 +4512,7 @@ static int check_castle_draw(newb, oldb, rrow, rcol)
     return 0;
 }
 
-// [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph 
+// [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
 void DrawSeekAxis( int x, int y, int xTo, int yTo )
 {
       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
@@ -4417,7 +4538,7 @@ void DrawSeekDot(int x, int y, int colorNr)
        XFillRectangle(xDisplay, xBoardWindow, color,
                x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
     else
-       XFillArc(xDisplay, xBoardWindow, color, 
+       XFillArc(xDisplay, xBoardWindow, color,
                x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
 }
 
@@ -4442,7 +4563,7 @@ void XDrawPosition(w, repaint, board)
     if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
 
     if (board == NULL) {
-       if (!lastBoardValid) return;
+       if (!lastBoardValid[nr]) return;
        board = lastBoard[nr];
     }
     if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
@@ -4558,6 +4679,7 @@ void HandleUserMove(w, event, prms, nprms)
      Cardinal *nprms;
 {
     if (w != boardWidget || errorExitStatus != -1) return;
+    if(nprms) shiftKey = !strcmp(prms[0], "1");
 
     if (promotionUp) {
        if (event->type == ButtonPress) {
@@ -4639,6 +4761,7 @@ Widget CommentCreate(name, text, mutable, callback, lines)
     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
     edit =
       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
+    XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
 
     if (mutable) {
        j = 0;
@@ -4847,6 +4970,20 @@ Widget MiscCreate(name, text, mutable, callback, lines)
 
 static int savedIndex;  /* gross that this is global */
 
+void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{
+       String val;
+       XawTextPosition index, dummy;
+       Arg arg;
+
+       XawTextGetSelectionPos(w, &index, &dummy);
+       XtSetArg(arg, XtNstring, &val);
+       XtGetValues(w, &arg, 1);
+       ReplaceComment(savedIndex, val);
+       if(savedIndex != currentMove) ToNrEvent(savedIndex);
+       LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
+}
+
 void EditCommentPopUp(index, title, text)
      int index;
      char *title, *text;
@@ -5008,6 +5145,7 @@ void CommentPopUp(title, text)
     int j;
     Widget edit;
 
+    savedIndex = currentMove; // [HGM] vari
     if (commentShell == NULL) {
        commentShell =
          CommentCreate(title, text, False, CommentCallback, 4);
@@ -5074,13 +5212,6 @@ void FileNamePopUp(label, def, proc, openMode)
      FileProc proc;
      char *openMode;
 {
-    Arg args[16];
-    Widget popup, layout, dialog, edit;
-    Window root, child;
-    int x, y, i;
-    int win_x, win_y;
-    unsigned int mask;
-
     fileProc = proc;           /* I can't see a way not */
     fileOpenMode = openMode;   /*   to use globals here */
     {   // [HGM] use file-selector dialog stolen from Ghostview
@@ -5088,8 +5219,8 @@ void FileNamePopUp(label, def, proc, openMode)
        int index; // this is not supported yet
        FILE *f;
        if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
-           NULL, openMode, NULL, &name))
-               (void) (*fileProc)(f, index=0, name);
+                          def, openMode, NULL, &name))
+         (void) (*fileProc)(f, index=0, name);
     }
 }
 
@@ -5135,7 +5266,7 @@ void FileNameAction(w, event, prms, nprms)
     name = XawDialogGetValueString(w = XtParent(w));
 
     if ((name != NULL) && (*name != NULLCHAR)) {
-       strcpy(buf, name);
+        safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
        XtPopdown(w = XtParent(XtParent(w)));
        XtDestroyWidget(w);
        filenameUp = False;
@@ -5211,8 +5342,8 @@ void PromotionPopUp()
       XawDialogAddButton(dialog, _("King"), PromotionCallback,
                         (XtPointer) dialog);
     }
-    if(gameInfo.variant == VariantCapablanca || 
-       gameInfo.variant == VariantGothic || 
+    if(gameInfo.variant == VariantCapablanca ||
+       gameInfo.variant == VariantGothic ||
        gameInfo.variant == VariantCapaRandom) {
       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
                         (XtPointer) dialog);
@@ -5665,7 +5796,7 @@ SendPositionSelection(Widget w, Atom *selection, Atom *target,
      * automatically call XtFree on the value returned.  So have to
      * make a copy of it allocated with XtMalloc */
     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
-    strcpy(selection_tmp, selected_fen_position);
+    safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
 
     *value_return=selection_tmp;
     *length_return=strlen(selection_tmp);
@@ -5740,7 +5871,7 @@ void PastePositionProc(w, event, prms, nprms)
   String *prms;
   Cardinal *nprms;
 {
-    XtGetSelectionValue(menuBarWidget, 
+    XtGetSelectionValue(menuBarWidget,
       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
       /* (XtSelectionCallbackProc) */ PastePositionCB,
       NULL, /* client_data passed to PastePositionCB */
@@ -5930,7 +6061,7 @@ void AnalyzeModeProc(w, event, prms, nprms)
     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
     if (appData.icsActive) {
         if (gameMode != IcsObserving) {
-            sprintf(buf,_("You are not observing a game"));
+         snprintf(buf, MSG_SIZ, _("You are not observing a game"));
             DisplayError(buf, 0);
             /* secure check */
             if (appData.icsEngineAnalyze) {
@@ -6989,23 +7120,23 @@ void DisplayMessage(message, extMessage)
      char *message, *extMessage;
 {
   /* display a message in the message widget */
-  
+
   char buf[MSG_SIZ];
   Arg arg;
-  
-  if (extMessage) 
+
+  if (extMessage)
     {
-      if (*message) 
+      if (*message)
        {
          snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
          message = buf;
-       } 
-      else 
+       }
+      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 */
@@ -7014,7 +7145,7 @@ void DisplayMessage(message, extMessage)
       XtSetArg(arg, XtNlabel, message);
       XtSetValues(messageWidget, &arg, 1);
     };
-  
+
   return;
 }
 
@@ -7035,8 +7166,8 @@ void DisplayTitle(text)
     }
 
     if (*text != NULLCHAR) {
-       strcpy(icon, text);
-       strcpy(title, text);
+      safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
     } else if (appData.icsActive) {
         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
        snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
@@ -7046,19 +7177,19 @@ void DisplayTitle(text)
 #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) {
-       strcpy(icon, programName);
-       strcpy(title, GOTHIC);
+      safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
 #endif
 #ifdef FALCON
     } else if (gameInfo.variant == VariantFalcon) {
-       strcpy(icon, programName);
-       strcpy(title, FALCON);
+      safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
 #endif
     } else if (appData.noChessProgram) {
-       strcpy(icon, programName);
-       strcpy(title, programName);
+      safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
     } else {
-       strcpy(icon, first.tidy);
+      safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
        snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
     }
     i = 0;
@@ -7068,7 +7199,8 @@ void DisplayTitle(text)
 }
 
 
-void DisplayError(message, error)
+void
+DisplayError(message, error)
      String message;
      int error;
 {
@@ -7216,10 +7348,10 @@ void AskQuestionReplyAction(w, event, prms, nprms)
     String reply;
 
     reply = XawDialogGetValueString(w = XtParent(w));
-    strcpy(buf, pendingReplyPrefix);
-    if (*buf) strcat(buf, " ");
-    strcat(buf, reply);
-    strcat(buf, "\n");
+    safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
+    if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
+    strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
+    strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
     AskQuestionPopDown();
 
@@ -7254,7 +7386,7 @@ void AskQuestion(title, question, replyPrefix, pr)
     int win_x, win_y;
     unsigned int mask;
 
-    strcpy(pendingReplyPrefix, replyPrefix);
+    safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
     pendingReplyPR = pr;
 
     i = 0;
@@ -7371,18 +7503,18 @@ Colorize(cc, continuation)
 
     if (textColors[(int)cc].bg > 0) {
        if (textColors[(int)cc].fg > 0) {
-           sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
-                   textColors[(int)cc].fg, textColors[(int)cc].bg);
+         snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
+                  textColors[(int)cc].fg, textColors[(int)cc].bg);
        } else {
-           sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
-                   textColors[(int)cc].bg);
+         snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
+                  textColors[(int)cc].bg);
        }
     } else {
        if (textColors[(int)cc].fg > 0) {
-           sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
+         snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
                    textColors[(int)cc].fg);
        } else {
-           sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
+         snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
        }
     }
     count = strlen(buf);
@@ -7432,11 +7564,12 @@ char *UserName()
     return getpwuid(getuid())->pw_name;
 }
 
-static char *ExpandPathName(path)
+static char *
+ExpandPathName(path)
      char *path;
 {
-    static char static_buf[2000];
-    char *d, *s, buf[2000];
+    static char static_buf[4*MSG_SIZ];
+    char *d, *s, buf[4*MSG_SIZ];
     struct passwd *pwd;
 
     s = path;
@@ -7452,25 +7585,25 @@ static char *ExpandPathName(path)
 
     if (*s == '~') {
        if (*(s+1) == '/') {
-           strcpy(d, getpwuid(getuid())->pw_dir);
-           strcat(d, s+1);
+         safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
+         strcat(d, s+1);
        }
        else {
-           strcpy(buf, s+1);
-           *strchr(buf, '/') = 0;
-           pwd = getpwnam(buf);
-           if (!pwd)
-             {
-                 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
-                         buf, path);
-                 return NULL;
-             }
-           strcpy(d, pwd->pw_dir);
-           strcat(d, strchr(s+1, '/'));
+         safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
+         { char *p; if(p = strchr(buf, '/')) *p = 0; }
+         pwd = getpwnam(buf);
+         if (!pwd)
+           {
+             fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
+                     buf, path);
+             return NULL;
+           }
+         safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
+         strcat(d, strchr(s+1, '/'));
        }
     }
     else
-      strcpy(d, s);
+      safeStrCpy(d, s, 4*MSG_SIZ );
 
     return static_buf;
 }
@@ -7645,16 +7778,16 @@ DisplayTimerLabel(w, color, timer, highlight)
     Pixel foregroundOrWarningColor = timerForegroundPixel;
 
     if (timer > 0 &&
-        appData.lowTimeWarning && 
+        appData.lowTimeWarning &&
         (timer / 1000) < appData.icsAlarmTime)
       foregroundOrWarningColor = lowTimeWarningColor;
 
     if (appData.clockMode) {
-       sprintf(buf, "%s: %s", color, TimeString(timer));
-       XtSetArg(args[0], XtNlabel, buf);
+      snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
+      XtSetArg(args[0], XtNlabel, buf);
     } else {
-       sprintf(buf, "%s  ", color);
-       XtSetArg(args[0], XtNlabel, buf);
+      snprintf(buf, MSG_SIZ, "%s  ", color);
+      XtSetArg(args[0], XtNlabel, buf);
     }
 
     if (highlight) {
@@ -7735,7 +7868,7 @@ int StartChildProcess(cmdLine, dir, pr)
        most simple-minded way possible.
        */
     i = 0;
-    strcpy(buf, cmdLine);
+    safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
     p = buf;
     for (;;) {
        while(*p == ' ') p++;
@@ -8549,6 +8682,7 @@ OverlayPiece(piece, clip, outline,  dest)
       kind = 0;
     else
       kind = 2;
+    if(appData.upsideDown && flipView) kind ^= 2;
     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
              dest, clip,
              0, 0, squareSize, squareSize,
@@ -8568,7 +8702,7 @@ BeginAnimation(anim, piece, startColor, start)
   Pixmap mask;
 
   /* The old buffer is initialised with the start square (empty) */
-  BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
+  BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
   anim->prevFrame = *start;
 
   /* The piece will be drawn using its own bitmap as a matte   */
@@ -8673,6 +8807,29 @@ FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
   EndAnimation(anim, finish);
 }
 
+void
+AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
+{
+    int i, x, y;
+    ChessSquare piece = board[fromY][toY];
+    board[fromY][toY] = EmptySquare;
+    DrawPosition(FALSE, board);
+    if (flipView) {
+       x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
+       y = lineGap + toY * (squareSize + lineGap);
+    } else {
+       x = lineGap + toX * (squareSize + lineGap);
+       y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
+    }
+    for(i=1; i<4*kFactor; i++) {
+       int r = squareSize * 9 * i/(20*kFactor - 5);
+       XFillArc(xDisplay, xBoardWindow, highlineGC,
+               x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
+       FrameDelay(appData.animSpeed);
+    }
+    board[fromY][toY] = piece;
+}
+
 /* Main control logic for deciding what to animate and how */
 
 void
@@ -8693,8 +8850,8 @@ 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) 
+  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;
@@ -8704,7 +8861,7 @@ AnimateMove(board, fromX, fromY, toX, toY)
 #if DONT_HOP
   hop = FALSE;
 #else
-  hop = (piece == WhiteKnight || piece == BlackKnight);
+  hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
 #endif
 
   if (appData.debugMode) {
@@ -8716,12 +8873,12 @@ AnimateMove(board, fromX, fromY, toX, toY)
   ScreenSquare(toX, toY, &finish, &endColor);
 
   if (hop) {
-    /* Knight: make diagonal movement then straight */
+    /* Knight: make straight movement then diagonal */
     if (abs(toY - fromY) < abs(toX - fromX)) {
        mid.x = start.x + (finish.x - start.x) / 2;
-       mid.y = finish.y;
+       mid.y = start.y;
      } else {
-       mid.x = finish.x;
+       mid.x = start.x;
        mid.y = start.y + (finish.y - start.y) / 2;
      }
   } else {
@@ -8735,6 +8892,11 @@ AnimateMove(board, fromX, fromY, toX, toY)
   else
     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
+  if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
+    int i,j;
+    for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
+      if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
+  }
 
   /* Be sure end square is redrawn */
   damage[0][toY][toX] = True;
@@ -8777,6 +8939,17 @@ DragPieceBegin(x, y)
            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
                     corner.x, corner.y, squareSize, squareSize,
                     0, 0); // [HGM] zh: unstack in stead of grab
+           if(gatingPiece != EmptySquare) {
+               /* Kludge alert: When gating we want the introduced
+                  piece to appear on the from square. To generate an
+                  image of it, we draw it on the board, copy the image,
+                  and draw the original piece again. */
+               ChessSquare piece = boards[currentMove][boardY][boardX];
+               DrawSquare(boardY, boardX, gatingPiece, 0);
+               XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
+                    corner.x, corner.y, squareSize, squareSize, 0, 0);
+               DrawSquare(boardY, boardX, piece, 0);
+           }
        damage[0][boardY][boardX] = True;
     } else {
        player.dragActive = False;
@@ -8852,7 +9025,7 @@ DrawDragPiece ()
      it's being dragged around the board. So we erase the square
      that the piece is on and draw it at the last known drag point. */
   BlankSquare(player.startSquare.x, player.startSquare.y,
-               player.startColor, EmptySquare, xBoardWindow);
+               player.startColor, EmptySquare, xBoardWindow, 1);
   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
   damage[0][player.startBoardY][player.startBoardX] = TRUE;
 }
@@ -8877,14 +9050,15 @@ int get_term_width()
     return default_width;
 }
 
-void update_ics_width()
+void
+update_ics_width()
 {
-    static int old_width = 0;
-    int new_width = get_term_width();
+  static int old_width = 0;
+  int new_width = get_term_width();
 
-    if (old_width != new_width)
-       ics_printf("set width %d\n", new_width);
-    old_width = new_width;
+  if (old_width != new_width)
+    ics_printf("set width %d\n", new_width);
+  old_width = new_width;
 }
 
 void NotifyFrontendLogin()