* Massachusetts.
*
* Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free
+ * Software Foundation, Inc.
*
* The following terms apply to Digital Equipment Corporation's copyright
* interest in XBoard:
#include "common.h"
#include "frontend.h"
#include "backend.h"
+#include "xboard2.h"
#include "moves.h"
#include "board.h"
+#include "draw.h"
#ifdef __EMX__
#endif
-int squareSize, lineGap, hOffset;
+int squareSize, lineGap;
int damage[2][BOARD_RANKS][BOARD_FILES];
AnimState anims[NrOfAnims];
static void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
+static Boolean IsDrawArrowEnabled P((void));
+static void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
+static void ArrowDamage P((int s_col, int s_row, int d_col, int d_row));
static void
drawHighlight (int file, int rank, int type)
(squareSize + lineGap);
}
- DrawBorder(x,y, type);
+ DrawBorder(x,y, type, lineGap & 1); // pass whether lineGap is odd
}
int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
void
SetHighlights (int fromX, int fromY, int toX, int toY)
-{
- if (hi1X != fromX || hi1Y != fromY) {
- if (hi1X >= 0 && hi1Y >= 0) {
- drawHighlight(hi1X, hi1Y, 0);
- }
- } // [HGM] first erase both, then draw new!
+{ // [HGM] schedule old for erasure, and leave drawing new to DrawPosition
+ int change = 0;
- if (hi2X != toX || hi2Y != toY) {
- if (hi2X >= 0 && hi2Y >= 0) {
- drawHighlight(hi2X, hi2Y, 0);
- }
- }
- if (hi1X != fromX || hi1Y != fromY) {
- if (fromX >= 0 && fromY >= 0) {
- drawHighlight(fromX, fromY, 1);
+ if (hi1X >= 0 && hi1Y >= 0) {
+ if (hi1X != fromX || hi1Y != fromY) {
+ damage[0][hi1Y][hi1X] |= 2;
+ change |= 1;
}
+ change |= 4;
}
- if (hi2X != toX || hi2Y != toY) {
- if (toX >= 0 && toY >= 0) {
- drawHighlight(toX, toY, 1);
+
+ if (hi2X >= 0 && hi2Y >= 0) {
+ if (hi2X != toX || hi2Y != toY) {
+ damage[0][hi2Y][hi2X] |= 2;
+ change |= 2;
}
+ change |= 8;
}
- if(toX<0) // clearing the highlights must have damaged arrow
- DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y); // for now, redraw it (should really be cleared!)
+ if(change > 12 && IsDrawArrowEnabled()) ArrowDamage(hi1X, hi1Y, hi2X, hi2Y);
hi1X = fromX;
hi1Y = fromY;
{
if (pm1X != fromX || pm1Y != fromY) {
if (pm1X >= 0 && pm1Y >= 0) {
- drawHighlight(pm1X, pm1Y, 0);
- }
- if (fromX >= 0 && fromY >= 0) {
- drawHighlight(fromX, fromY, 2);
+ damage[0][pm1Y][pm1X] |= 2;
}
}
if (pm2X != toX || pm2Y != toY) {
if (pm2X >= 0 && pm2Y >= 0) {
- drawHighlight(pm2X, pm2Y, 0);
- }
- if (toX >= 0 && toY >= 0) {
- drawHighlight(toX, toY, 2);
+ damage[0][pm1Y][pm1X] |= 2;
}
}
pm1X = fromX;
MyRectangle updates[4];
MyRectangle overlap;
Pnt pt;
- int count, i;
AnimState *anim = &anims[anr];
+ int count, i, x, y, w, h;
/* Save what we are about to draw into the new buffer */
CopyRectangle(anr, DISP, 0,
- frame->x, frame->y, squareSize, squareSize,
+ x = frame->x, y = frame->y, w = squareSize, h = squareSize,
0, 0);
/* Erase bits of the previous frame */
updates[i].y - anim->prevFrame.y,
updates[i].width, updates[i].height,
updates[i].x, updates[i].y);
+ /* [HGM] correct expose rectangle to encompass both overlapping squares */
+ if(x > anim->prevFrame.x) w += x - anim->prevFrame.x, x = anim->prevFrame.x;
+ else w += anim->prevFrame.x - x;
+ if(y > anim->prevFrame.y) h += y - anim->prevFrame.y, y = anim->prevFrame.y;
+ else h += anim->prevFrame.y - y;
} else {
/* Easy when no overlap */
CopyRectangle(anr, 2, DISP,
0, 0, squareSize, squareSize,
anim->prevFrame.x, anim->prevFrame.y);
+ GraphExpose(currBoard, anim->prevFrame.x, anim->prevFrame.y, squareSize, squareSize);
}
/* Save this frame for next time round */
CopyRectangle(anr, 0, DISP,
0, 0, squareSize, squareSize,
frame->x, frame->y);
+ GraphExpose(currBoard, x, y, w, h);
}
static void
FrameDelay(appData.animSpeed);
}
board[fromY][toY] = piece;
+ DrawGrid();
}
/* Main control logic for deciding what to animate and how */
AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
{
ChessSquare piece;
- int hop;
+ int hop, x = toX, y = toY, x2 = kill2X;
Pnt start, finish, mid;
Pnt frames[kFactor * 2 + 1];
int nFrames, startColor, endColor;
+ if(killX >= 0 && IS_LION(board[fromY][fromX])) Roar();
+
/* 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)
+ board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing ||
+ board[toY][toX] == WhiteKing && board[fromY][fromX] == WhiteRook || // [HGM] seirawan
+ board[toY][toX] == BlackKing && board[fromY][fromX] == BlackRook)
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(x2 >= 0) toX = kill2X, toY = kill2Y; else
+ if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square
+
+again:
+
#if DONT_HOP
hop = FALSE;
#else
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;
+ if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] |= 1 + ((i-toX ^ j-toY) & 1);
}
- /* Be sure end square is redrawn */
- damage[0][toY][toX] = True;
+ /* Be sure end square is redrawn, with piece in it */
+ damage[0][toY][toX] |= 4;
+
+ if(toX == x2 && toY == kill2Y) { fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1; goto again; } // second leg
+ if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg
}
void
{
anims[Player].dragPiece = piece;
SetDragPiece(Player, piece);
+ damage[0][fromY][fromX] = True;
}
void
/* 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. */
- damage[0][boardY][boardX] = True;
+ damage[0][boardY][boardX] |= True;
} else {
anims[Player].dragActive = False;
}
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. */
DrawOneSquare(anims[Player].startSquare.x, anims[Player].startSquare.y,
- EmptySquare, anims[Player].startColor, 0, NULL, 0);
+ EmptySquare, anims[Player].startColor, 0, NULL, NULL, 0);
AnimationFrame(Player, &anims[Player].prevFrame, anims[Player].dragPiece);
- damage[0][anims[Player].startBoardY][anims[Player].startBoardX] = TRUE;
+ damage[0][anims[Player].startBoardY][anims[Player].startBoardX] |= TRUE;
}
static void
{
int square_color, x, y, align=0;
int i;
- char string[2];
+ char tString[3], bString[2];
int flash_delay;
/* Calculate delay in milliseconds (2-delays per complete flash) */
(squareSize + lineGap);
}
- if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
-
square_color = SquareColor(row, column);
- string[1] = NULLCHAR;
+ bString[1] = bString[0] = NULLCHAR;
if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
&& column >= BOARD_LEFT && column < BOARD_RGHT) {
- string[0] = 'a' + column - BOARD_LEFT;
+ bString[0] = 'a' + column - BOARD_LEFT;
align = 1; // coord in lower-right corner
}
if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
- string[0] = ONE + row;
+ snprintf(tString, 3, "%d", ONE - '0' + row);
align = 2; // coord in upper-left corner
}
if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
- string[0] = '0' + piece;
+ snprintf(tString, 3, "%d", piece);
align = 3; // holdings count in upper-right corner
}
if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
- string[0] = '0' + piece;
+ snprintf(tString, 3, "%d", piece);
align = 4; // holdings count in upper-left corner
}
+ if(piece == DarkSquare) square_color = 2;
if(square_color == 2 || appData.blindfold) piece = EmptySquare;
if (do_flash && piece != EmptySquare && appData.flashCount > 0) {
for (i=0; i<appData.flashCount; ++i) {
- DrawOneSquare(x, y, piece, square_color, 0, string, 0);
+ DrawOneSquare(x, y, piece, square_color, 0, tString, bString, 0);
+ GraphExpose(currBoard, x, y, squareSize, squareSize);
+ DoEvents(); // requires event processing to actually update screen :-(
FlashDelay(flash_delay);
- DrawOneSquare(x, y, EmptySquare, square_color, 0, string, 0);
+ DrawOneSquare(x, y, EmptySquare, square_color, 0, tString, bString, 0);
+ GraphExpose(currBoard, x, y, squareSize, squareSize);
+ DoEvents();
FlashDelay(flash_delay);
}
}
- DrawOneSquare(x, y, piece, square_color, partnerUp ? 0 : marker[row][column], string, align);
+ DrawOneSquare(x, y, piece, square_color, partnerUp ? 0 : marker[row][column], tString, bString, align);
}
/* Returns 1 if there are "too many" differences between b1 and b2
}
void
+SquareExpose(int i, int j, int d)
+{
+ int x, y;
+ if (flipView) {
+ x = lineGap + ((BOARD_WIDTH-1)-j) *
+ (squareSize + lineGap);
+ y = lineGap + i * (squareSize + lineGap);
+ } else {
+ x = lineGap + j * (squareSize + lineGap);
+ y = lineGap + ((BOARD_HEIGHT-1)-i) *
+ (squareSize + lineGap);
+ }
+ GraphExpose(currBoard, x-d, y-d, squareSize+2*d, squareSize+2*d);
+}
+
+void
DrawPosition (int repaint, Board board)
{
- int i, j, do_flash;
+ int i, j, do_flash, exposeAll = False;
static int lastFlipView = 0;
static int lastBoardValid[2] = {0, 0};
static Board lastBoard[2];
- Arg args[16];
- int rrow, rcol;
+ static char lastMarker[BOARD_RANKS][BOARD_FILES], messedUp;
+ int rrow = -1, rcol = -1;
int nr = twoBoards*partnerUp;
+ repaint |= messedUp;
+
if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
if (board == NULL) {
board = lastBoard[nr];
}
if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
- MarkMenuItem("Flip View", flipView);
+ MarkMenuItem("View.Flip View", flipView);
}
+ if(nr) { SlavePopUp(); SwitchWindow(0); } // [HGM] popup board if not yet popped up, and switch drawing to it.
+
/*
* 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())
- DrawGrid(0);
-
/* 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];
+ if(check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
+ /* Mark rook for drawing with NO flashing. */
+ damage[nr][rrow][rcol] |= 1;
}
}
- /* First pass -- Draw (newly) empty squares and repair damage.
+ /* First pass -- Erase arrow and grid highlights, but keep square content unchanged. Except for new markers. */
+ for (i = 0; i < BOARD_HEIGHT; i++)
+ for (j = 0; j < BOARD_WIDTH; j++)
+ if (damage[nr][i][j] || !nr && marker[i][j] != lastMarker[i][j]) {
+ DrawSquare(i, j, board[i][j], 0);
+ if(lineGap && damage[nr][i][j] & 2) {
+ drawHighlight(j, i, 0);
+ SquareExpose(i, j, lineGap);
+ } else SquareExpose(i, j, 0);
+ damage[nr][i][j] = 0;
+ }
+
+ /* Second pass -- Draw (newly) empty squares
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]) {
+ if (board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare) {
DrawSquare(i, j, board[i][j], 0);
- damage[nr][i][j] = False;
+ SquareExpose(i, j, 0);
}
- /* Second pass -- Draw piece(s) in new position and flash them */
+ /* Third 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);
+ DrawSquare(i, j, board[i][j], do_flash && (i != rrow || j != rcol));
+ damage[nr][i][j] = 1; // mark for expose
}
+
} else {
if (lineGap > 0)
- DrawGrid(twoBoards & partnerUp);
+ DrawGrid();
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;
}
+
+ exposeAll = True;
}
CopyBoard(lastBoard[nr], board);
lastBoardValid[nr] = 1;
if(nr == 0) { // [HGM] dual: no highlights on second board yet
lastFlipView = flipView;
+ for (i = 0; i < BOARD_HEIGHT; i++)
+ for (j = 0; j < BOARD_WIDTH; j++)
+ lastMarker[i][j] = marker[i][j];
/* Draw highlights */
if (pm1X >= 0 && pm1Y >= 0) {
drawHighlight(pm1X, pm1Y, 2);
+ if(lineGap) damage[nr][pm1Y][pm1X] |= 2;
}
if (pm2X >= 0 && pm2Y >= 0) {
drawHighlight(pm2X, pm2Y, 2);
+ if(lineGap) damage[nr][pm2Y][pm2X] |= 2;
}
if (hi1X >= 0 && hi1Y >= 0) {
drawHighlight(hi1X, hi1Y, 1);
+ if(lineGap) damage[nr][hi1Y][hi1X] |= 2;
}
if (hi2X >= 0 && hi2Y >= 0) {
drawHighlight(hi2X, hi2Y, 1);
+ if(lineGap) damage[nr][hi2Y][hi2X] |= 2;
}
DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
}
+ else DrawArrowHighlight (board[EP_STATUS-3], board[EP_STATUS-4], board[EP_STATUS-1], board[EP_STATUS-2]);
+
/* If piece being dragged around board, must redraw that too */
DrawDragPiece();
+ if(exposeAll)
+ GraphExpose(currBoard, 0, 0, BOARD_WIDTH*(squareSize + lineGap) + lineGap, BOARD_HEIGHT*(squareSize + lineGap) + lineGap);
+ else {
+ for (i = 0; i < BOARD_HEIGHT; i++)
+ for (j = 0; j < BOARD_WIDTH; j++)
+ if(damage[nr][i][j]) {
+ if(damage[nr][i][j] & 2) // damage by old or new arrow
+ SquareExpose(i, j, lineGap);
+ else
+ SquareExpose(i, j, 0);
+ damage[nr][i][j] = 0;
+ }
+ }
+
FlashDelay(0); // this flushes drawing queue;
+ if(nr) SwitchWindow(1);
+ else {
+ TimeMark now;
+ GetTimeMark(&now);
+ if(SubtractTimeMarks(&now, &programStartTime) < 1000) {
+ DrawSeekBackground(2*squareSize, 3*squareSize, 6*squareSize, 5*squareSize);
+ DrawText("Right-clicking dialog texts", 2*squareSize + 5, 3*squareSize + 5, 2);
+ DrawText("pops up help on them", 2*squareSize + 5, (int) (3.3*squareSize) + 5, 2);
+ GraphExpose(currBoard, 2*squareSize, 3*squareSize, 4*squareSize, 2*squareSize);
+ messedUp = TRUE;
+ } else messedUp = FALSE;
+ }
}
/* [AS] Arrow highlighting support */
}
/* Draw an arrow between two points using current settings */
-void
+static void
DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
{
Pnt arrow[8];
// Polygon( hdc, arrow, 7 );
}
-void
+static 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;
+ int hor, vert, i, n = partnerUp * twoBoards, delta = abs(d_row - s_row);
+
+ if( 2*(d_row - s_row) > abs(d_col - s_col) ) d_row = 4*d_row + 1; else
+ if( 2*(s_row - d_row) > abs(d_col - s_col) ) d_row = 4*d_row + 3; else d_row = 4*d_row + 2;
+ if( 2*(d_col - s_col) > delta ) d_col = 4*d_col + 1; else
+ if( 2*(s_col - d_col) > delta ) d_col = 4*d_col + 3; else d_col = 4*d_col + 2;
+ s_row = 4*s_row + 2; s_col = 4*s_col + 2;
+
+ hor = 64*s_col; vert = 64*s_row;
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;
+ damage[n][vert+30>>8][hor+30>>8] |= 2;
+ damage[n][vert-30>>8][hor+30>>8] |= 2;
+ damage[n][vert+30>>8][hor-30>>8] |= 2;
+ damage[n][vert-30>>8][hor-30>>8] |= 2;
hor += d_col - s_col; vert += d_row - s_row;
}
}
/* [AS] Draw an arrow between two squares */
-void
+static void
DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
{
- int s_x, s_y, d_x, d_y;
+ int s_x, s_y, d_x, d_y, delta_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);
+ delta_y = abs(d_y - s_y);
- if( d_y > s_y ) {
+ if( d_y > s_y && 2*(d_y - s_y) > abs(d_x - s_x)) {
d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
}
- else if( d_y < s_y ) {
+ else if( d_y < s_y && 2*(s_y - d_y) > abs(d_x - s_x)) {
d_y += squareSize / 2 + squareSize / 4;
}
else {
d_y += squareSize / 2;
}
- if( d_x > s_x ) {
+ if( d_x > s_x && 2*(d_x - s_x) > delta_y) {
d_x += squareSize / 2 - squareSize / 4;
}
- else if( d_x < s_x ) {
+ else if( d_x < s_x && 2*(s_x - d_x) > delta_y) {
d_x += squareSize / 2 + squareSize / 4;
}
else {
ArrowDamage(s_col, s_row, d_col, d_row);
}
-Boolean
+static Boolean
IsDrawArrowEnabled ()
{
- return appData.highlightMoveWithArrow && squareSize >= 32;
+ return (appData.highlightMoveWithArrow || twoBoards && partnerUp) && squareSize >= 32;
}
-void
+static void
DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
{
if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
DrawArrowBetweenSquares(fromX, fromY, toX, toY);
}
-
-