DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T b

⟦1e71904c0⟧ TextFile

    Length: 34145 (0x8561)
    Types: TextFile
    Names: »boardsw.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/Sun/Nchess/boardsw.c« 

TextFile

/*
 * Copyright 1987 Tom Anderson; 20831 Frank Waters Road;
 * Stanwood, WA  98282.   All rights reserved.
 */

/*
 * handle the board subwindow (as well as fielding RPC and chess game
 * process selects)
 */

#include <stdio.h>
#include <suntool/tool_hs.h>
#include <suntool/panel.h>
#include <suntool/gfxsw.h>
#include <suntool/icon_load.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <strings.h>

#include "nchess.h"

#define	MOVE_PR_WIDTH	(SQUARE_WIDTH * 2)
#define	MOVE_PR_HEIGHT	(SQUARE_HEIGHT * 2)
#define	MOVE_X_OFFSET	(MOVE_PR_WIDTH/2 - SQUARE_WIDTH/2)
#define	MOVE_Y_OFFSET	(MOVE_PR_HEIGHT/2 - SQUARE_HEIGHT/2)

extern int svc_fds;			/* RPC service file descriptor(s) */
int BoardSWMask;			/* board gfx sw file des. */
BOOL Flashing = FALSE;			/* tool icon is flashing */

enum {					/* confirmation state using mouse */
    CONFIRM_WANTED,
    CONFIRM_SETUP_END,
    CONFIRM_WHOSE_TURN,
    CONFIRM_UNDO,
    CONFIRM_RESIGNATION,
} confirmState;

MouseState Mouse = IDLE;

char * PieceIconFileNames[] = {
    "pawn.icon",
    "knight.icon",
    "bishop.icon",
    "rook.icon",
    "queen.icon",
    "king.icon",
};

char * PieceStencilFileNames[] = {
    "pawnStencil.icon",
    "knightStencil.icon",
    "bishopStencil.icon",
    "rookStencil.icon",
    "queenStencil.icon",
    "kingStencil.icon",
};

/*
 * piece icons and stencils
 */
unsigned short RookBlackImage[] = {
#include "Icons/rook.icon"
};
unsigned short RookWhiteImage[] = {
#include "Icons/rook.icon"
};
unsigned short RookStencilImage[] = {
#include "Icons/rookStencil.icon"
};
mpr_static(RookBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, RookBlackImage);
mpr_static(RookWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, RookWhiteImage);
mpr_static(RookStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, RookStencilImage);

unsigned short KnightBlackImage[] = {
#include "Icons/knight.icon"
};
unsigned short KnightWhiteImage[] = {
#include "Icons/knight.icon"
};
unsigned short KnightStencilImage[] = {
#include "Icons/knightStencil.icon"
};
mpr_static(KnightBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KnightBlackImage);
mpr_static(KnightWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KnightWhiteImage);
mpr_static(KnightStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KnightStencilImage);

unsigned short BishopBlackImage[] = {
#include "Icons/bishop.icon"
};
unsigned short BishopWhiteImage[] = {
#include "Icons/bishop.icon"
};
unsigned short BishopStencilImage[] = {
#include "Icons/bishopStencil.icon"
};
mpr_static(BishopBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BishopBlackImage);
mpr_static(BishopWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BishopWhiteImage);
mpr_static(BishopStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BishopStencilImage);

unsigned short KingBlackImage[] = {
#include "Icons/king.icon"
};
unsigned short KingWhiteImage[] = {
#include "Icons/king.icon"
};
unsigned short KingStencilImage[] = {
#include "Icons/kingStencil.icon"
};
mpr_static(KingBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KingBlackImage);
mpr_static(KingWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KingWhiteImage);
mpr_static(KingStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, KingStencilImage);

unsigned short QueenBlackImage[] = {
#include "Icons/queen.icon"
};
unsigned short QueenWhiteImage[] = {
#include "Icons/queen.icon"
};
unsigned short QueenStencilImage[] = {
#include "Icons/queenStencil.icon"
};
mpr_static(QueenBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, QueenBlackImage);
mpr_static(QueenWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, QueenWhiteImage);
mpr_static(QueenStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, QueenStencilImage);

unsigned short PawnBlackImage[] = {
#include "Icons/pawn.icon"
};
unsigned short PawnWhiteImage[] = {
#include "Icons/pawn.icon"
};
unsigned short PawnStencilImage[] = {
#include "Icons/pawnStencil.icon"
};
mpr_static(PawnBlackPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, PawnBlackImage);
mpr_static(PawnWhitePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, PawnWhiteImage);
mpr_static(PawnStencilPR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, PawnStencilImage);

struct pixrect * PieceStencils[6] = {
    &PawnStencilPR,
    &KnightStencilPR,
    &BishopStencilPR,
    &RookStencilPR,
    &QueenStencilPR,
    &KingStencilPR,
};

struct pixrect * PieceIcons[6][2] = {
    &PawnBlackPR,	&PawnWhitePR,
    &KnightBlackPR,	&KnightWhitePR,
    &BishopBlackPR,	&BishopWhitePR,
    &RookBlackPR,	&RookWhitePR,
    &QueenBlackPR,	&QueenWhitePR,
    &KingBlackPR,	&KingWhitePR,
};

/*
 * blank square pixrects
 */
unsigned short WhiteSquareImage[] = {
#include "Icons/whiteSquare.icon"
};
mpr_static(WhiteSquarePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, WhiteSquareImage);

