/* routines to draw the cursor on the screen */

#include "X.h"
#include "ega.h"
#include "cursorst.h"
#include "servermd.h"
#include "../common/ibmmouse.h"
#include <dos.h>

/* Ok, this is cheating, but I know in this product there will always
 * be only one screen (If someone puts more then one screen on a machine
 * we'll fix this later).  Therefore, I'm going to keep one near structure
 * for the cursor stuff.
 */
struct egaCursor _near egaCursor;
Bool near mouse_moved;
Bool near direct_mouse = FALSE;

extern int near mouse_X, near mouse_Y;
static void _near egaSaveUnderCursor(void);
static void _fastcall _near egaPutUpCursor(char *);
static void _near egaRestoreUnderCursor(void);

/* Assume the cursor is not already up.  Display it at x, y.
 * If where is true, copy the cursor onto where
 */
static void _fastcall _near
egaPutUpCursor(where)
char *where;
{
    int startx, starty;		/* top-left of cursor, relative to screen */
    int endx, endy;		/* bottom-right of cursor */
    int x;			/* relative to cursor bitmap */
    u_char *pSource, *pMask;	/* pointers to bitmaps */
    _segment		base;
    unsigned char _based(base)	*cp;
    int widthDst;
    int widthSrc = PixmapBytePad(egaCursor.pBits->width, 1);
#ifdef EGA_HIRES
    Bool overlap = FALSE;	/* TRUE is cursor overlaps both pages */
#endif


    if (where) {
	base = (u_short)((u_long)where >> 16);
	cp = (u_char _based(base) *)(u_short)(u_long)where;
	widthDst = PixmapBytePad(egaCursor.saved.x2 - egaCursor.saved.x1, 1);
    } else {
	widthDst = egapriv.devKind;
	base = egapriv.fb;
	cp = 0;
	egaCursor.isUp = 1;
    }


    /* forgive me if I use x when I mean y here, but this saves a variable. */
    starty = egaCursor.pos_y - egaCursor.pBits->yhot;
    endy = starty + egaCursor.pBits->height;
    if (endy > egaCursor.screen_height)
	endy = egaCursor.screen_height;
    if (starty < 0) {
	x = -starty;
	starty = 0;
	pSource = &egaCursor.pBits->source[x * widthSrc];
	pMask = &egaCursor.pBits->mask[x * widthSrc];
    } else {
	pSource = egaCursor.pBits->source;
	pMask = egaCursor.pBits->mask;
    }

    startx = egaCursor.pos_x - egaCursor.pBits->xhot;
    endx = startx + egaCursor.pBits->width;
    if (endx > egaCursor.screen_width)
	endx = egaCursor.screen_width;
    if (startx < 0) {
	x = -startx;
	startx = 0;
	pMask += x >> 3;
	pSource += x >> 3;
	x &= 7;
    } else
	x = 0;
    if (startx >= endx)
	return;

/* printf("PutUp: %d,%d -> %d,%d\n", startx, starty, endx, endy); */
    endx -= startx;
    endy -= starty;
    if (where) {
	starty -= egaCursor.saved.y1;
	startx -= egaCursor.saved.x1;
    }

    cp += startx >> 3;
    cp += starty * widthDst;
#ifdef EGA_HIRES
    if (egapriv.hires && where == 0) {
	if (starty >= HIRES_LPP) {
	    starty -= HIRES_LPP;
	    starty = 0;
	    if (!egapriv.curpage)
		(*vga_conf.page)(egapriv.curpage = 1);
	} else {
	    if (egapriv.curpage)
		(*vga_conf.page)(egapriv.curpage = 0);
	    if (starty + endy > HIRES_LPP) {
		/* put extra in starty */
		starty = starty + endy - HIRES_LPP;
		endy -= starty;
	    } else
		starty = 0;
	}
    } else
	starty = 0;
#endif
    startx &= 7;
/* printf("%d ", startx); */
#ifdef EGA_HIRES
more:
#endif
    while (endy-- > 0) {
	andInvertedbits(pMask, (char _far *)cp, x, startx, endx);
	orInvertedbits(pSource, (char _far *)cp, x, startx, endx);
	cp += widthDst;
	pSource += widthSrc;
	pMask += widthSrc;
    }
#ifdef EGA_HIRES
    if (starty) {
	(*vga_conf.page)(egapriv.curpage = 1);
	endy = starty;
	starty = 0;
	goto more;
    }
#endif
}

