Split back-endish part off drawing code and move to board.c
[xboard.git] / xboard.c
index ec7568c..934f903 100644 (file)
--- a/xboard.c
+++ b/xboard.c
@@ -204,6 +204,7 @@ extern char *getenv();
 #include "xhistory.h"
 #include "xedittags.h"
 #include "menus.h"
+#include "board.h"
 #include "gettext.h"
 
 
@@ -251,7 +252,6 @@ void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
                   u_int wreq, u_int hreq));
 void CreateGrid P((void));
 int EventToSquare P((int x, int limit));
-void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
 void DelayedDrag P((void));
 void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
@@ -311,8 +311,6 @@ void ICSInitScript P((void));
 void ErrorPopUp P((char *title, char *text, int modal));
 void ErrorPopDown P((void));
 static char *ExpandPathName P((char *path));
-static void DragPieceMove P((int x, int y));
-static void DrawDragPiece P((void));
 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
 void GameListOptionsPopDown P(());
 void GenericPopDown P(());
@@ -343,7 +341,6 @@ Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
   ICSInputShell, fileNameShell, askQuestionShell;
 Widget historyShell, evalGraphShell, gameListShell;
-int hOffset; // [HGM] dual
 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
@@ -370,12 +367,12 @@ BoardSize boardSize;
 Boolean chessProgram;
 
 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
-int squareSize, smallLayout = 0, tinyLayout = 0,
+int smallLayout = 0, tinyLayout = 0,
   marginW, marginH, // [HGM] for run-time resizing
   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
   ICSInputBoxUp = False, askQuestionUp = False,
   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
-  errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
+  errorUp = False, errorExitStatus = -1, defaultLineGap;
 Dimension textHeight;
 Pixel timerForegroundPixel, timerBackgroundPixel;
 Pixel buttonForegroundPixel, buttonBackgroundPixel;
@@ -418,26 +415,6 @@ XImage *xim_Cross;
 
 #define White(piece) ((int)(piece) < (int)BlackPawn)
 
-/* Variables for doing smooth animation. This whole thing
-   would be much easier if the board was double-buffered,
-   but that would require a fairly major rewrite.      */
-
-typedef struct {
-       Pixmap  saveBuf;
-       Pixmap  newBuf;
-       GC      blitGC, pieceGC, outlineGC;
-       XPoint  startSquare, prevFrame, mouseDelta;
-       int     startColor;
-       int     dragPiece;
-       Boolean dragActive;
-        int     startBoardX, startBoardY;
-    } AnimState;
-
-/* There can be two pieces being animated at once: a player
-   can begin dragging a piece before the remote opponent has moved. */
-
-static AnimState game, player;
-
 /* Bitmaps for use as masks when drawing XPM pieces.
    Need one for each black and white piece.            */
 static Pixmap xpmMask[BlackKing + 1];
@@ -3354,128 +3331,23 @@ BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
 }
 
 
-/*
- * If the user selects on a border boundary, return -1; if off the board,
- *   return -2.  Otherwise map the event coordinate to the square.
- */
-int
-EventToSquare (int x, int limit)
-{
-    if (x <= 0)
-      return -2;
-    if (x < lineGap)
-      return -1;
-    x -= lineGap;
-    if ((x % (squareSize + lineGap)) >= squareSize)
-      return -1;
-    x /= (squareSize + lineGap);
-    if (x >= limit)
-      return -2;
-    return x;
-}
-
 static void
 do_flash_delay (unsigned long msec)
 {
     TimeDelay(msec);
 }
 
-static void
-drawHighlight (int file, int rank, GC gc)
+void
+DrawBorder (int x, int y, int type)
 {
-    int x, y;
+    GC gc = lineGC;
 
-    if (lineGap == 0) return;
-
-    if (flipView) {
-       x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
-         (squareSize + lineGap);
-       y = lineGap/2 + rank * (squareSize + lineGap);
-    } else {
-       x = lineGap/2 + file * (squareSize + lineGap);
-       y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
-         (squareSize + lineGap);
-    }
+    if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
 
     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
                   squareSize+lineGap, squareSize+lineGap);
 }
 