unsigned short BlackSquareImage[] = {
#include "Icons/blackSquare.icon"
};
mpr_static(BlackSquarePR, SQUARE_WIDTH, SQUARE_HEIGHT, 1, BlackSquareImage);

/* board subwindow handles */
struct toolsw * BoardSW;
struct gfxsubwindow * Board;

/* pixrects used for piece animation */
struct pixrect * MoveFromPR, * MoveToPR;

/* pixrect used for victim drawing */
struct pixrect * VictimPR;

/*
 * board sigwinch handler 
 */
/*ARGSUSED*/
boardSigwinch(sw)
    caddr_t sw;
{
    gfxsw_interpretesigwinch(Board);
    gfxsw_handlesigwinch(Board);
    if (Board->gfx_flags & GFX_RESTART) {
	Board->gfx_flags &= ~ GFX_RESTART;
	DrawBoard();
    }
}

/*
 * map a mouse coordinate to a board coordinate
 */
void
mapMouseToBoard(mlocp, blocp)
    struct pr_pos * mlocp;
    BoardCoordinate * blocp;
{
    if (MyColor == WHITE) {
	blocp->x = mlocp->x / (SQUARE_WIDTH-1);
	blocp->y = mlocp->y / (SQUARE_HEIGHT-1);
    } else {
	blocp->x = (8 * (SQUARE_WIDTH-1) - mlocp->x)/(SQUARE_WIDTH-1);
	blocp->y = (8 * (SQUARE_HEIGHT-1) - mlocp->y)/(SQUARE_HEIGHT-1);
    }
}

/* 
 * map a board coordinate to a mouse coordinate
 */
void
mapBoardToMouse(blocp, mlocp)
    BoardCoordinate * blocp;
    struct pr_pos * mlocp;
{
    if (MyColor == WHITE) {
	mlocp->x = blocp->x * (SQUARE_WIDTH-1) - 1;
	mlocp->y = blocp->y * (SQUARE_HEIGHT-1) - 1;
    } else {
	mlocp->x = (7 - blocp->x) * (SQUARE_WIDTH-1) - 1;
	mlocp->y = (7 - blocp->y) * (SQUARE_WIDTH-1) - 1;
    }
}

/*
 * put a piece back where we got it (used to abort piece animation for 
 * various reasons)
 */
void
putPieceBack(from, stencil, icon)
    BoardCoordinate * from;
    struct pixrect * stencil, * icon;
{
    struct pr_pos loc;

    mapBoardToMouse(from, &loc);
    pw_stencil(Board->gfx_pixwin, 
	loc.x, loc.y,
	SQUARE_WIDTH, SQUARE_HEIGHT,
	PIX_SRC, stencil, 0, 0, icon, 0, 0);
}

/*
 * parameters which belong in boardSelected(), but which are shared
 * with RequestUndo() so that we can abort piece animation if the 
 * klutz at the other end panics (via an undo request or a resignation).  
 * all of them need to be static, anyway.
 */

BoardCoordinate from, to;
struct pixrect * pieceIcon, * pieceStencil;
struct pr_pos lastMouseLoc;

/*
 * abort any mouse activity - this really only means aborting piece 
 * animation.
 */
void
KillMouseActivity()
{
    switch (Mouse) {
    case PROMOTING_PAWN:
	UnDoMove();
	break;
    case MOVING_PIECE:
	/* repaint the background */
	pw_rop(Board->gfx_pixwin,
	    lastMouseLoc.x - MOVE_X_OFFSET,
	    lastMouseLoc.y - MOVE_Y_OFFSET,
	    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
	    PIX_SRC, MoveFromPR, 0, 0);
	putPieceBack(&from, pieceStencil, pieceIcon);
	break;
    }
}

/*
 * relay a request for an undo by the opponent.
 *
 * this has the side-effect of aborting any mouse activity that the 
 * user had going at the time.  however, setups will not be interrupted 
 * by undo requests since the (spurious) undo request will be prevented 
 * when the opponent's program detects that he hasn't moved yet.
 */
void
RequestUndo()
{
    Message("Your opponent wants to undo - left OK, other not OK");
    KillMouseActivity();
    Mouse = CONFIRMING;
    confirmState = CONFIRM_UNDO;
}

/*
 * confirm a wish to resign
 */
void
ConfirmResignation()
{

    Message("Sure you want to resign? - left yes, other no");
    Mouse = CONFIRMING;
    confirmState = CONFIRM_RESIGNATION;
}

/*
 * board select() handler 
 */
/*ARGSUSED*/
boardSelected(nullsw, ibits, obits, ebits, timer)
    caddr_t * nullsw;
    int * ibits, * obits, * ebits;
    struct timeval ** timer;
{
    static tickCount = 0;
    static Move move;
    static SetupChange setup;
    struct inputevent ie;
    struct pr_pos newMouseLoc;
    Square * sqp;
    long nbytes;
    BOOL clamped;
    int color, i;
    struct pixrect * pr;