/* I'm going to do a no-no and modify the cursor passed in.  All this
 * will do is zero those bits where mask is 0 so I can just OR the source
 * with the screen (above).
 */
Bool
mouseRealizeCursor(pScr, pCur)
ScreenPtr pScr;
CursorPtr pCur;
{
    u_char *pSource, *pMask;	/* pointers to bitmaps */
    int width, height;

    pSource = pCur->bits->source;
    pMask = pCur->bits->mask;
    height = pCur->bits->height;
    width = PixmapBytePad(pCur->bits->width, 1);
    height *= width;
    while (height--)
	*pSource++ |= ~*pMask++;
    return TRUE;
}

Bool
mouseUnrealizeCursor(pScr, pCur)
ScreenPtr pScr;
CursorPtr pCur;
{
    return TRUE;
}

Bool
mouseDisplayCursor(pScr, pCur)
ScreenPtr pScr;
CursorPtr pCur;
{
    egaCursor.pScreen = pScr;
    egaCursor.screen_width = pScr->width;
    egaCursor.screen_height = pScr->height;

    if (egaCursor.isUp && egaCursor.pBits != pCur->bits) {
	egaRestoreUnderCursor();	/* remove old cursor */
    }
    egaCursor.pBits = pCur->bits;
    return TRUE;
}

extern Bool Must_have_memory;

static void _near
egaSaveUnderCursor()
{
    int start, end, nlines;
    _segment		base;
    unsigned char _based(base)	*cp;
    char *dp;
    int widthDst = egapriv.devKind;

    if (!egaCursor.pScreen || !egaCursor.pBits)
	return;
    base = egapriv.fb;
    cp = 0;

    /* First, compute region to be saved. */
    /* For the X direction, use some slop and round to the nearest byte. */
    start = egaCursor.pos_x - egaCursor.pBits->xhot;
    end = start + egaCursor.pBits->width + 15;
    start -= 7;
    if (start < 0)
	start = 0;
    egaCursor.saved.x1 = start & ~7;
    end &= ~7;
    if (end < 0)
	end = 0;
    else if (end > egaCursor.screen_width)
	end = egaCursor.screen_width;
    egaCursor.saved.x2 = end;
    if (egaCursor.saved.x1 >= egaCursor.saved.x2) {
	/* can't save */
	egaCursor.saved.x1 = 0;
	egaCursor.saved.x2 = 0;
	egaCursor.saved.y1 = 0;
	egaCursor.saved.y2 = 0;
	egaCursor.validSaved = 0;
	return;
    }

    /* for the Y direction, take a slop of 8 top and 8 bottom. */
    start = egaCursor.pos_y - egaCursor.pBits->yhot - 8;
    end = start + egaCursor.pBits->height + 16;
    if (start < 0)
	start = 0;
    egaCursor.saved.y1 = start;
    if (end < 0)
	end = 0;
    else if (end > egaCursor.screen_height)
	end = egaCursor.screen_height;
    egaCursor.saved.y2 = end;
    if (egaCursor.saved.y1 >= egaCursor.saved.y2) {
	/* can't save */
	egaCursor.saved.x1 = 0;
	egaCursor.saved.x2 = 0;
	egaCursor.saved.y1 = 0;
	egaCursor.saved.y2 = 0;
	egaCursor.validSaved = 0;
	return;
    }

    /* compute the number of bytes needed */
    start = ((egaCursor.saved.x2 - egaCursor.saved.x1) >> 3) *
	    (egaCursor.saved.y2 - egaCursor.saved.y1);
    Must_have_memory = TRUE;
    if (!egaCursor.pSaved) {
	egaCursor.pSaved = (char *)xalloc(start);
	egaCursor.save_size = start;
    } else if (egaCursor.save_size < start) {
	egaCursor.pSaved = (char *)xrealloc(egaCursor.pSaved, start);
	egaCursor.save_size = start;
    }
    Must_have_memory = FALSE;

    dp = egaCursor.pSaved;
    cp += egaCursor.saved.x1 >> 3;
    cp += egaCursor.saved.y1 * widthDst;
    end = (egaCursor.saved.x2 - egaCursor.saved.x1) >> 3;
    nlines = egaCursor.saved.y2 - egaCursor.saved.y1;
#ifdef EGA_HIRES
    if (egapriv.hires) {
	if (egaCursor.saved.y1 >= HIRES_LPP) {
	    start = 0;
	    if (!egapriv.curpage)
		(*vga_conf.page)(egapriv.curpage = 1);
	} else {
	    if (egapriv.curpage)
		(*vga_conf.page)(egapriv.curpage = 0);
	    if (egaCursor.saved.y2 > HIRES_LPP) {
		start = egaCursor.saved.y2 - HIRES_LPP;
		nlines = HIRES_LPP - egaCursor.saved.y1;
	    } else
		start = 0;
	}
    } else
	start = 0;
#endif
again:
    while (nlines--) {
	memcpy(dp, cp, end);
	dp += end;
	cp += widthDst;
    }
#ifdef EGA_HIRES
    if (start) {
	(*vga_conf.page)(egapriv.curpage = 1);
	nlines = start;
	start = 0;
	goto again;
    }
#endif
    egaCursor.validSaved = 1;
}

