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

#include "X.h"
#include "xproto.h"
#include "svga.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 svgaCursor _near svgaCursor;
Bool near mouse_moved;
Bool near direct_mouse = FALSE;

extern int near mouse_X, near mouse_Y;
static void _near svgaSaveUnderCursor(void);
static void _fastcall _near svgaPutUpCursor(char *);
static void _near svgaRestoreUnderCursor(char *);

/* 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
svgaPutUpCursor(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(svgaCursor.pBits->width, 1);
    Bool overlap = FALSE;	/* TRUE is cursor overlaps multiple pages */
    u_long bytenum;		/* which byte on display */
    u_char page;		/* which page on display */

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

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

    startx = svgaCursor.pos_x - svgaCursor.pBits->xhot;
    endx = startx + svgaCursor.pBits->width;
    if (endx > svgaCursor.screen_width)
	endx = svgaCursor.screen_width;
    if (startx < 0) {
	x = -startx;
	startx = 0;
    } else
	x = 0;
    if (startx >= endx)
	return;

/* printf("PutUp: %d,%d -> %d,%d\n", startx, starty, endx, endy); */
    /* Convert endx and endy from coordinates into counts. */
    endx -= startx;
    endy -= starty;
    pMask += x >> 3;
    pSource += x >> 3;
    x &= 7;

    if (!where) {
	/* compute area in screen memory */
	bytenum = (u_long)starty * widthDst + startx;
	page = (u_char)(bytenum >> 16);
	if (svgapriv.curpage != page) {
	    svgapriv.curpage = page;
	    SetPage();
	}
	cp += (u_short)bytenum;
	/* Check to see if the end of the data will wrap cp into the
	 * next page.
	 * Why does this work?  The part on the right of the less-than
	 * is the number of bytes that will be transfered.  Call it N.
	 * The amount of space left in the current page can be obtained
	 * by taking the negative of cp as a short then casting the result
	 * as unsigned.  For instance if cp is 0xfffc then there are 4
	 * bytes left: 0xfffc, 0xfffd, 0xfffe, and 0xffff.  An exception
	 * is if cp = 0 but this will work ok here as will be show later.
	 *
	 * Our test is then
	 *  if ( (u_short)(-(short)cp) >= N ) then no overlap.
	 * this is equivalent to
	 *  if ( (u_short)(-(short)cp) < N ) then overlap.
	 * or
	 *  if ( (u_short)cp > -N ) then overlap.
	 * As you can see, for the exception of cp = 0 this still works.
	 */
	if ((u_short)cp > (u_short)-((endy - 1) * widthDst + endx))
	    overlap = TRUE;
    } else {
	starty -= svgaCursor.saved.y1;
	startx -= svgaCursor.saved.x1;
	cp += starty * widthDst + startx;
    }
/* printf("%d ", startx); */
    while (endy-- > 0) {
	u_char mask;
	u_char *psource, *pmask, _based(base) *pcp;
	u_char _based(base) *startcp = cp;

	mask = (u_char)(0x80 >> x);
	psource = pSource;
	pmask = pMask;
	pcp = cp;
	for (startx = 0; startx < endx; startx++) {
	    if (*pmask & mask) {
		*pcp = *psource & mask ?
		    svgaCursor.fore_color : svgaCursor.back_color;
	    }
	    pcp++;
	    if (overlap) {
		if (pcp < startcp) {
		    svgapriv.curpage++;
		    SetPage();
		}
		startcp = pcp;
	    }
	    mask >>= 1;
	    if (!mask) {
		mask = 0x80;
		pmask++;
		psource++;
		startcp = pcp;
	    }
	}
	cp += widthDst;
	if (overlap) {
	    if (cp < startcp) {
		svgapriv.curpage++;
		SetPage();
	    }
	}
	startcp = cp;
	pSource += widthSrc;
	pMask += widthSrc;
    }
}

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

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

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

    if (svgaCursor.isUp && svgaCursor.pBits != pCur->bits) {
	svgaRestoreUnderCursor(svgaCursor.pSaved);	/* remove old cursor */
    }
    svgaCursor.pBits = pCur->bits;
    svgaRecolorCursor(pScr, pCur, 1);
    return TRUE;
}