    /*
     * 1-second ticker.  this cannot use SIGALRM, since the RPC
     * package already has dibs on that signal for implementing timeouts.
     * hence, it uses the subwindow select timeout mechanism.
     * the ticker is used to flash the window every 5 seconds if 
     * there is some event the user hasn't seen yet (opening the 
     * tool window turns the flasher off).
     *
     * note - timeouts render the file descriptor masks undefined, 
     * so we clean up and return w/o checking them.
     */
    if ((*timer)->tv_sec == 0L && (*timer)->tv_usec == 0L) {
	if (Flashing) {
	    if (wmgr_iswindowopen(NchessTool->tl_windowfd)) {
		tickCount = 0;
		Flashing = FALSE;
	    } else if (tickCount-- <= 0
	    && (pr = (struct pixrect *) tool_get_attribute(NchessTool, 
		WIN_ICON_IMAGE)) != (struct pixrect *) -1)
	    {
		tickCount = 5;
		pr_rop(pr, 0, 0, pr->pr_size.x, pr->pr_size.x, 
		    PIX_NOT(PIX_SRC), pr, 0, 0);
		tool_set_attributes(NchessTool, WIN_ICON_IMAGE, pr, 0);
		pr_rop(pr, 0, 0, pr->pr_size.x, pr->pr_size.x, 
		    PIX_NOT(PIX_SRC), pr, 0, 0);
		tool_set_attributes(NchessTool, WIN_ICON_IMAGE, pr, 0);
		tool_free_attribute(WIN_ICON_IMAGE, pr);
	    }
	}
	/* reset the timer and the file des. masks */
	(*timer)->tv_sec = 1L;
	(*timer)->tv_usec = 0L;
	* ibits = svc_fds | BoardSWMask 
	    | ChessProcessFDs[BLACK] | ChessProcessFDs[WHITE];
	* obits = * ebits = 0;
	return;
    }
    /* 
     * check for RPC service required 
     */
    if (*ibits & svc_fds) {
	svc_getreq(*ibits & svc_fds);
    } 
    /*
     * check for machine move received
     *
     * note: it is possible in some circumstances to receive a move
     * from a machine player after the game is over (for example, 
     * the opponent of the machine which is on move dies).
     * in that event, we simply throw the move away.
     */
    for (color = BLACK , i = 0  ; i < 2 ; color = WHITE , i++) {
	if ((*ibits & ChessProcessFDs[color]) 
	&& GetMachineMove(&move, color)
	&& ! GameOver) {
	    BOOL updateMsgWindow = TRUE;

	    Flashing = TRUE;
	    DoMove(&move, TRUE);
	    Turn = OTHERCOLOR(Turn);
	    /* 
	     * if we are trying to save a game of machine vs. machine,
	     * hold up sending the move until both machine states 
	     * have been saved.  when subsequently restoring the 
	     * game, this move will be resubmitted.
	     */
	    if (SaveWanted) {
		SaveGame(SaveFileName);
		SaveWanted = FALSE;
		updateMsgWindow = FALSE;
	    }
	    /* 
	     * if the player not moving is a machine, send the move 
	     */
	    if (IsMachine[OTHERCOLOR(color)]) {
		SendMove(&move, OTHERCOLOR(color));
	    /*
	     * else if the player not moving is a human that wants an
	     * undo, back it out
	     */
	    } else if (UndoWanted) {
		MachineUndo(color);
		UndoWanted = FALSE;
		Mouse = IDLE;
	    }
	    if (updateMsgWindow)
		WhoseMoveMessage((char *) 0);
	}
    }
    /*
     * check for board subwindow service required
     */
    if (*ibits & BoardSWMask) {
	if (input_readevent(BoardSW->ts_windowfd, &ie) == -1) {
	    perror("input failed");
	    abort();
	}
	switch (Mouse) {
	/*
	 * locked: ignore all mouse activity
	 */
	case LOCKED:
	    break;
	/* 
	 * we are using the mouse to confirm something 
	 */
	case CONFIRMING:
	    if (win_inputposevent(&ie) 
	    && ie.ie_code >= BUT_FIRST
	    && ie.ie_code <= BUT_LAST) {
		Mouse = IDLE;
		switch(confirmState) {
		case CONFIRM_RESIGNATION:
		    if (ie.ie_code == MS_LEFT) {
			SendResignation(PeerColor);
			Mouse = LOCKED;		/* game is over */
			DoResignation(MyColor);
			Message("Resigned");
		    } else {
			WhoseMoveMessage((char *) 0);
		    }
		    break;
		case CONFIRM_SETUP_END:
		    if (ie.ie_code == MS_LEFT) {
			/* 
			 * if either player is a machine, white always moves
			 * first following a setup (another brain-damaged
			 * attribute of the unix chess program).
			 */
			if (IsMachine[WHITE] || IsMachine[BLACK]) {
			    BOOL legalSetup = TRUE;

			    Turn = WHITE;
			    if (IsMachine[BLACK]) {
				legalSetup = MachineSetup(BLACK);
			    }
			    if (legalSetup && IsMachine[WHITE]) {
				if (legalSetup = MachineSetup(WHITE))
				    MachineFirst(WHITE);
			    }
			    if ( ! legalSetup) {
				Message("Illegal setup - try again");
				Mouse = SETUP;
			    } else {
				/*
				 * if both players are machines, the human part
				 * is over.
				 */
				if (IsMachine[BLACK] && IsMachine[WHITE]) 
				    Mouse = LOCKED;
				/*
				 * else we get to play
				 */
				else 
				    InitialTurn = WHITE;
				WhoseMoveMessage((char *) 0);
				SetupMode = FALSE;
			    }
			/*
			 * else the opponent is a human, and we can specify
			 * who moves first.
			 */
			} else {
			    Message("Left button to move first, other to move second");
			    Mouse = CONFIRMING;
			    confirmState = CONFIRM_WHOSE_TURN;
			    SetupMode = FALSE;
			}
		    } else {
			Message("Setup: left - source, middle - delete, right - end");
			Mouse = SETUP;
		    }
		    break;
		case CONFIRM_WHOSE_TURN:
		    Turn = InitialTurn = (ie.ie_code == MS_LEFT ? 
			MyColor : 
			OTHERCOLOR(MyColor));
		    WhoseMoveMessage((char *) 0);
		    SendEndRestore();
		    break;
		case CONFIRM_UNDO:
		    SendUndoAcknowledgement(ie.ie_code == MS_LEFT);
		    break;
		}
	    }
	    break;
	/*
	 * we are setting up an initial board layout
	 */
	case SETUP:
	    switch (ie.ie_code) {
	    /*
	     * generate and pick up a source piece
	     */
	    case MS_LEFT:
		if (win_inputposevent(&ie)) {
		    newMouseLoc.x = ie.ie_locx;
		    newMouseLoc.y = ie.ie_locy;
		    mapMouseToBoard(&newMouseLoc, &from);
		    /* if this a source square */
		    if (IsSrcPieceAt(&from)) {
			Mouse = MOVING_PIECE;
			sqp = GetSrcSquare(from.x, from.y);
			setup.type = sqp->type;
			setup.color = sqp->color;
			/*
			 * create the first background pixrect,
			 * centered on the selected board square
			 */
			mapBoardToMouse(&from, &lastMouseLoc);
			/* grab the currently displayed image */
			pw_read(MoveFromPR, 
			    0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC,
			    Board->gfx_pixwin,
			    lastMouseLoc.x - MOVE_X_OFFSET,
			    lastMouseLoc.y - MOVE_Y_OFFSET);
			/* repaint the blank square */
			pr_rop(MoveFromPR,
			    MOVE_X_OFFSET,
			    MOVE_Y_OFFSET,
			    SQUARE_WIDTH, SQUARE_HEIGHT,
			    PIX_SRC,
			    ((from.x + from.y) & 0x01) ? 
				&BlackSquarePR :
				&WhiteSquarePR,
			    0, 0);
			/* 
			 * remember the pixrect used to paint the piece 
			 * being moved 
			 */
			pieceIcon = PieceIcons[(int) sqp->type][sqp->color];
			pieceStencil = PieceStencils[(int) sqp->type];
			/* 
			 * if there is a piece at the source square, repaint 
			 * the piece on the background pixrect 
			 */
			sqp = GetSquare(from.x, from.y);
			if (sqp->type != NULLPC) {
			    pr_stencil(MoveFromPR,
				MOVE_X_OFFSET,
				MOVE_Y_OFFSET,
				SQUARE_WIDTH, SQUARE_HEIGHT,
				PIX_SRC, 
				PieceStencils[(int)sqp->type], 0, 0, 
				PieceIcons[(int) sqp->type][sqp->color], 0, 0);
			}
		    }
		}
		break;
	    /*
	     * delete a piece 
	     */
	    case MS_MIDDLE:
		if (win_inputposevent(&ie)) {
		    newMouseLoc.x = ie.ie_locx;
		    newMouseLoc.y = ie.ie_locy;
		    mapMouseToBoard(&newMouseLoc, &from);
		    setup.x = from.x;
		    setup.y = from.y;
		    setup.type = NULLPC;
		    DoSetupChange(&setup);
		    SendSetupChange(&setup, PeerColor);
		}
		break;
	    /*
	     * exit setup
	     */
	    case MS_RIGHT:
		if (win_inputposevent(&ie)) {
		    Message("Sure you want to end setup? left - yes, other - no");
		    Mouse = CONFIRMING;
		    confirmState = CONFIRM_SETUP_END;
		}
		break;
	    }
	    break;
	/*
	 * we are promoting a pawn
	 */
	case PROMOTING_PAWN:
	    switch(ie.ie_code) {
	    /*
	     * select the next pawn morph
	     */
	    case MS_LEFT:
		if (win_inputposevent(&ie))
		    move.newPieceType = PromotePawn(&to);
		break;
	    /*
	     * go for the current morph
	     */
	    case MS_MIDDLE:
		if (win_inputposevent(&ie) && SendMove(&move, PeerColor)) {
		    Turn = OTHERCOLOR(Turn);
		    WhoseMoveMessage((char *) 0);
		    Mouse = IDLE;
		}
		break;
	    /*
	     * we exited the window - back out the pawn move 
	     */
	    case LOC_WINEXIT:
		UnDoMove();
		Mouse = IDLE;
		break;
	    }
	    break;
	/* 
	 * we aren't currently doing anything
	 */
	case IDLE:
	    switch(ie.ie_code) {
	    case MS_LEFT:
		/*
		 * if this a left button press 
		 * and it is our turn
		 * and we aren't waiting for an undo acknowledgement
		 */
		if (win_inputposevent(&ie) && Turn == MyColor && ! UndoWanted) {
		    newMouseLoc.x = ie.ie_locx;
		    newMouseLoc.y = ie.ie_locy;
		    mapMouseToBoard(&newMouseLoc, &from);
		    /* 
		     * if there is one of our pieces on this square 
		     */
		    if (IsOurPieceAt(&from)) {
			Mouse = MOVING_PIECE;
			sqp = GetSquare(from.x, from.y);
			/*
			 * create the first background pixrect,
			 * centered on the selected board square
			 */
			mapBoardToMouse(&from, &lastMouseLoc);
			/* grab the currently displayed image */
			pw_read(MoveFromPR, 
			    0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC,
			    Board->gfx_pixwin,
			    lastMouseLoc.x - MOVE_X_OFFSET,
			    lastMouseLoc.y - MOVE_Y_OFFSET);
			/* repaint the blank square */
			pr_rop(MoveFromPR,
			    MOVE_X_OFFSET,
			    MOVE_Y_OFFSET,
			    SQUARE_WIDTH, SQUARE_HEIGHT,
			    PIX_SRC,
			    ((from.x + from.y) & 0x01) ? 
				&BlackSquarePR :
				&WhiteSquarePR,
			    0, 0);
			/* 
			 * remember the pixrect used to paint the piece 
			 * being moved 
			 */
			pieceIcon = PieceIcons[(int) sqp->type][sqp->color];
			pieceStencil = PieceStencils[(int) sqp->type];
			/*
			 * a bit un-structured, but we are forced to do this
			 * if we want the piece to "jump" to the cursor when
			 * the button is initially depressed ("forced" in the
			 * sense that we are inside a switch statement).
			 */
			goto moveIt;
		    }
		}
		break;
	    }
	    break;
	/*
	 * we are animating a piece 
	 */
	case MOVING_PIECE:
	    switch(ie.ie_code) {
	    case MS_LEFT:
		/*
		 * if we are putting down a piece
		 */
		if (win_inputnegevent(&ie)) {
		    BOOL legal;

		    Mouse = IDLE;
		    newMouseLoc.x = ie.ie_locx;
		    newMouseLoc.y = ie.ie_locy;
		    mapMouseToBoard(&newMouseLoc, &to);
		    legal = TRUE;
		    move.x1 = from.x; move.y1 = from.y;
		    move.x2 = to.x; move.y2 = to.y;
		    if (SetupMode) {
			/* repaint the background */
			pw_rop(Board->gfx_pixwin,
			    lastMouseLoc.x - MOVE_X_OFFSET,
			    lastMouseLoc.y - MOVE_Y_OFFSET,
			    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, MoveFromPR, 0, 0);
			/* put the piece down no matter what */
			setup.x = to.x;
			setup.y = to.y;
			DoSetupChange(&setup);
			SendSetupChange(&setup, PeerColor);
			Mouse = SETUP;
		    } else if ( ! (from.x == to.x && from.y == to.y) 
		    && Turn == MyColor 
		    && (legal = IsMoveLegal(&from, &to))) {
			/* if this is a pawn promotion */
			if (GetSquare(from.x, from.y)->type == PAWN
			&& (to.y == 0 || to.y == 7)) {
			    /* repaint the background */
			    pw_rop(Board->gfx_pixwin,
				lastMouseLoc.x - MOVE_X_OFFSET,
				lastMouseLoc.y - MOVE_Y_OFFSET,
				MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
				PIX_SRC, MoveFromPR, 0, 0);
			    move.newPieceType = (int) QUEEN;
			    DoMove(&move, TRUE);
			    /* if we are playing the brain-damaged unix chess
			     * program, can only promote to queens */
			    if (IsMachine[OTHERCOLOR(MyColor)]) {
				Turn = OTHERCOLOR(Turn);
				SendMove(&move, PeerColor);
				WhoseMoveMessage((char *) 0);
			    /* else need to have the user select the promoted 
			     * piece type */
			    } else {
				Message("Left button: select piece type, middle: send move.");
				Mouse = PROMOTING_PAWN;
			    }
			/* else if we can send the move */
			} else if (SendMove(&move, PeerColor)) {
			    /* repaint the background */
			    pw_rop(Board->gfx_pixwin,
				lastMouseLoc.x - MOVE_X_OFFSET,
				lastMouseLoc.y - MOVE_Y_OFFSET,
				MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
				PIX_SRC, MoveFromPR, 0, 0);
			    DoMove(&move, TRUE);
			    Turn = OTHERCOLOR(Turn);
			    WhoseMoveMessage((char *) 0);
			/* else the peer is dead */
			} else {
			    /* repaint the background */
			    pw_rop(Board->gfx_pixwin,
				lastMouseLoc.x - MOVE_X_OFFSET,
				lastMouseLoc.y - MOVE_Y_OFFSET,
				MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
				PIX_SRC, MoveFromPR, 0, 0);
			    putPieceBack(&from, pieceStencil, pieceIcon);
			}
		    } else {
			/* repaint the background */
			pw_rop(Board->gfx_pixwin,
			    lastMouseLoc.x - MOVE_X_OFFSET,
			    lastMouseLoc.y - MOVE_Y_OFFSET,
			    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, MoveFromPR, 0, 0);
			putPieceBack(&from, pieceStencil, pieceIcon);
			if ( ! legal)
			    Message("Illegal move");
		    }
		}
		break;
	    /*
	     * exited the window - clean it up.
	     */
	    case LOC_WINEXIT:
		/* repaint the background */
		pw_rop(Board->gfx_pixwin,
		    lastMouseLoc.x - MOVE_X_OFFSET,
		    lastMouseLoc.y - MOVE_Y_OFFSET,
		    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
		    PIX_SRC, MoveFromPR, 0, 0);
		if (SetupMode) {
		    Mouse = SETUP;
		} else {
		    putPieceBack(&from, pieceStencil, pieceIcon);
		    Mouse = IDLE;
		}
		break;
	    /* 
	     * animate the piece
	     */
	    case LOC_MOVEWHILEBUTDOWN:
	moveIt:
		/* ignore old motion events */
		ioctl(Board->gfx_windowfd, (int) FIONREAD, (char *) &nbytes);
		if (nbytes/sizeof(struct inputevent) <= 3) {
		    do {
			newMouseLoc.x = ie.ie_locx - SQUARE_WIDTH/2;
			newMouseLoc.y = ie.ie_locy - SQUARE_HEIGHT/2;
			clamped = FALSE;
			/*
			 * clamp motion if necessary
			 */
			if (newMouseLoc.x - lastMouseLoc.x >= MOVE_X_OFFSET) {
			    newMouseLoc.x = lastMouseLoc.x + (MOVE_X_OFFSET-1);
			    clamped = TRUE;
			} else if (newMouseLoc.x - lastMouseLoc.x <= - MOVE_X_OFFSET) {
			    newMouseLoc.x = lastMouseLoc.x - (MOVE_X_OFFSET-1);
			    clamped = TRUE;
			} 
			if (newMouseLoc.y - lastMouseLoc.y >= MOVE_Y_OFFSET) {
			    newMouseLoc.y = lastMouseLoc.y + (MOVE_Y_OFFSET-1);
			    clamped = TRUE;
			} else if (newMouseLoc.y - lastMouseLoc.y <= - MOVE_Y_OFFSET) {
			    newMouseLoc.y = lastMouseLoc.y - (MOVE_Y_OFFSET-1);
			    clamped = TRUE;
			}
			/* grab the new area */
			pw_read(MoveToPR, 
			    0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, Board->gfx_pixwin,
			    newMouseLoc.x - MOVE_X_OFFSET,
			    newMouseLoc.y - MOVE_Y_OFFSET);
			/* paste the old background over the new area */
			pr_rop(MoveToPR,
			    lastMouseLoc.x - newMouseLoc.x,
			    lastMouseLoc.y - newMouseLoc.y,
			    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, MoveFromPR, 0, 0);
			/* save the new background */
			pr_rop(MoveFromPR, 0, 0, MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, MoveToPR, 0, 0);
			/* paint the piece on the new area */
			pr_stencil(MoveToPR, 
			    MOVE_X_OFFSET, MOVE_Y_OFFSET,
			    SQUARE_WIDTH, SQUARE_HEIGHT,
			    PIX_SRC, pieceStencil, 0, 0, pieceIcon, 0, 0);
			/* now paint the new area on the screen */
			pw_rop(Board->gfx_pixwin,
			    newMouseLoc.x - MOVE_X_OFFSET,
			    newMouseLoc.y - MOVE_Y_OFFSET,
			    MOVE_PR_WIDTH, MOVE_PR_HEIGHT,
			    PIX_SRC, MoveToPR, 0, 0);
			lastMouseLoc.x = newMouseLoc.x;
			lastMouseLoc.y = newMouseLoc.y;
		    } while (clamped);
		}
		break;
	    }
	    break;
	}
    }
    * ibits = svc_fds | BoardSWMask | ChessProcessFDs[BLACK] | ChessProcessFDs[WHITE];
    * obits = * ebits = 0;
}