static void _near
egaRestoreUnderCursor()
{
    int start, end, nlines;
    _segment		base;
    char _based(base)	*cp;
    char *dp;
    int widthDst = egapriv.devKind;

    base = egapriv.fb;
    cp = 0;

    dp = egaCursor.pSaved;
    cp += egaCursor.saved.x1 >> 3;
    end = (egaCursor.saved.x2 - egaCursor.saved.x1) >> 3;
    cp += egaCursor.saved.y1 * widthDst;
    nlines = egaCursor.saved.y2 - egaCursor.saved.y1;
#ifdef EGA_HIRES
    if (egapriv.hires) {
	if (egaCursor.saved.y1 >= HIRES_LPP) {
	    start = 0;
	    if (!egapriv.curpage)
		(*vga_conf.page)(egapriv.curpage = 1);
	} else {
	    if (egapriv.curpage)
		(*vga_conf.page)(egapriv.curpage = 0);
	    if (egaCursor.saved.y2 > HIRES_LPP) {
		start = egaCursor.saved.y2 - HIRES_LPP;
		nlines = HIRES_LPP - egaCursor.saved.y1;
	    } else
		start = 0;
	}
    } else
	start = 0;
#endif
again:
    while (nlines--) {
	memcpy((u_char _far *)cp, dp, end);
	dp += end;
	cp += widthDst;
    }
#ifdef EGA_HIRES
    if (start) {
	(*vga_conf.page)(egapriv.curpage = 1);
	nlines = start;
	start = 0;
	goto again;
    }
#endif

    egaCursor.isUp = 0;
}

