Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!mnetor!seismo!lll-lcc!mordor!sri-spam!sri-unix!hplabs!pesnta!amd!intelca!mipos3!omepd!psu-cs!reed!tektronix!uw-beaver!fluke!toma
From: toma@tc.fluke.COM (Tom Anderson)
Newsgroups: net.sources.games
Subject: network chess (5 of 5)
Message-ID: <260@spock.tc.fluke.COM>
Date: Thu, 8-Jan-87 18:52:36 EST
Article-I.D.: spock.260
Posted: Thu Jan  8 18:52:36 1987
Date-Received: Thu, 15-Jan-87 19:33:36 EST
Organization: John Fluke Mfg. Co., Inc., Everett, WA
Lines: 1831


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by spock!toma on Thu Jan  8 14:34:19 PST 1987
# Contents:  boardsw.c msgsw.c talksw.c chessprocess.c
 
echo x - boardsw.c
sed 's/^@//' > "boardsw.c" <<'@//E*O*F boardsw.c//'
/*
 * handle the board subwindow (as well as fielding RPC and chess game
 * process selects)
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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);
    }
}

@//E*O*F boardsw.c//
chmod u=r,g=r,o=r boardsw.c
 
echo x - msgsw.c
sed 's/^@//' > "msgsw.c" <<'@//E*O*F msgsw.c//'
/*
 * message subwindow handling
 */

#include 
#include 
#include 
#include 
#include 

#include "nchess.h"

struct toolsw * MessageSW;
Panel MessagePanel;

Panel_item MessageItem;

/*
 * set up the message subwindow
 */
void
InitMsgSW()
{
    if ((MessageSW = panel_create(NchessTool, 0)) == NULL) {
	fprintf(stderr, "Can't create message panel\n");
	exit(1);
    }
    MessagePanel = MessageSW->ts_data;
    /* create the message panel item */
    MessageItem = panel_create_item(MessagePanel, PANEL_MESSAGE,
	PANEL_LABEL_STRING, 
	    RestoringGame ? "Please wait..." :
	    SetupMode ? "Setup: left - source, middle - delete, right - end" :
	    IsMachine[Turn] || Turn != MyColor ? 
		(Turn == WHITE ?
		    "Waiting for white to play..." : 
		    "Waiting for black to play...") :
		"Your move" ,
	PANEL_SHOW_ITEM, TRUE,
	0);
    panel_fit_height(MessagePanel); 
}

void 
Message(cp)
    char * cp;
{
    panel_set(MessageItem,
	PANEL_LABEL_STRING, cp,
	PANEL_SHOW_ITEM, TRUE,
	0);
}

/*
 * write the standard "your move", "waiting ..." message
 * pre-pended with the passed string (if non-null)
 */
void
WhoseMoveMessage(cp)
    char * cp;
{
    char c1[256], c2[256];

    if (GameOver) 
	strcpy(c2, "Game over");
    else if (IsMachine[Turn] || Turn != MyColor)
	strcpy(c2, Turn == WHITE ? 
	    "Waiting for white to play..." :
	    "Waiting for black to play...");
    else if (InCheck())
	strcpy(c2, "Your move (check)");
    else
	strcpy(c2, "Your move");

    if (cp != (char *) 0) {
	c2[0] = tolower(c2[0]);
	strcpy(c1, cp);
	strcat(c1, " - ");
	Message(strcat(c1, c2));
    } else 
	Message(c2);
}

@//E*O*F msgsw.c//
chmod u=r,g=r,o=r msgsw.c
 
echo x - talksw.c
sed 's/^@//' > "talksw.c" <<'@//E*O*F talksw.c//'
/*
 * talk subwindow handling
 */

#include 
#include 
#include 
#include 

#include "nchess.h"

#define	MAX_SEND_LENGTH		60

struct toolsw * TalkSW;
Panel TalkPanel;

Panel_item SendItem;
Panel_item RecvItem;

/*ARGSUSED*/
sendProc(item, event)
    Panel_item item;
    struct inputevent *event;
{
    char c[MAX_SEND_LENGTH+1];

    strcpy(c, (char *) panel_get_value(SendItem));
    SendTalkMsg(c);
    /* set the command text to nil */
    panel_set_value(SendItem, "");
}

/*
 * set up the talk send and receive subwindows 
 * (if we are playing against the machine, leave them out)
 */