/*
 * initialize the board subwindow
 */
void
InitBoardSW(useRetained, iconDirectory)
    BOOL useRetained;			/* use a retained pixrect */
    char * iconDirectory;		/* custom piece icon directory */
{
    static struct timeval tickValue;
    struct inputmask mask;
    register unsigned int i;
    int height = (SQUARE_HEIGHT-1) * 8 + ((7 * SQUARE_HEIGHT)/4) + 10;

    /*
     * initialize the subwindow
     */
    if ((BoardSW = gfxsw_createtoolsubwindow(NchessTool, "",
	TOOL_SWEXTENDTOEDGE, 
	/* playing surface    +  victim area */
/*	(SQUARE_HEIGHT-1) * 8 + ((7 * SQUARE_HEIGHT)/4) + 10,  */
	height,
	NULL)) == NULL) 
    {
	fprintf(stderr, "Can't create board subwindow\n");
	exit(1);
    }
    Board = (struct gfxsubwindow *) BoardSW->ts_data;
    if (useRetained)
	gfxsw_getretained(Board); 
    BoardSW->ts_io.tio_handlesigwinch = boardSigwinch;
    BoardSW->ts_io.tio_selected = boardSelected;
    input_imnull(&mask);
    win_setinputcodebit(&mask, MS_LEFT);
    win_setinputcodebit(&mask, MS_MIDDLE);
    win_setinputcodebit(&mask, MS_RIGHT);
    win_setinputcodebit(&mask, LOC_MOVEWHILEBUTDOWN);
    win_setinputcodebit(&mask, LOC_WINEXIT);
    mask.im_flags |= IM_NEGEVENT;
    win_setinputmask(BoardSW->ts_windowfd, &mask, NULL, WIN_NULLLINK);
    /* 
     * add the RPC service and chess process file descriptor select
     * masks to this subwindow.
     */
    BoardSW->ts_io.tio_inputmask = svc_fds 
	| (BoardSWMask = 1 << BoardSW->ts_windowfd) 
	| ChessProcessFDs[BLACK] 
	| ChessProcessFDs[WHITE] ;
    /*
     * set the timeout to 1 second 
     */
    tickValue.tv_sec = 1L;
    tickValue.tv_usec = 0L;
    BoardSW->ts_io.tio_timer = &tickValue;
    /*
     * create the white pieces by inverting the black pieces, using custom
     * pieces where appropriate.
     */
    for ( i = 0 ; i < 6 ; i++ ) {
	if (iconDirectory != (char *) 0) {
	    FILE * iconFile, * stencilFile;
	    icon_header_object iconHeader, stencilHeader;
	    char fileName[512], errorMsg[IL_ERRORMSG_SIZE + 2];

	    strcpy(fileName, iconDirectory);
	    strcat(fileName, "/");
	    strcat(fileName, PieceIconFileNames[i]);
	    if ((iconFile = icon_open_header(fileName, errorMsg, 
		&iconHeader)) != (FILE *) 0) 
	    {
		if (iconHeader.width != SQUARE_WIDTH 
		|| iconHeader.height != SQUARE_HEIGHT
		|| iconHeader.depth != 1) {
		    fprintf(stderr, "warning: bogus icon (ignored): %s\n", fileName);
		} else {
		    strcpy(fileName, iconDirectory);
		    strcat(fileName, "/");
		    strcat(fileName, PieceStencilFileNames[i]);
		    if ((stencilFile = icon_open_header(fileName, errorMsg, 
			&stencilHeader)) != (FILE *) 0) 
		    {
			if (stencilHeader.width != SQUARE_WIDTH 
			|| stencilHeader.height != SQUARE_HEIGHT
			|| stencilHeader.depth != 1) {
			    fprintf(stderr, "warning: bogus icon (ignored): %s\n", fileName);
			} else {
			    icon_read_pr(iconFile, &iconHeader, PieceIcons[i][BLACK]);
			    icon_read_pr(stencilFile, &stencilHeader, PieceStencils[i]);
			}
			fclose(stencilFile);
		    }
		}
		fclose(iconFile);
	    }
	}
	pr_rop(PieceIcons[i][WHITE], 0, 0, SQUARE_WIDTH, SQUARE_HEIGHT,
	    PIX_NOT(PIX_SRC),
	    PieceIcons[i][BLACK], 0, 0);
    }
    /* 
     * create the pixrects used for piece animation and victim drawing
     */
    if ((MoveFromPR = mem_create(MOVE_PR_WIDTH, MOVE_PR_HEIGHT, 1)) == (struct pixrect *) 0
    || (MoveToPR = mem_create(MOVE_PR_WIDTH, MOVE_PR_HEIGHT, 1)) == (struct pixrect *) 0
    || (VictimPR = mem_create(SQUARE_WIDTH, SQUARE_HEIGHT, 1)) == (struct pixrect *) 0) {
	fprintf(stderr, "can't create the animation pixrects\n");
	exit(1);
    }
}