-int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
-int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
-
-void
-SetHighlights (int fromX, int fromY, int toX, int toY)
-{
-    if (hi1X != fromX || hi1Y != fromY) {
-       if (hi1X >= 0 && hi1Y >= 0) {
-           drawHighlight(hi1X, hi1Y, lineGC);
-       }
-    } // [HGM] first erase both, then draw new!
-
-    if (hi2X != toX || hi2Y != toY) {
-       if (hi2X >= 0 && hi2Y >= 0) {
-           drawHighlight(hi2X, hi2Y, lineGC);
-       }
-    }
-    if (hi1X != fromX || hi1Y != fromY) {
-       if (fromX >= 0 && fromY >= 0) {
-           drawHighlight(fromX, fromY, highlineGC);
-       }
-    }
-    if (hi2X != toX || hi2Y != toY) {
-       if (toX >= 0 && toY >= 0) {
-           drawHighlight(toX, toY, highlineGC);
-       }
-    }
-
-    if(toX<0) // clearing the highlights must have damaged arrow
-       DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
-
-    hi1X = fromX;
-    hi1Y = fromY;
-    hi2X = toX;
-    hi2Y = toY;
-}
-
-void
-ClearHighlights ()
-{
-    SetHighlights(-1, -1, -1, -1);
-}
-
-
-void
-SetPremoveHighlights (int fromX, int fromY, int toX, int toY)
-{
-    if (pm1X != fromX || pm1Y != fromY) {
-       if (pm1X >= 0 && pm1Y >= 0) {
-           drawHighlight(pm1X, pm1Y, lineGC);
-       }
-       if (fromX >= 0 && fromY >= 0) {
-           drawHighlight(fromX, fromY, prelineGC);
-       }
-    }
-    if (pm2X != toX || pm2Y != toY) {
-       if (pm2X >= 0 && pm2Y >= 0) {
-           drawHighlight(pm2X, pm2Y, lineGC);
-       }
-       if (toX >= 0 && toY >= 0) {
-           drawHighlight(toX, toY, prelineGC);
-       }
-    }
-    pm1X = fromX;
-    pm1Y = fromY;
-    pm2X = toX;
-    pm2Y = toY;
-}
-
-void
-ClearPremoveHighlights ()
-{
-  SetPremoveHighlights(-1, -1, -1, -1);
-}
-
 static int
 CutOutSquare (int x, int y, int *x0, int *y0, int  kind)
 {
@@ -3672,154 +3544,71 @@ ChooseDrawFunc ()
     }
 }
 
-/* [HR] determine square color depending on chess variant. */
-static int
-SquareColor (int row, int column)
-{
-    int square_color;
-
-    if (gameInfo.variant == VariantXiangqi) {
-        if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
-            square_color = 1;
-        } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
-            square_color = 0;
-        } else if (row <= 4) {
-            square_color = 0;
-        } else {
-            square_color = 1;
-        }
-    } else {
-        square_color = ((column + row) % 2) == 1;
-    }
-
-    /* [hgm] holdings: next line makes all holdings squares light */
-    if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
-
-    return square_color;
+void
+DrawDot (int marker, int x, int y, int r)
+{
+       if(appData.monoMode) {
+           XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
+                   x, y, r, r, 0, 64*360);
+           XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
+                   x, y, r, r, 0, 64*360);
+       } else
+       XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
+                   x, y, r, r, 0, 64*360);
 }
 
 void
-DrawSquare (int row, int column, ChessSquare piece, int do_flash)
-{
-    int square_color, x, y, direction, font_ascent, font_descent;
-    int i;
-    char string[2];
+DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
+{   // basic front-end board-draw function: takes care of everything that can be in square:
+    // piece, background, coordinate/count, marker dot
+    int direction, font_ascent, font_descent;
     XCharStruct overall;
     DrawFunc drawfunc;
-    int flash_delay;
 
-    /* Calculate delay in milliseconds (2-delays per complete flash) */
-    flash_delay = 500 / appData.flashRate;
-
-    if (flipView) {
-       x = lineGap + ((BOARD_WIDTH-1)-column) *
-         (squareSize + lineGap);
-       y = lineGap + row * (squareSize + lineGap);
+    if (piece == EmptySquare) {
+       BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
     } else {
-       x = lineGap + column * (squareSize + lineGap);
-       y = lineGap + ((BOARD_HEIGHT-1)-row) *
-         (squareSize + lineGap);
-    }
-
-    if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
-
-    square_color = SquareColor(row, column);
-
-    if ( // [HGM] holdings: blank out area between board and holdings
-                 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, 1);
-
-                       // [HGM] print piece counts next to holdings
-                       string[1] = NULLCHAR;
-                       if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
-                           string[0] = '0' + piece;
-                           XTextExtents(countFontStruct, string, 1, &direction,
-                                &font_ascent, &font_descent, &overall);
-                           if (appData.monoMode) {
-                               XDrawImageString(xDisplay, xBoardWindow, countGC,
-                                                x + squareSize - overall.width - 2,
-                                                y + font_ascent + 1, string, 1);
-                           } else {
-                               XDrawString(xDisplay, xBoardWindow, countGC,
-                                           x + squareSize - overall.width - 2,
-                                           y + font_ascent + 1, string, 1);
-                           }
-                       }
-                       if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
-                           string[0] = '0' + piece;
-                           XTextExtents(countFontStruct, string, 1, &direction,
-                                        &font_ascent, &font_descent, &overall);
-                           if (appData.monoMode) {
-                               XDrawImageString(xDisplay, xBoardWindow, countGC,
-                                                x + 2, y + font_ascent + 1, string, 1);
-                           } else {
-                               XDrawString(xDisplay, xBoardWindow, countGC,
-                                           x + 2, y + font_ascent + 1, string, 1);
-                           }
-                       }
-    } else {
-           if (piece == EmptySquare || appData.blindfold) {
-                       BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
-           } else {
-                       drawfunc = ChooseDrawFunc();
-
-                       if (do_flash && appData.flashCount > 0) {
-                           for (i=0; i<appData.flashCount; ++i) {
-                                       drawfunc(piece, square_color, x, y, xBoardWindow);
-                                       XSync(xDisplay, False);
-                                       do_flash_delay(flash_delay);
-
-                                       BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
-                                       XSync(xDisplay, False);
-                                       do_flash_delay(flash_delay);
-                           }
-                       }
-                       drawfunc(piece, square_color, x, y, xBoardWindow);
-       }
-       }
-
-    string[1] = NULLCHAR;
-    if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
-               && column >= BOARD_LEFT && column < BOARD_RGHT) {
-       string[0] = 'a' + column - BOARD_LEFT;
-       XTextExtents(coordFontStruct, string, 1, &direction,
-                    &font_ascent, &font_descent, &overall);
-       if (appData.monoMode) {
-           XDrawImageString(xDisplay, xBoardWindow, coordGC,
-                            x + squareSize - overall.width - 2,
-                            y + squareSize - font_descent - 1, string, 1);
-       } else {
-           XDrawString(xDisplay, xBoardWindow, coordGC,
-                       x + squareSize - overall.width - 2,
-                       y + squareSize - font_descent - 1, string, 1);
+       drawfunc = ChooseDrawFunc();
+       drawfunc(piece, square_color, x, y, xBoardWindow);
+    }
+
+    if(align) { // square carries inscription (coord or piece count)
+       int xx = x, yy = y;
+       GC hGC = align < 3 ? coordGC : countGC;
+       // first calculate where it goes
+       XTextExtents(countFontStruct, string, 1, &direction,
+                        &font_ascent, &font_descent, &overall);
+       if (align == 1) {
+           xx += squareSize - overall.width - 2;
+           yy += squareSize - font_descent - 1;
+       } else if (align == 2) {
+           xx += 2, yy += font_ascent + 1;
+       } else if (align == 3) {
+           xx += squareSize - overall.width - 2;
+           yy += font_ascent + 1;
+       } else if (align == 4) {
+           xx += 2, yy += font_ascent + 1;
        }
-    }
-    if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
-       string[0] = ONE + row;
-       XTextExtents(coordFontStruct, string, 1, &direction,
-                    &font_ascent, &font_descent, &overall);
+       // then draw it
        if (appData.monoMode) {
-           XDrawImageString(xDisplay, xBoardWindow, coordGC,
-                            x + 2, y + font_ascent + 1, string, 1);
+           XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
        } else {
-           XDrawString(xDisplay, xBoardWindow, coordGC,
-                       x + 2, y + font_ascent + 1, string, 1);
+           XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
        }
     }