void
InitTalkSW()
{
    if (IsMachine[PeerColor])
	return;
    if ((TalkSW = panel_create(NchessTool, 0)) == NULL) {
	fprintf(stderr, "Can't create talk subwindows\n");
	exit(1);
    }
    TalkPanel = TalkSW->ts_data;
    RecvItem = panel_create_item(TalkPanel, PANEL_MESSAGE,
	PANEL_LABEL_STRING, "Recv:",
	PANEL_SHOW_ITEM, TRUE,
	0);
    SendItem = panel_create_item(TalkPanel, PANEL_TEXT,
	PANEL_LABEL_STRING, "Send:",
	PANEL_NOTIFY_STRING, "\n\r",
	PANEL_NOTIFY_LEVEL, PANEL_SPECIFIED,
	PANEL_NOTIFY_PROC, sendProc,
	PANEL_VALUE_STORED_LENGTH, MAX_SEND_LENGTH,
	0);
    panel_fit_height(TalkPanel); 
}

/*
 * receive a pithy banality from the opponent
 */
void
RecvTalkMsg(cp)
    char *cp;
{
    char ncp[128];

    strcpy(ncp, "Recv: ");
    panel_set(RecvItem, 
	PANEL_LABEL_STRING, strcat(ncp, cp),
	0);
}
@//E*O*F talksw.c//
chmod u=r,g=r,o=r talksw.c
 
echo x - chessprocess.c
sed 's/^@//' > "chessprocess.c" <<'@//E*O*F chessprocess.c//'
/*
 * manage one or two chess game processes
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "nchess.h"

extern char * gets();

int ChessPid[2],
    ChessProcessFDs[2];
FILE * ReadChessProcess[2], * WriteChessProcess[2];
BOOL SigChildInterceptOn = FALSE;
BOOL MachineDebug = FALSE;
char * colorStrings[] = { "black", "white" };

/*
 * intercept SIGCHLD
 *
 * print a message for the first chess process that dies and abort the 
 * game.  (note: any change in child status is interpreted as death).
 */
handleSigChild()
{
    int pid;
    union wait status;

    while(1) {
	pid = wait3(&status, WNOHANG, (struct rusage *) 0);
	if (pid <= 0)
	    return;
	if ( ! GameOver && (pid == ChessPid[BLACK] || pid == ChessPid[WHITE])) {
	    Message (pid == ChessPid[WHITE] ?
		"White's process died" :
		"Black's process died" );
	    KillMouseActivity();
	    GameOver = TRUE;
	    Mouse = LOCKED;
	}
    }
}

/*
 * have a chess process make the first move.
 *
 * note: with the wonderful unix chess program, "first" also implies
 * playing the white pieces.  nonetheless, color is an argument to this
 * function in case we ever find out how to get around this problem.
 */