/*
 * draw a square, including the piece (if any)
 */
void
DrawSquare(x, y, sqp)
    int x, y;
    register Square * sqp;
{
    BoardCoordinate bloc;
    struct pr_pos mloc;

    bloc.x = x; bloc.y = y;
    mapBoardToMouse(&bloc, &mloc);
    /* paint the blank square */
    pw_rop(Board->gfx_pixwin, 
	mloc.x, mloc.y,
	SQUARE_WIDTH, SQUARE_HEIGHT,
	PIX_SRC, 
	(((x + y) & 0x01) ? &BlackSquarePR : &WhiteSquarePR), 
	0, 0);
    /* paint the piece, if there is one */
    if (sqp->type != NULLPC) {
	pw_stencil(Board->gfx_pixwin, mloc.x, mloc.y,
	    SQUARE_WIDTH, SQUARE_HEIGHT,
	    PIX_SRC,
	    PieceStencils[(int) sqp->type], 0, 0,
	    PieceIcons[(int) sqp->type][sqp->color], 0, 0);
    }
}

/*
 * draw the playing surface and victim area
 */
void
DrawBoard()
{
    register int x, y;
    register Square * sqp;

    /* clear the board area */
    pw_rop(Board->gfx_pixwin,
	0, 0, Board->gfx_rect.r_width, Board->gfx_rect.r_height,
	PIX_CLR, (struct pixrect *) 0, 0, 0);
    /* draw the playing area */
    for (x = 0 ; x < 8 ; x++) {
	for (y = 0 ; y < 8 ; y++) {
	    sqp = GetSquare(x, y);
	    DrawSquare(x, y, sqp);
	}
    }
    /* draw the victims */
    drawVictims();
}