-    if(!partnerUp && marker[row][column]) {
-       if(appData.monoMode) {
-           XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? darkSquareGC : lightSquareGC,
-                   x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
-           XDrawArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? lightSquareGC : darkSquareGC,
-                   x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
-       } else
-       XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
-               x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
+
+    if(marker) { // print fat marker dot, if requested
+       DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
     }
 }
 
+void
+FlashDelay (int flash_delay)
+{
+       XSync(xDisplay, False);
+       if(flash_delay) do_flash_delay(flash_delay);
+}
+
 double
 Fraction (int x, int start, int stop)
 {
@@ -3879,7 +3668,7 @@ DragProc ()
        if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
        if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
        wpMain = wpNew;
-       XDrawPosition(boardWidget, True, NULL);
+       DrawPosition(True, NULL);
        delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
 }
 
@@ -3905,10 +3694,10 @@ EventProc (Widget widget, caddr_t unused, XEvent *event)
        break;
       case Expose:
        if (event->xexpose.count > 0) return;  /* no clipping is done */
-       XDrawPosition(widget, True, NULL);
+       DrawPosition(True, NULL);
        if(twoBoards) { // [HGM] dual: draw other board in other orientation
            flipView = !flipView; partnerUp = !partnerUp;
-           XDrawPosition(widget, True, NULL);
+           DrawPosition(True, NULL);
            flipView = !flipView; partnerUp = !partnerUp;
        }
        break;
@@ -3920,77 +3709,6 @@ EventProc (Widget widget, caddr_t unused, XEvent *event)
 }
 /* end why */
 
-void
-DrawPosition (int fullRedraw, Board board)
-{
-    XDrawPosition(boardWidget, fullRedraw, board);
-}
-
-/* Returns 1 if there are "too many" differences between b1 and b2
-   (i.e. more than 1 move was made) */
-static int
-too_many_diffs (Board b1, Board b2)
-{
-    int i, j;
-    int c = 0;
-
-    for (i=0; i<BOARD_HEIGHT; ++i) {
-       for (j=0; j<BOARD_WIDTH; ++j) {
-           if (b1[i][j] != b2[i][j]) {
-               if (++c > 4)    /* Castling causes 4 diffs */
-                 return 1;
-           }
-       }
-    }
-    return 0;
-}
-
-/* Matrix describing castling maneuvers */
-/* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
-static int castling_matrix[4][5] = {
-    { 0, 0, 4, 3, 2 },         /* 0-0-0, white */
-    { 0, 7, 4, 5, 6 },         /* 0-0,   white */
-    { 7, 0, 4, 3, 2 },         /* 0-0-0, black */
-    { 7, 7, 4, 5, 6 }          /* 0-0,   black */
-};
-
-/* Checks whether castling occurred. If it did, *rrow and *rcol
-   are set to the destination (row,col) of the rook that moved.
-
-   Returns 1 if castling occurred, 0 if not.
-
-   Note: Only handles a max of 1 castling move, so be sure
-   to call too_many_diffs() first.
-   */
-static int
-check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
-{
-    int i, *r, j;
-    int match;
-
-    /* For each type of castling... */
-    for (i=0; i<4; ++i) {
-       r = castling_matrix[i];
-
-       /* Check the 4 squares involved in the castling move */
-       match = 0;
-       for (j=1; j<=4; ++j) {
-           if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
-               match = 1;
-               break;
-           }
-       }
-
-       if (!match) {
-           /* All 4 changed, so it must be a castling move */
-           *rrow = r[0];
-           *rcol = r[3];
-           return 1;
-       }
-    }
-    return 0;
-}
-
 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
 void
 DrawSeekAxis (int x, int y, int xTo, int yTo)