void
mouseBlockHandler()
{
    BoxRec toDraw;
    _segment		base;
    char _based(base)	*cp;
    char *dp;
    int widthDst, widthSrc;
    int nlines;
#ifdef EGA_HIRES
    int extra;
#endif

    mouse_moved = FALSE;
    if (egaCursor.isUp && !egaCursor.reDraw)
	return;

    if (!egaCursor.validSaved)
	goto jump;
    toDraw.x1 = egaCursor.pos_x - egaCursor.pBits->xhot;
    toDraw.x2 = toDraw.x1 + egaCursor.pBits->width;
    if (toDraw.x1 < 0)
	toDraw.x1 = 0;
    if (toDraw.x2 > egaCursor.screen_width)
	toDraw.x2 = egaCursor.screen_width;
    toDraw.y1 = egaCursor.pos_y - egaCursor.pBits->yhot;
    toDraw.y2 = toDraw.y1 + egaCursor.pBits->height;
    if (toDraw.y1 < 0)
	toDraw.y1 = 0;
    if (toDraw.y2 > egaCursor.screen_height)
	toDraw.y2 = egaCursor.screen_height;
    if (toDraw.x1 >= egaCursor.saved.x1 && toDraw.x2 <= egaCursor.saved.x2 &&
	toDraw.y1 >= egaCursor.saved.y1 && toDraw.y2 <= egaCursor.saved.y2 &&
	(dp = ALLOCATE_LOCAL(egaCursor.save_size))) {
/* printf("move\n"); */
	/* this may copy more than we need but who cares */
	memcpy(dp, egaCursor.pSaved, egaCursor.save_size);
	/* draw cursor onto copy */
	egaPutUpCursor(dp);
	/* copy onto screen */
	widthSrc = (egaCursor.saved.x2 - egaCursor.saved.x1) >> 3;
	nlines = egaCursor.saved.y2 - egaCursor.saved.y1;
	widthDst = ((egaPrivScr *)(egaCursor.pScreen->devPrivate))->devKind;
	base = ((egaPrivScr *)(egaCursor.pScreen->devPrivate))->fb;
	cp = 0;
	cp += egaCursor.saved.x1 >> 3;	/* will be a multiple of 8 */
#ifdef EGA_HIRES
	if (egapriv.hires) {
	    if (egaCursor.saved.y1 >= HIRES_LPP) {
		cp += (egaCursor.saved.y1 - HIRES_LPP) * widthDst;
		extra = 0;
		if (!egapriv.curpage)
		    (*vga_conf.page)(egapriv.curpage = 1);
	    } else {
		cp += egaCursor.saved.y1 * widthDst;
		if (egapriv.curpage)
		    (*vga_conf.page)(egapriv.curpage = 0);
		if (egaCursor.saved.y2 > HIRES_LPP) {
		    nlines = HIRES_LPP - egaCursor.saved.y1;
		    extra = egaCursor.saved.y2 - HIRES_LPP;
		} else
		    extra = 0;
	    }
	} else {
	    cp += egaCursor.saved.y1 * widthDst;
	    extra = 0;
	}
#else
	cp += egaCursor.saved.y1 * widthDst;
#endif
more:
	while (nlines--) {
	    memcpy((u_char _far *)cp, dp, widthSrc);
	    cp += widthDst;
	    dp += widthSrc;
	}
#ifdef EGA_HIRES
	if (extra) {
	    (*vga_conf.page)(egapriv.curpage = 1);
	    nlines = extra;
	    extra = 0;
	    goto more;
	}
#endif
	egaCursor.isUp = 1;
	DEALLOCATE_LOCAL(dp);
    } else {
jump:
	if (egaCursor.isUp)
	    egaRestoreUnderCursor();
	egaSaveUnderCursor();
	egaPutUpCursor((char *)0);
    }
    egaCursor.reDraw = 0;
}

/* Set the cursor position */
void _fastcall _near
mouseSetCursor(x, y)
int x, y;
{

    if (x == egaCursor.pos_x && x == egaCursor.pos_y)
	return;		/* no movement: punt */
    egaCursor.pos_x = x;
    egaCursor.pos_y = y;
    egaCursor.reDraw = egaCursor.isUp;
    mouse_moved = TRUE;
}

void
HideCursor()
{
    if (egaCursor.isUp)
	egaRestoreUnderCursor();
    egaCursor.validSaved = 0;
}