/*
 * your basic victim 
 */
typedef struct {
    PieceType type;			/* victim type */
    BOOL active;			/* victim slot state */
    int count;				/* victim overflow count */
} Victim;

#define	NUM_VICTIM_SLOTS 8

Victim victims[2 /* pawns(1) vs. pieces(0) */][2 /* color */ ][NUM_VICTIM_SLOTS /* victim slot # */];

/*
 * map the victim specified by the triple 
 * [ isPawn { 0, 1 }, color { BLACK, WHITE }, slot { 0 .. NUM_VICTIM_SLOTS-1 } ]
 * to a mouse coordinate (relative to the board subwindow)
 */
void
mapVictimToMouse(isPawn, color, slot, mlocp)
    BOOL isPawn;
    int color, slot;
    struct pr_pos * mlocp;
{
    mlocp->x = slot * (3 * SQUARE_WIDTH/4) + color * (3 * SQUARE_WIDTH/8);
    mlocp->y = 8 * (SQUARE_HEIGHT-1) + isPawn * (SQUARE_HEIGHT/2) + color * (SQUARE_HEIGHT/4) + 5;
}

/* 
 * draw the victims
 */
drawVictims()
{
    register int i, j, k;
    register Victim * victim;
    struct pr_pos victimOrigin;

    for (i = 0 ; i < 2 ; i++) {
	for (j = 0 ; j < 2 ; j++) {
	    for (k = 0 ; k < NUM_VICTIM_SLOTS ; k++) {
		victim = &victims[i][j][k];
		if (victim->active) {
		    mapVictimToMouse(i, j, k, &victimOrigin);
		    pw_stencil(Board->gfx_pixwin, 
			victimOrigin.x, victimOrigin.y,
			SQUARE_WIDTH, SQUARE_HEIGHT,
			PIX_SRC,
			PieceStencils[(int) victim->type], 0, 0,
			PieceIcons[(int) victim->type][j], 0, 0);
		}
	    }
	}
    }
}