@@ -4025,111 +3743,12 @@ DrawSeekDot (int x, int y, int colorNr)
                x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
 }
 
-static int damage[2][BOARD_RANKS][BOARD_FILES];
-
-/*
- * event handler for redrawing the board
- */
 void
-XDrawPosition (Widget w, int repaint, Board board)
+DrawGrid (int second)
 {
-    int i, j, do_flash;
-    static int lastFlipView = 0;
-    static int lastBoardValid[2] = {0, 0};
-    static Board lastBoard[2];
-    Arg args[16];
-    int rrow, rcol;
-    int nr = twoBoards*partnerUp;
-
-    if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
-
-    if (board == NULL) {
-       if (!lastBoardValid[nr]) return;
-       board = lastBoard[nr];
-    }
-    if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
-       MarkMenuItem("Flip View", flipView);
-    }
-
-    /*
-     * It would be simpler to clear the window with XClearWindow()
-     * but this causes a very distracting flicker.
-     */
-
-    if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
-
-       if ( lineGap && IsDrawArrowEnabled())
-           XDrawSegments(xDisplay, xBoardWindow, lineGC,
-                       gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
-
-       /* If too much changes (begin observing new game, etc.), don't
-          do flashing */
-       do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
-
-       /* Special check for castling so we don't flash both the king
-          and the rook (just flash the king). */
-       if (do_flash) {
-           if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
-               /* Draw rook with NO flashing. King will be drawn flashing later */
-               DrawSquare(rrow, rcol, board[rrow][rcol], 0);
-               lastBoard[nr][rrow][rcol] = board[rrow][rcol];
-           }
-       }
-
-       /* First pass -- Draw (newly) empty squares and repair damage.
-          This prevents you from having a piece show up twice while it
-          is flashing on its new square */
-       for (i = 0; i < BOARD_HEIGHT; i++)
-         for (j = 0; j < BOARD_WIDTH; j++)
-           if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
-               || damage[nr][i][j]) {
-               DrawSquare(i, j, board[i][j], 0);
-               damage[nr][i][j] = False;
-           }
-
-       /* Second pass -- Draw piece(s) in new position and flash them */
-       for (i = 0; i < BOARD_HEIGHT; i++)
-         for (j = 0; j < BOARD_WIDTH; j++)
-           if (board[i][j] != lastBoard[nr][i][j]) {
-               DrawSquare(i, j, board[i][j], do_flash);
-           }
-    } else {
-       if (lineGap > 0)
          XDrawSegments(xDisplay, xBoardWindow, lineGC,
-                       twoBoards & partnerUp ? secondSegments : // [HGM] dual
+                       second ? secondSegments : // [HGM] dual
                        gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
-
-       for (i = 0; i < BOARD_HEIGHT; i++)
-         for (j = 0; j < BOARD_WIDTH; j++) {
-             DrawSquare(i, j, board[i][j], 0);
-             damage[nr][i][j] = False;
-         }
-    }
-
-    CopyBoard(lastBoard[nr], board);
-    lastBoardValid[nr] = 1;
-  if(nr == 0) { // [HGM] dual: no highlights on second board yet
-    lastFlipView = flipView;
-
-    /* Draw highlights */
-    if (pm1X >= 0 && pm1Y >= 0) {
-      drawHighlight(pm1X, pm1Y, prelineGC);
-    }
-    if (pm2X >= 0 && pm2Y >= 0) {
-      drawHighlight(pm2X, pm2Y, prelineGC);
-    }
-    if (hi1X >= 0 && hi1Y >= 0) {
-      drawHighlight(hi1X, hi1Y, highlineGC);
-    }
-    if (hi2X >= 0 && hi2Y >= 0) {
-      drawHighlight(hi2X, hi2Y, highlineGC);
-    }
-    DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
-  }
-    /* If piece being dragged around board, must redraw that too */
-    DrawDragPiece();
-
-    XSync(xDisplay, False);
 }
 
 
@@ -4139,7 +3758,7 @@ XDrawPosition (Widget w, int repaint, Board board)
 void
 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
 {
-    XDrawPosition(w, True, NULL);
+    DrawPosition(True, NULL);
 }
 
 
@@ -6067,18 +5686,7 @@ OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, lon
     return outCount;
 }
 