void
MachineFirst(color)
    int color;
{
    fputs("first\n", WriteChessProcess[color]);
    fflush(WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s: first\n", colorStrings[color]);
}

/*
 * open a pipe to a chess game process
 *
 * startIt indicates whether the process should be sent the "first"
 * command.
 */
void
InitChessProcess(cp, color, startIt)
    char ** cp;
    int color;
    BOOL startIt;
{
    int sv[2];
    char c[128];

    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
	fputs("socketpair failed\n", stderr);
	exit(1);
    }
    if ((ChessPid[color] = fork()) == 0) {
	dup2(sv[0], 0);
	dup2(sv[0], 1); 
	chdir("/tmp");		/* so that "chess.out" can always be written */
	execvp(cp[0], cp);
	puts("exec");
	fflush(stdout);
	_exit(1);
    }
/*    dup2(sv[1], 0);
    dup2(sv[1], 1); */
    /* want to intercept SIGCHLD */
    if ( ! SigChildInterceptOn) {
	SigChildInterceptOn = TRUE;
	signal(SIGCHLD, handleSigChild);
    }
    ChessProcessFDs[color] = (1 << sv[1]);
    ReadChessProcess[color] = fdopen(sv[1], "r");
    WriteChessProcess[color] = fdopen(sv[1], "w");
    setbuf(ReadChessProcess[color], (char *) 0);
    /* 
     * eat the "Chess" prompt 
     * note: if the child's exec failed,
     * catch the "exec" prompt and give up
     */
    fgets(c, sizeof(c), ReadChessProcess[color]);
    if (strcmp(c, "exec\n") == 0) {
	KillChessProcesses();
	fprintf(stderr, "exec of %s failed\n", cp[0]);
	exit(1);
    }
    /* set algebraic notation mode */
    fputs("alg\n", WriteChessProcess[color]);
    fflush(WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s: alg\n", colorStrings[color]);
    if (startIt)
	MachineFirst(color);
}
 

/*
 * reap one or more chess process
 */
void
ReapChessProcesses()
{
    union wait status;

    while (wait3(&status, WNOHANG, (struct rusage *) 0) >= 0)
	;
}

/*
 * kill any and all chess processes
 */
void
KillChessProcesses()
{
    signal(SIGCHLD, SIG_IGN);
    if (ChessPid[BLACK])
	kill(ChessPid[BLACK], SIGKILL);
    if (ChessPid[WHITE])
	kill(ChessPid[WHITE], SIGKILL);
    ReapChessProcesses();
}

/*
 * get a move from a chess process 
 * 
 * return 1 if a move was successfully obtained, 0 if not.
 */
int
GetMachineMove(move, color)
    Move * move;
    int color;
{
    char c[256], c2[256];
    char file1, file2;
    int rank1, rank2;

    if (fgets(c, sizeof(c), ReadChessProcess[color]) == (char *) 0) 
	return(0);
    if (MachineDebug)
	fprintf(stderr, "rec'd from %s: %s", colorStrings[color], c);
    /* look for special announcements */
    if (strcmp(c, "Forced mate\n") == 0) {
	Message(color == WHITE ? 
	    "White announces forced mate" :
	    "Black announces forced mate");
	return(0);
    } 
    if (strcmp(c, "Resign\n") == 0) {
	Message(color == WHITE ? "White resigns" : "Black resigns");
	DoResignation(color);
	Mouse = LOCKED;
	return(0);
    } 
    if (strcmp(c, "Illegal move\n") == 0) {
	Message("Internal botch - illegal move detected");
	return(0);
    } 
    if (strncmp(c, "done", 4) == 0) {
	return(0);
    }
    if (strncmp(c, "White wins", 10) == 0
    || strncmp(c, "Black wins", 10) == 0) {
	c[10] = '\0';
	Message(c);
	GameOver = TRUE;
	Mouse = LOCKED;
	return(0);
    }
    /* e.g., 1. d2d4 */
    if (sscanf(c, "%*d. %c%d%c%d", &file1, &rank1, &file2, &rank2) == 4
    /* e.g., 1. ... d2d4 */
    || sscanf(c, "%*d. ... %c%d%c%d", &file1, &rank1, &file2, &rank2) == 4) {
	move->x1 = file1 - (isupper(file1) ? 'A' : 'a');
	move->y1 = 8 - rank1;
	move->x2 = file2 - (isupper(file2) ? 'A' : 'a');
	move->y2 = 8 - rank2;
	/* TBD: possible non-queen pawn promotion 
	 * (the existing chess program doesn't implement this) */
	move->newPieceType = (int) QUEEN;
	return(1);
    } else {
	strcpy(c2, color == WHITE ? "White announces: " : "Black announces: ");
	Message(strncat(c2, c, sizeof(c2) - strlen(c2) - 2));
	if (strncmp(c, "Draw", 4) == 0) {
	    GameOver = TRUE;
	    Mouse = LOCKED;
	}
	return(0);
    }
}

/*
 * send a move to a chess process
 *
 * note: if the process responds with a move too quickly, we won't 
 * get another select() trigger to get its move.  thus, we need to
 * check for the availability of a move when the echo is received.
 */
void
SendMachineMove(move, color)
    Move * move;
    int color;
{
    char c[128];

    if (move->x1 != move->x2
    && GetSquare(move->x1, move->y1)->type == PAWN
    && GetSquare(move->x2, move->y2)->type == NULLPC) {
	/* re-encode en passant captures as horizontal moves */
	fprintf(WriteChessProcess[color], "%c%d%c%d\n", 
	    move->x1 + 'a', 8 - move->y1, 
	    move->x2 + 'a', 8 - move->y1);
	if (MachineDebug)
	    fprintf(stderr, "sent move to %s: %c%d%c%d\n", 
		colorStrings[color],
		move->x1 + 'a', 8 - move->y1, 
		move->x2 + 'a', 8 - move->y1);
    } else {
	fprintf(WriteChessProcess[color], "%c%d%c%d\n", 
	    move->x1 + 'a', 8 - move->y1, 
	    move->x2 + 'a', 8 - move->y2);
	if (MachineDebug)
	    fprintf(stderr, "sent move to %s: %c%d%c%d\n", 
		colorStrings[color],
		move->x1 + 'a', 8 - move->y1, 
		move->x2 + 'a', 8 - move->y2);
    }
    fflush(WriteChessProcess[color]);
    fgets(c, sizeof(c), ReadChessProcess[color]); /* eat the move echo */
    if (MachineDebug)
	fprintf(stderr, "rec'd from %s: %s", colorStrings[color], c);
}

/*
 * undo the last machine move and our move 
 */
void
MachineUndo(color)
{
    fputs("remove\n", WriteChessProcess[color]);
    fflush(WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s: remove\n", colorStrings[color]);
    UnDoMove();
    UnDoMove();
}

/*
 * set up the board 
 * (as noted above: with the existing unix chess program, there is no 
 * apparent way (short of deciphering the chess.out format, which is 
 * totally un-fun) to inform the chess program whose turn it is and 
 * which color it is supposed to play - it assumes that white always 
 * is the first to move.  
 */

char whitePieceChars[] = { 'p', 'n', 'b', 'r', 'q', 'k', ' ' };
char blackPieceChars[] = { 'P', 'N', 'B', 'R', 'Q', 'K', ' ' };

/*
 * set up a board state against the machine.
 * returns TRUE if successful, FALSE otherwise
 */
BOOL
MachineSetup(color)
    int color;
{
    register int x, y;
    Square * sqp;
    char c[128];

    fputs("setup\n", WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s:\nsetup\n", colorStrings[color]);
    for (y = 0 ; y < 8 ; y++) {
	for (x = 0 ; x < 8 ; x++) {
	    sqp = GetSquare(x, y);
	    putc(sqp->color == WHITE ? 
		whitePieceChars[(int) sqp->type] :
		blackPieceChars[(int) sqp->type], 
		WriteChessProcess[color]);
	    if (MachineDebug)
		fputc(sqp->color == WHITE ? 
		    whitePieceChars[(int) sqp->type] :
		    blackPieceChars[(int) sqp->type], 
		    stderr);
	}
	putc('\n', WriteChessProcess[color]);
	if (MachineDebug)
	    fputc('\n', stderr);
    }
    fflush(WriteChessProcess[color]);
    fgets(c, sizeof(c), ReadChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "rec'd from %s: %s", colorStrings[color], c);
    return(strcmp(c, "Setup successful\n") == 0);
}

/*
 * have a chess process save the game as "/tmp/chess.out".
 * return the size of chess.out.
 */
int
MachineSave(color)
    int color;
{
    FILE * saveFile;
    struct stat saveFileStatus;
    int saveFileSize;
    int retryCount = 0;

    fputs("save\n", WriteChessProcess[color]);
    fflush(WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s: save\n", colorStrings[color]);
    sleep((unsigned) 1);
    /* wait for the chess process to create chess.out */
    do {
	if ((saveFile = fopen("/tmp/chess.out", "r")) == (FILE *) 0) 
	    sleep((unsigned) 2); 
    } while(saveFile == (FILE *) 0 && ++retryCount < 10);
    if (saveFile == (FILE *) 0) {
	Message("Can't open chess.out!");
	return(-1);
    }
    saveFileStatus.st_size = -1;
    /* wait until chess.out stops growing */
    do {
	sleep((unsigned) 1);
	saveFileSize = saveFileStatus.st_size;
	fstat(fileno(saveFile), &saveFileStatus);
    } while (saveFileSize != saveFileStatus.st_size);
    fclose(saveFile);
    return(saveFileSize);
}

/*
 * restore the game 
 */
void
MachineRestore(color)
    int color;
{
    fputs("restore\n", WriteChessProcess[color]);
    fflush(WriteChessProcess[color]);
    if (MachineDebug)
	fprintf(stderr, "sent to %s: restore\n", colorStrings[color]);
}

@//E*O*F chessprocess.c//
chmod u=r,g=r,o=r chessprocess.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
    1249    4199   34033 boardsw.c
      83     217    1681 msgsw.c
      74     154    1462 talksw.c
     382    1283    9700 chessprocess.c
    1788    5853   46876 total
!!!
wc  boardsw.c msgsw.c talksw.c chessprocess.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0