/* 
 * add a piece to the set of victims 
 */
void
AddVictim(type, color, drawIt) 
    PieceType type;
    int color;
    BOOL drawIt;
{
    register int i, j, k;
    int empty, lastMatch, extras;
    register Victim * victim;
    BOOL isPawn = (type == PAWN);
    struct pr_pos victimOrigin, othersOrigin;

    /* 
     * look for the first empty slot and the last slot which 
     * contains a piece of the same type
     */
    for (lastMatch = empty = -1 , i = 0 ; i < NUM_VICTIM_SLOTS ; i++) {
	victim = &victims[isPawn][color][i];
	if (empty < 0 && ! victim->active)
	    empty = i;
	if (victim->active && victim->type == type)
	    lastMatch = i;
    }
    /*
     * if there were no empty slots 
     */
    if (empty == -1) {
	/* 
	 * if there was one or more pieces of the same type, update
	 * the last instance's overflow count (includes coalescing
	 * all overflows of that type at the last instance)
	 */
	if (lastMatch >= 0) {
	    for (extras = i = 0 ; i < lastMatch ; i++) {
		victim = &victims[isPawn][color][i];
		if (victim->active && victim->type == type && victim->count > 1) {
		    extras += victim->count - 1;
		    victim->count = 1;
		}
	    }
	    victims[isPawn][color][lastMatch].count += extras;
	}
    /*
     * else install the victim in the empty slot 
     */
    } else {
	victim = &victims[isPawn][color][empty];
	victim->type = type;
	victim->active = TRUE;
	victim->count = 1;
	if (drawIt) {
	    /*
	     * re-draw all the pieces in the victim's slot 
	     */
	    mapVictimToMouse(isPawn, color, empty, &victimOrigin);
	    pr_rop(VictimPR, 0, 0, SQUARE_WIDTH, SQUARE_HEIGHT,
		PIX_CLR, (struct pixrect *) 0, 0, 0);
	    for (i = 0 ; i < 2 ; i++) {
		for (j = 0 ; j < 2 ; j++) {
		    for (k = 0 ; k < NUM_VICTIM_SLOTS ; k++) {
			victim = &victims[i][j][k];
			if (victim->active) {
			    mapVictimToMouse(i, j, k, &othersOrigin);
			    pr_stencil(VictimPR,
				othersOrigin.x - victimOrigin.x,
				othersOrigin.y - victimOrigin.y,
				SQUARE_WIDTH, SQUARE_HEIGHT,
				PIX_SRC,
				PieceStencils[(int) victim->type], 0, 0,
				PieceIcons[(int) victim->type][j], 0, 0);
			}
		    }
		}
	    }
	    /*
	     * now re-draw the slot 
	     */
	    pw_rop(Board->gfx_pixwin,
		victimOrigin.x, victimOrigin.y, 
		SQUARE_WIDTH, SQUARE_HEIGHT,
		PIX_SRC, VictimPR, 0, 0);
	}
    }
}