-/****  Animation code by Hugh Fisher, DCS, ANU.
-
-       Known problem: if a window overlapping the board is
-       moved away while a piece is being animated underneath,
-       the newly exposed area won't be updated properly.
-       I can live with this.
-
-       Known problem: if you look carefully at the animation
-       of pieces in mono mode, they are being drawn as solid
-       shapes without interior detail while moving. Fixing
-       this would be a major complication for minimal return.
-****/
+/****  Animation code by Hugh Fisher, DCS, ANU. ****/
 
 /*     Masks for XPM pieces. Black and white pieces can have
        different shapes, but in the interest of retaining my
@@ -6087,6 +5695,8 @@ OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, lon
        background square colors/images.                */
 
 static int xpmDone = 0;
+static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
+static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
 
 static void
 CreateAnimMasks (int pieceDepth)
@@ -6167,15 +5777,16 @@ CreateAnimMasks (int pieceDepth)
 }
 
 static void
-InitAnimState (AnimState *anim, XWindowAttributes *info)
+InitAnimState (AnimNr anr, XWindowAttributes *info)
 {
   XtGCMask  mask;
   XGCValues values;
 
   /* Each buffer is square size, same depth as window */
-  anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
+  animBufs[anr+4] = xBoardWindow;
+  animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
                        squareSize, squareSize, info->depth);
-  anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
+  animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
                        squareSize, squareSize, info->depth);
 
   /* Create a plain GC for blitting */
@@ -6186,14 +5797,14 @@ InitAnimState (AnimState *anim, XWindowAttributes *info)
   values.function   = GXcopy;
   values.plane_mask = AllPlanes;
   values.graphics_exposures = False;
-  anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
+  animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
 
   /* Piece will be copied from an existing context at
      the start of each new animation/drag. */
-  anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
+  animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
 
   /* Outline will be a read-only copy of an existing */
-  anim->outlineGC = None;
+  animGCs[anr+4] = None;
 }
 
 void
@@ -6205,8 +5816,8 @@ CreateAnimVars ()
   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
 
-  InitAnimState(&game, &info);
-  InitAnimState(&player, &info);
+  InitAnimState(Game, &info);
+  InitAnimState(Player, &info);
 
   /* For XPM pieces, we need bitmaps to use as masks. */
   if (useImages)
@@ -6225,7 +5836,7 @@ FrameAlarm (int sig)
   signal(SIGALRM, FrameAlarm);
 }
 
-static void
+void
 FrameDelay (int time)
 {
   struct itimerval delay;
@@ -6249,7 +5860,7 @@ FrameDelay (int time)
 
 #else
 
-static void
+void
 FrameDelay (int time)
 {
   XSync(xDisplay, False);
@@ -6259,159 +5870,6 @@ FrameDelay (int time)
 
 #endif
 
-void
-DoSleep (int n)
-{
-    FrameDelay(n);
-}
-
-/*     Convert board position to corner of screen rect and color       */
-
-static void
-ScreenSquare (int column, int row, XPoint *pt, int *color)
-{
-  if (flipView) {
-    pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
-    pt->y = lineGap + row * (squareSize + lineGap);
-  } else {
-    pt->x = lineGap + column * (squareSize + lineGap);
-    pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
-  }
-  *color = SquareColor(row, column);
-}
-
-/*     Convert window coords to square                 */
-
-static void
-BoardSquare (int x, int y, int *column, int *row)
-{
-  *column = EventToSquare(x, BOARD_WIDTH);
-  if (flipView && *column >= 0)
-    *column = BOARD_WIDTH - 1 - *column;
-  *row = EventToSquare(y, BOARD_HEIGHT);
-  if (!flipView && *row >= 0)
-    *row = BOARD_HEIGHT - 1 - *row;
-}
-
-/*   Utilities */
-
-#undef Max  /* just in case */
-#undef Min
-#define Max(a, b) ((a) > (b) ? (a) : (b))
-#define Min(a, b) ((a) < (b) ? (a) : (b))
-
-static void
-SetRect (XRectangle *rect, int x, int y, int width, int height)
-{
-  rect->x = x;
-  rect->y = y;
-  rect->width  = width;
-  rect->height = height;
-}
-
-/*     Test if two frames overlap. If they do, return
-       intersection rect within old and location of
-       that rect within new. */
-
-static Boolean
-Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
-{
-  if (old->x > new->x + size || new->x > old->x + size ||
-      old->y > new->y + size || new->y > old->y + size) {
-    return False;
-  } else {
-    SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
-            size - abs(old->x - new->x), size - abs(old->y - new->y));
-    pt->x = Max(old->x - new->x, 0);
-    pt->y = Max(old->y - new->y, 0);
-    return True;
-  }
-}
-
-/*     For two overlapping frames, return the rect(s)
-       in the old that do not intersect with the new.   */
-
-static void
-CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
-{
-  int       count;
-
-  /* If old = new (shouldn't happen) then nothing to draw */
-  if (old->x == new->x && old->y == new->y) {
-    *nUpdates = 0;
-    return;
-  }
-  /* Work out what bits overlap. Since we know the rects
-     are the same size we don't need a full intersect calc. */
-  count = 0;
-  /* Top or bottom edge? */
-  if (new->y > old->y) {
-    SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
-    count ++;
-  } else if (old->y > new->y) {
-    SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
-                             size, old->y - new->y);
-    count ++;
-  }
-  /* Left or right edge - don't overlap any update calculated above. */
-  if (new->x > old->x) {
-    SetRect(&(update[count]), old->x, Max(new->y, old->y),
-                             new->x - old->x, size - abs(new->y - old->y));
-    count ++;
-  } else if (old->x > new->x) {
-    SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
-                             old->x - new->x, size - abs(new->y - old->y));
-    count ++;
-  }
-  /* Done */
-  *nUpdates = count;
-}
-
-/*     Generate a series of frame coords from start->mid->finish.
-       The movement rate doubles until the half way point is
-       reached, then halves back down to the final destination,
-       which gives a nice slow in/out effect. The algorithmn
-       may seem to generate too many intermediates for short
-       moves, but remember that the purpose is to attract the
-       viewers attention to the piece about to be moved and
-       then to where it ends up. Too few frames would be less
-       noticeable.                                             */
-
-static void
-Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
-{
-  int fraction, n, count;
-
-  count = 0;
-
-  /* Slow in, stepping 1/16th, then 1/8th, ... */
-  fraction = 1;
-  for (n = 0; n < factor; n++)
-    fraction *= 2;
-  for (n = 0; n < factor; n++) {
-    frames[count].x = start->x + (mid->x - start->x) / fraction;
-    frames[count].y = start->y + (mid->y - start->y) / fraction;
-    count ++;
-    fraction = fraction / 2;
-  }
-
-  /* Midpoint */
-  frames[count] = *mid;
-  count ++;
-
-  /* Slow out, stepping 1/2, then 1/4, ... */
-  fraction = 2;
-  for (n = 0; n < factor; n++) {
-    frames[count].x = finish->x - (finish->x - mid->x) / fraction;
-    frames[count].y = finish->y - (finish->y - mid->y) / fraction;
-    count ++;
-    fraction = fraction * 2;
-  }
-  *nFrames = count;
-}
-
-/*     Draw a piece on the screen without disturbing what's there      */
-
 static void
 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
 {
@@ -6480,329 +5938,32 @@ OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
   }
 }
 