extern Bool Must_have_memory;

static void _near
svgaSaveUnderCursor()
{
    int start, end, nlines;
    _segment		base;
    unsigned char _based(base)	*cp, _based(base) *startcp;
    char *dp;
    int widthDst = svgapriv.devKind;
    Bool overlap = FALSE;	/* TRUE is cursor overlaps multiple pages */
    u_long bytenum;		/* which byte on display */
    u_char page;		/* which page on display */

    if (!svgaCursor.pScreen || !svgaCursor.pBits)
	return;
    base = svgapriv.fb;

    /* First, compute region to be saved. */
    start = svgaCursor.pos_x - svgaCursor.pBits->xhot - 10;
    end = start + svgaCursor.pBits->width + 20;
    if (start < 0)
	start = 0;
    if (end < 0)
	end = 0;
    else if (end > svgaCursor.screen_width)
	end = svgaCursor.screen_width;
    if (start >= end) {
	/* can't save */
	svgaCursor.saved.x1 = 0;
	svgaCursor.saved.x2 = 0;
	svgaCursor.saved.y1 = 0;
	svgaCursor.saved.y2 = 0;
	svgaCursor.validSaved = 0;
	return;
    }
    svgaCursor.saved.x2 = end;
    svgaCursor.saved.x1 = start;

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

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

    dp = svgaCursor.pSaved;

    bytenum = (u_long)svgaCursor.saved.y1 * widthDst + svgaCursor.saved.x1;
    page = (u_char)(bytenum >> 16);
    page &= 0xf;	/* just to be sure */
    if (svgapriv.curpage != page) {
	svgapriv.curpage = page;
	SetPage();
    }
    cp = (u_char _based(base) *)(u_short)bytenum;

    end = svgaCursor.saved.x2 - svgaCursor.saved.x1;
    nlines = svgaCursor.saved.y2 - svgaCursor.saved.y1;

    /* Check to see if the end of the data will wrap cp into the
     * next page.
     */
    if ((u_short)cp > (u_short)(-((nlines - 1) * widthDst + end)))
	overlap = TRUE;

    startcp = cp;
    while (nlines--) {
	if (overlap && cp + end < startcp) {
/* printf("cp=%x, count=%d %d\n", (short)cp, end, -(short)cp); */
	    memcpy(dp, cp, -(short)cp);		/* copy until end of page */
	    svgapriv.curpage++;
	    SetPage();
	    startcp = 0;
	    memcpy(dp - (short)cp, startcp, end + (short)cp);
	} else
	    memcpy(dp, cp, end);
	dp += end;
	cp += widthDst;
	if (overlap) {
	    if (cp < startcp ) {
		svgapriv.curpage++;
		SetPage();
	    }
	    startcp = cp;
	}
    }
    svgaCursor.validSaved = 1;
}