/*
 * reincarnate a victim (via an undo)
 */
void
DeleteVictim(type, color)
    PieceType type;
    int color;
{
    register int i, j, k;
    int lastMatch, extras;
    register Victim * victim;
    BOOL isPawn = (type == PAWN);
    struct pr_pos victimOrigin, othersOrigin;

    /* 
     * look for the last slot which contains a piece of this type
     */
    for (lastMatch = -1 , i = 0 ; i < NUM_VICTIM_SLOTS ; i++) {
	victim = &victims[isPawn][color][i];
	if (victim->active && victim->type == type)
	    lastMatch = i;
    }
    /*
     * if there were no matches, don't do anything
     */
    if (lastMatch == -1) {
	/* do nothing */
    /*
     * else if the last match slot contains overflows, simply 
     * decrement the overflow count
     */
    } else if ((victim = &victims[isPawn][color][lastMatch])->count > 1) {
	victim->count--;
    /*
     * else zero out the slot and re-draw it as empty
     */
    } else {
	victim->active = FALSE;
	/*
	 * re-draw all the remaining pieces in the victim's slot 
	 */
	mapVictimToMouse(isPawn, color, lastMatch, &victimOrigin);
	pr_rop(VictimPR, 0, 0, SQUARE_WIDTH, SQUARE_HEIGHT,
	    PIX_CLR, (struct pixrect *) 0, 0, 0);
	for (i = 0 ; i < 2 ; i++) {
	    for (j = 0 ; j < 2 ; j++) {
		for (k = 0 ; k < NUM_VICTIM_SLOTS ; k++) {
		    victim = &victims[i][j][k];
		    if (victim->active) {
			mapVictimToMouse(i, j, k, &othersOrigin);
			pr_stencil(VictimPR,
			    othersOrigin.x - victimOrigin.x,
			    othersOrigin.y - victimOrigin.y,
			    SQUARE_WIDTH, SQUARE_HEIGHT,
			    PIX_SRC,
			    PieceStencils[(int) victim->type], 0, 0,
			    PieceIcons[(int) victim->type][j], 0, 0);
		    }
		}
	    }
	}
	/*
	 * now re-draw the slot 
	 */
	pw_rop(Board->gfx_pixwin,
	    victimOrigin.x, victimOrigin.y, 
	    SQUARE_WIDTH, SQUARE_HEIGHT,
	    PIX_SRC, VictimPR, 0, 0);
    }
}