-/* Animate the movement of a single piece */
-
-static void
-BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
-{
-  Pixmap mask;
-
-  if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
-  /* The old buffer is initialised with the start square (empty) */
-  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   */
-  SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
-  XSetClipMask(xDisplay, anim->pieceGC, mask);
-}
-
-static void
-AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
-{
-  XRectangle updates[4];
-  XRectangle overlap;
-  XPoint     pt;
-  int       count, i;
-
-  /* Save what we are about to draw into the new buffer */
-  XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
-           frame->x, frame->y, squareSize, squareSize,
-           0, 0);
-
-  /* Erase bits of the previous frame */
-  if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
-    /* Where the new frame overlapped the previous,
-       the contents in newBuf are wrong. */
-    XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
-             overlap.x, overlap.y,
-             overlap.width, overlap.height,
-             pt.x, pt.y);
-    /* Repaint the areas in the old that don't overlap new */
-    CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
-    for (i = 0; i < count; i++)
-      XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
-               updates[i].x - anim->prevFrame.x,
-               updates[i].y - anim->prevFrame.y,
-               updates[i].width, updates[i].height,
-               updates[i].x, updates[i].y);
-  } else {
-    /* Easy when no overlap */
-    XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
-                 0, 0, squareSize, squareSize,
-                 anim->prevFrame.x, anim->prevFrame.y);
-  }
-
-  /* Save this frame for next time round */
-  XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
-               0, 0, squareSize, squareSize,
-               0, 0);
-  anim->prevFrame = *frame;
-
-  /* Draw piece over original screen contents, not current,
-     and copy entire rect. Wipes out overlapping piece images. */
-  OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
-  XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
-               0, 0, squareSize, squareSize,
-               frame->x, frame->y);
-}
-
-static void
-EndAnimation (AnimState *anim, XPoint *finish)
-{
-  XRectangle updates[4];
-  XRectangle overlap;
-  XPoint     pt;
-  int       count, i;
-
-  /* The main code will redraw the final square, so we
-     only need to erase the bits that don't overlap.   */
-  if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
-    CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
-    for (i = 0; i < count; i++)
-      XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
-               updates[i].x - anim->prevFrame.x,
-               updates[i].y - anim->prevFrame.y,
-               updates[i].width, updates[i].height,
-               updates[i].x, updates[i].y);
-  } else {
-    XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
-               0, 0, squareSize, squareSize,
-               anim->prevFrame.x, anim->prevFrame.y);
-  }
-}
-
-static void
-FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
-{
-  int n;
-
-  BeginAnimation(anim, piece, startColor, start);
-  for (n = 0; n < nFrames; n++) {
-    AnimationFrame(anim, &(frames[n]), piece);
-    FrameDelay(appData.animSpeed);
-  }
-  EndAnimation(anim, finish);
-}
-
 void
-AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
+InsertPiece (AnimNr anr, ChessSquare piece)
 {
-    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;
+  OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
 }
 
-/* Main control logic for deciding what to animate and how */
-
 void
-AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
+DrawBlank (AnimNr anr, int x, int y, int startColor)
 {
-  ChessSquare piece;
-  int hop;
-  XPoint      start, finish, mid;
-  XPoint      frames[kFactor * 2 + 1];
-  int        nFrames, startColor, endColor;
-
-  /* Are we animating? */
-  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;
-
-#if DONT_HOP
-  hop = FALSE;
-#else
-  hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
-#endif
-
-  ScreenSquare(fromX, fromY, &start, &startColor);
-  ScreenSquare(toX, toY, &finish, &endColor);
-
-  if (hop) {
-    /* Knight: make straight movement then diagonal */
-    if (abs(toY - fromY) < abs(toX - fromX)) {
-       mid.x = start.x + (finish.x - start.x) / 2;
-       mid.y = start.y;
-     } else {
-       mid.x = start.x;
-       mid.y = start.y + (finish.y - start.y) / 2;
-     }
-  } else {
-    mid.x = start.x + (finish.x - start.x) / 2;
-    mid.y = start.y + (finish.y - start.y) / 2;
-  }
-
-  /* Don't use as many frames for very short moves */
-  if (abs(toY - fromY) + abs(toX - fromX) <= 2)
-    Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
-  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;
+    BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
 }
 
-void
-DragPieceBegin (int x, int y, Boolean instantly)
+void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
+                int srcX, int srcY, int width, int height, int destX, int destY)
 {
-    int         boardX, boardY, color;
-    XPoint corner;
-
-    /* Are we animating? */
-    if (!appData.animateDragging || appData.blindfold)
-      return;
-
-    /* Figure out which square we start in and the
-       mouse position relative to top left corner. */
-    BoardSquare(x, y, &boardX, &boardY);
-    player.startBoardX = boardX;
-    player.startBoardY = boardY;
-    ScreenSquare(boardX, boardY, &corner, &color);
-    player.startSquare  = corner;
-    player.startColor   = color;
-    /* 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;
-    /* Initialise animation */
-    player.dragPiece = PieceForSquare(boardX, boardY);
-    /* Sanity check */
-    if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
-       player.dragActive = True;
-       BeginAnimation(&player, player.dragPiece, color, &corner);
-       /* Mark this square as needing to be redrawn. Note that
-          we don't remove the piece though, since logically (ie
-          as seen by opponent) the move hasn't been made yet. */
-           if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
-              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] 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;
-    }
+    XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
+               srcX, srcY, width, height, destX, destY);
 }
 
 void
