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

#include "X.h"
#include "ega.h"
#include "cursorstr.h"
#include "servermd.h"
#include "../common/ibmmouse.h"
#include <dos.h>
#include "egafuncs.h"
#include "mxfuncs.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 structure
 * for the cursor stuff.
 */
struct egaCursor egaCursor;
Bool mouse_moved;

static void egaSaveUnderCursor(void);
static void egaPutUpCursor(u_char *);
static void egaRestoreUnderCursor(void);

/* Assume the cursor is not already up.  Display it at x, y.
 * If where is non-zero, copy the cursor onto where
 */
static void
egaPutUpCursor(where)
u_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 */
    u_char VGA_BASED *pdst;
    int widthDst;
    int widthSrc = PixmapBytePad(egaCursor.pBits->width, 1);

    if (where) {
	widthDst = egaCursor.saved_bwidth;
    } else {
	widthDst = vga_width;
	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;

    endx -= startx;
    endy -= starty;
    if (where) {
	starty -= egaCursor.saved.y1;
	startx -= egaCursor.saved.x1;
	where += starty * widthDst + (startx >> 3);
#ifdef EGA_HIRES
	starty = 0;
#endif
    } else {
	u_long bytenum;

	/* compute area in screen memory */
	bytenum = (u_long)starty * vga_width + (startx >> 3);
	pdst = vga_mem((u_short)bytenum);
#ifdef EGA_HIRES
	if (vga_hires) {
	    bytenum >>= 16;
	    if (bytenum != vga_curpage) {
		vga_curpage = bytenum;
		SetPage();
	    }
	    if (bytenum == 0 && 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); */
    if (where)
	pdst = where;
#ifdef EGA_HIRES
more:
#endif
    while (endy-- > 0) {
	andInvertedbits(pdst, pMask, startx, x, endx);
	orInvertedbits(pdst, pSource, startx, x, endx);
	pdst += widthDst;
	pSource += widthSrc;
	pMask += widthSrc;
    }
#ifdef EGA_HIRES
    if (starty) {
	vga_curpage = 1;
	SetPage();
	endy = starty;
	starty = 0;
	pdst -= 0x10000;
	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;

    pScr = pScr;
    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;
{
    pScr = pScr;
    pCur = 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 (!(mouse_flags & MOUSE_ON))
	return FALSE;
    if (egaCursor.isUp && egaCursor.pBits != pCur->bits) {
	egaRestoreUnderCursor();	/* remove old cursor */
    }
    egaCursor.pBits = pCur->bits;
    return TRUE;
}

extern Bool Must_have_memory;

static void
egaSaveUnderCursor()
{
    int start, end, nlines;
    u_char VGA_BASED *cp;
    u_char *dp;
    int i;
    int widthDst = vga_width;

    if (!egaCursor.pScreen || !egaCursor.pBits)
	return;
    cp = vga_mem(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;
    }

    egaCursor.saved_bwidth = (egaCursor.saved.x2 - egaCursor.saved.x1) >> 3;
    /* compute the number of bytes needed */
    start = egaCursor.saved_bwidth * (egaCursor.saved.y2 - egaCursor.saved.y1);
    Must_have_memory = TRUE;
    if (!egaCursor.pSaved) {
	egaCursor.pSaved = (u_char *)xalloc(start);
	egaCursor.save_size = start;
    } else if (egaCursor.save_size < start) {
	egaCursor.pSaved = (u_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;
    nlines = egaCursor.saved.y2 - egaCursor.saved.y1;
#ifdef EGA_HIRES
    if (vga_hires) {
	if (cp > vga_mem(0xffff)) {
	    cp -= 0x10000;
	    start = 0;
	    if (!vga_curpage) {
		vga_curpage = 1;
		SetPage();
	    }
	} else {
	    if (vga_curpage) {
		vga_curpage = 0;
		SetPage();
	    }
	    if (egaCursor.saved.y2 > HIRES_LPP) {
		start = egaCursor.saved.y2 - HIRES_LPP;
		nlines = HIRES_LPP - egaCursor.saved.y1;
	    } else
		start = 0;
	}
    }
#endif
    widthDst -= egaCursor.saved_bwidth;
again:
    while (nlines--) {
	for (i = egaCursor.saved_bwidth; i; i--)
		*dp++ = *cp++;
	cp += widthDst;
    }
#ifdef EGA_HIRES
    if (vga_hires && start) {
	vga_curpage = 1;
	SetPage();
	nlines = start;
	start = 0;
	cp -= 0x10000;
	goto again;
    }
#endif
    egaCursor.validSaved = 1;
}

static void
egaRestoreUnderCursor()
{
    int nlines, i;
    u_char VGA_BASED *cp;
    u_char *dp;
    int widthDst = vga_width;
#ifdef EGA_HIRES
    int start;
#endif

    cp = vga_mem(0);

    dp = egaCursor.pSaved;
    cp += egaCursor.saved.x1 >> 3;
    cp += egaCursor.saved.y1 * widthDst;
    nlines = egaCursor.saved.y2 - egaCursor.saved.y1;
#ifdef EGA_HIRES
    if (vga_hires) {
	if (cp > vga_mem(0xffff)) {
	    cp -= 0x10000;
	    start = 0;
	    if (!vga_curpage) {
		vga_curpage = 1;
		SetPage();
	    }
	} else {
	    if (vga_curpage) {
		vga_curpage = 0;
		SetPage();
	    }
	    if (egaCursor.saved.y2 > HIRES_LPP) {
		start = egaCursor.saved.y2 - HIRES_LPP;
		nlines = HIRES_LPP - egaCursor.saved.y1;
	    } else
		start = 0;
	}
    }
#endif
    widthDst -= egaCursor.saved_bwidth;
again:
    while (nlines--) {
	for (i = egaCursor.saved_bwidth; i; i--)
		*cp++ = *dp++;
	cp += widthDst;
    }
#ifdef EGA_HIRES
    if (vga_hires && start) {
	vga_curpage = 1;
	SetPage();
	nlines = start;
	start = 0;
	cp -= 0x10000;
	goto again;
    }
#endif

    egaCursor.isUp = 0;
}

void
mouseBlockHandler()
{
    BoxRec toDraw;
    u_char VGA_BASED *cp;
    u_char *dp;
    int widthDst, widthSrc;
    int nlines, i;
#ifdef EGA_HIRES
    int extra;
#endif

    mouse_moved = FALSE;
    if (!(mouse_flags & MOUSE_ON))
	return;
    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)) != 0) {
/* 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_bwidth;
	nlines = egaCursor.saved.y2 - egaCursor.saved.y1;
	widthDst = vga_width;
	cp = vga_mem(0) + egaCursor.saved.y1 * widthDst;
	cp += egaCursor.saved.x1 >> 3;	/* will be a multiple of 8 */
#ifdef EGA_HIRES
	if (vga_hires) {
	    if (cp > vga_mem(0xffff)) {
		cp -= 0x10000;
		extra = 0;
		if (!vga_curpage) {
		    vga_curpage = 1;
		    SetPage();
		}
	    } else {
		if (vga_curpage) {
		    vga_curpage = 0;
		    SetPage();
		}
		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;
#endif
	widthDst -= widthSrc;
more:
	while (nlines--) {
	    for (i = widthSrc; i; i--)
		*cp++ = *dp++;
	    cp += widthDst;
	}
#ifdef EGA_HIRES
	if (vga_hires && extra) {
	    vga_curpage = 1;
	    SetPage();
	    nlines = extra;
	    extra = 0;
	    cp -= 0x10000;
	    goto more;
	}
#endif
	egaCursor.isUp = 1;
	DEALLOCATE_LOCAL(dp);
    } else {
jump:
	if (egaCursor.isUp)
	    egaRestoreUnderCursor();
	egaSaveUnderCursor();
	egaPutUpCursor((u_char *)0);
    }
    egaCursor.reDraw = 0;
}

/* Set the cursor position */
void
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 (!(mouse_flags & MOUSE_OPEN))
	return;
    if (egaCursor.isUp)
	egaRestoreUnderCursor();
    egaCursor.validSaved = 0;
}