static void _near
svgaRestoreUnderCursor(dp)
char *dp;
{
    int end, nlines;
    _segment		base;
    u_char _based(base)	*cp, _based(base) *startcp;
    int widthDst = svgapriv.devKind;
    Bool overlap = FALSE;	/* TRUE is cursor overlaps multiple pages */
    u_long bytenum;		/* which byte on display */
    u_char page;		/* which page on display */

    base = svgapriv.fb;
    cp = 0;

    bytenum = (u_long)svgaCursor.saved.y1 * widthDst + svgaCursor.saved.x1;
    page = (u_char)(bytenum >> 16);
    page &= 0xf;	/* just to be sure */
    if (svgapriv.curpage != page) {
	svgapriv.curpage = page;
	SetPage();
    }
    cp += (u_short)bytenum;

    end = svgaCursor.saved.x2 - svgaCursor.saved.x1;
    nlines = svgaCursor.saved.y2 - svgaCursor.saved.y1;

    /* Check to see if the end of the data will wrap cp into the
     * next page.
     */
    if ((u_short)cp > (u_short)-((nlines - 1) * widthDst + end))
	overlap = TRUE;

    startcp = cp;
    while (nlines--) {
	if (overlap && (u_short)cp > (u_short)(-end)) {
	    memcpy(cp, dp, -(short)cp);		/* copy until end of page */
	    svgapriv.curpage++;
	    SetPage();
	    startcp = 0;
	    memcpy(startcp, dp - (short)cp, end + (short)cp);
	} else
	    memcpy(cp, dp, end);
	dp += end;
	cp += widthDst;
	if (overlap) {
	    if (cp < startcp) {
		svgapriv.curpage++;
		SetPage();
	    }
	    startcp = cp;
	}
    }
    svgaCursor.isUp = 0;
}

void
mouseBlockHandler()
{
    BoxRec toDraw;
    u_char *dp;

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

    if (!svgaCursor.validSaved)
	goto jump;
    toDraw.x1 = svgaCursor.pos_x - svgaCursor.pBits->xhot;
    toDraw.x2 = toDraw.x1 + svgaCursor.pBits->width;
    if (toDraw.x1 < 0)
	toDraw.x1 = 0;
    if (toDraw.x2 > svgaCursor.screen_width)
	toDraw.x2 = svgaCursor.screen_width;
    toDraw.y1 = svgaCursor.pos_y - svgaCursor.pBits->yhot;
    toDraw.y2 = toDraw.y1 + svgaCursor.pBits->height;
    if (toDraw.y1 < 0)
	toDraw.y1 = 0;
    if (toDraw.y2 > svgaCursor.screen_height)
	toDraw.y2 = svgaCursor.screen_height;
    if (toDraw.x1 >= svgaCursor.saved.x1 && toDraw.x2 <= svgaCursor.saved.x2 &&
	toDraw.y1 >= svgaCursor.saved.y1 && toDraw.y2 <= svgaCursor.saved.y2 &&
	(dp = ALLOCATE_LOCAL(svgaCursor.save_size))) {
/* printf("move\n"); */
	memcpy(dp, svgaCursor.pSaved, svgaCursor.save_size);
	/* draw cursor onto copy */
	svgaPutUpCursor(dp);
	/* copy onto screen */
	svgaRestoreUnderCursor(dp);
	svgaCursor.isUp = 1;
	DEALLOCATE_LOCAL(dp);
    } else {
jump:
	if (svgaCursor.isUp)
	    svgaRestoreUnderCursor(svgaCursor.pSaved);
	svgaSaveUnderCursor();
	svgaPutUpCursor((char *)0);
    }
    svgaCursor.reDraw = 0;
}

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

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

void _near
HideCursor()
{
    if (svgaCursor.isUp)
	svgaRestoreUnderCursor(svgaCursor.pSaved);
    svgaCursor.validSaved = 0;
}

void
svgaRecolorCursor(pScr, pCurs, displayed)
ScreenPtr pScr;
CursorPtr pCurs;
Bool displayed;
{
    xColorItem item;

    if (!displayed)
	return;
    item.flags = DoRed|DoGreen|DoBlue;
    item.red = pCurs->foreRed;
    item.green = pCurs->foreGreen;
    item.blue = pCurs->foreBlue;
    FakeAllocColor(svgapriv.colormap, &item);
    svgaCursor.fore_color = (u_char)item.pixel;

    item.red = pCurs->backRed;
    item.green = pCurs->backGreen;
    item.blue = pCurs->backBlue;
    FakeAllocColor(svgapriv.colormap, &item);
    svgaCursor.back_color = (u_char)item.pixel;

    FakeFreeColor(svgapriv.colormap, svgaCursor.fore_color);
    FakeFreeColor(svgapriv.colormap, svgaCursor.back_color);

    HideCursor();
}