-ChangeDragPiece (ChessSquare piece)
+SetDragPiece (AnimNr anr, ChessSquare piece)
 {
   Pixmap mask;
-  player.dragPiece = piece;
   /* The piece will be drawn using its own bitmap as a matte   */
-  SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
-  XSetClipMask(xDisplay, player.pieceGC, mask);
-}
-
-static void
-DragPieceMove (int x, int y)
-{
-    XPoint corner;
-
-    /* Are we animating? */
-    if (!appData.animateDragging || appData.blindfold)
-      return;
-
-    /* Sanity check */
-    if (! player.dragActive)
-      return;
-    /* Move piece, maintaining same relative position
-       of mouse within square   */
-    corner.x = x - player.mouseDelta.x;
-    corner.y = y - player.mouseDelta.y;
-    AnimationFrame(&player, &corner, player.dragPiece);
-#if HIGHDRAG*0
-    if (appData.highlightDragging) {
-       int boardX, boardY;
-       BoardSquare(x, y, &boardX, &boardY);
-       SetHighlights(fromX, fromY, boardX, boardY);
-    }
-#endif
-}
-
-void
-DragPieceEnd (int x, int y)
-{
-    int boardX, boardY, color;
-    XPoint corner;
-
-    /* Are we animating? */
-    if (!appData.animateDragging || appData.blindfold)
-      return;
-
-    /* Sanity check */
-    if (! player.dragActive)
-      return;
-    /* Last frame in sequence is square piece is
-       placed on, which may not match mouse exactly. */
-    BoardSquare(x, y, &boardX, &boardY);
-    ScreenSquare(boardX, boardY, &corner, &color);
-    EndAnimation(&player, &corner);
-
-    /* Be sure end square is redrawn */
-    damage[0][boardY][boardX] = True;
-
-    /* This prevents weird things happening with fast successive
-       clicks which on my Sun at least can cause motion events
-       without corresponding press/release. */
-    player.dragActive = False;
-}
-
-/* Handle expose event while piece being dragged */
-
-static void
-DrawDragPiece ()
-{
-  if (!player.dragActive || appData.blindfold)
-    return;
-
-  /* What we're doing: logically, the move hasn't been made yet,
-     so the piece is still in it's original square. But visually
-     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, 1);
-  AnimationFrame(&player, &player.prevFrame, player.dragPiece);
-  damage[0][player.startBoardY][player.startBoardX] = TRUE;
+  SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
+  XSetClipMask(xDisplay, animGCs[anr+2], mask);
 }
 
 #include <sys/ioctl.h>
@@ -6845,209 +6006,14 @@ NotifyFrontendLogin ()
 
 /* [AS] Arrow highlighting support */
 
-static double A_WIDTH = 5; /* Width of arrow body */
-
-#define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
-#define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
-
-static double
-Sqr (double x)
-{
-    return x*x;
-}
-
-static int
-Round (double x)
-{
-    return (int) (x + 0.5);
-}
-
-void
-SquareToPos (int rank, int file, int *x, int *y)
-{
-    if (flipView) {
-       *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
-       *y = lineGap + rank * (squareSize + lineGap);
-    } else {
-       *x = lineGap + file * (squareSize + lineGap);
-       *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
-    }
-}
-
-/* Draw an arrow between two points using current settings */
-void
-DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
-{
-    XPoint arrow[8];
-    double dx, dy, j, k, x, y;
-
-    if( d_x == s_x ) {
-        int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
-
-        arrow[0].x = s_x + A_WIDTH + 0.5;
-        arrow[0].y = s_y;
-
-        arrow[1].x = s_x + A_WIDTH + 0.5;
-        arrow[1].y = d_y - h;
-
-        arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
-        arrow[2].y = d_y - h;
-
-        arrow[3].x = d_x;
-        arrow[3].y = d_y;
-
-        arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
-        arrow[5].y = d_y - h;
-
-        arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
-        arrow[4].y = d_y - h;
-
-        arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
-        arrow[6].y = s_y;
-    }
-    else if( d_y == s_y ) {
-        int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
-
-        arrow[0].x = s_x;
-        arrow[0].y = s_y + A_WIDTH + 0.5;
-
-        arrow[1].x = d_x - w;
-        arrow[1].y = s_y + A_WIDTH + 0.5;
-
-        arrow[2].x = d_x - w;
-        arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
-
-        arrow[3].x = d_x;
-        arrow[3].y = d_y;
-
-        arrow[5].x = d_x - w;
-        arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
-
-        arrow[4].x = d_x - w;
-        arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
-
-        arrow[6].x = s_x;
-        arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
-    }
-    else {
-        /* [AS] Needed a lot of paper for this! :-) */
-        dy = (double) (d_y - s_y) / (double) (d_x - s_x);
-        dx = (double) (s_x - d_x) / (double) (s_y - d_y);
-
-        j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
-
-        k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
-
-        x = s_x;
-        y = s_y;
-
-        arrow[0].x = Round(x - j);
-        arrow[0].y = Round(y + j*dx);
-
-        arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice
-        arrow[1].y = Round(arrow[0].y - 2*j*dx);
-
-        if( d_x > s_x ) {
-            x = (double) d_x - k;
-            y = (double) d_y - k*dy;
-        }
-        else {
-            x = (double) d_x + k;
-            y = (double) d_y + k*dy;
-        }
-
-        x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
-
-        arrow[6].x = Round(x - j);
-        arrow[6].y = Round(y + j*dx);
-
-        arrow[2].x = Round(arrow[6].x + 2*j);
-        arrow[2].y = Round(arrow[6].y - 2*j*dx);
-
-        arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
-        arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
-
-        arrow[4].x = d_x;
-        arrow[4].y = d_y;
-
-        arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
-        arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
-    }
-
-    XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
-    if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
-//    Polygon( hdc, arrow, 7 );
-}
-
-void
-ArrowDamage (int s_col, int s_row, int d_col, int d_row)
-{
-    int hor, vert, i;
-    hor = 64*s_col + 32; vert = 64*s_row + 32;
-    for(i=0; i<= 64; i++) {
-            damage[0][vert+6>>6][hor+6>>6] = True;
-            damage[0][vert-6>>6][hor+6>>6] = True;
-            damage[0][vert+6>>6][hor-6>>6] = True;
-            damage[0][vert-6>>6][hor-6>>6] = True;
-            hor += d_col - s_col; vert += d_row - s_row;
-    }
-}
-
-/* [AS] Draw an arrow between two squares */
-void
-DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
-{
-    int s_x, s_y, d_x, d_y;
-
-    if( s_col == d_col && s_row == d_row ) {
-        return;
-    }
-
-    /* Get source and destination points */
-    SquareToPos( s_row, s_col, &s_x, &s_y);
-    SquareToPos( d_row, d_col, &d_x, &d_y);
-
-    if( d_y > s_y ) {
-        d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
-    }
-    else if( d_y < s_y ) {
-        d_y += squareSize / 2 + squareSize / 4;
-    }
-    else {
-        d_y += squareSize / 2;
-    }
-
-    if( d_x > s_x ) {
-        d_x += squareSize / 2 - squareSize / 4;
-    }
-    else if( d_x < s_x ) {
-        d_x += squareSize / 2 + squareSize / 4;
-    }
-    else {
-        d_x += squareSize / 2;
-    }
-
-    s_x += squareSize / 2;
-    s_y += squareSize / 2;
-
-    /* Adjust width */
-    A_WIDTH = squareSize / 14.; //[HGM] make float
-
-    DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
-    ArrowDamage(s_col, s_row, d_col, d_row);
-}
-
-Boolean
-IsDrawArrowEnabled ()
-{
-    return appData.highlightMoveWithArrow && squareSize >= 32;
-}
-
 void
-DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
+DrawPolygon (Pnt arrow[], int nr)
 {
-    if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
-        DrawArrowBetweenSquares(fromX, fromY, toX, toY);
+    XPoint pts[10];
+    int i;
+    for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
+    XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
+    if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
 }
 
 void