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

#include "X.h"
#include "xproto.h"
#include "svga.h"
#include "cursorstr.h"
#include "servermd.h"
#include "../common/ibmmouse.h"
#include <dos.h>
#include "mxfuncs.h"
#include "svgafuncs.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 svgaCursor svgaCursor;
Bool mouse_moved;
extern int mouse_flags;

static void svgaSaveUnderCursor(void);
static void svgaPutUpCursor(u_char *);
static void svgaRestoreUnderCursor(u_char *);

/* Assume the cursor is not already up.  Display it at x, y.
 * If where is non-zero, copy the cursor onto where
 */
static void
svgaPutUpCursor(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 SVGA_BASED *pdst;
    int widthDst;
    int widthSrc = PixmapBytePad(svgaCursor.pBits->width, 1);
    u_long bytenum;		/* which byte on display */
    u_short page;		/* which page on display */

    if (where) {
	widthDst = PixmapBytePad(svgaCursor.saved.x2 - svgaCursor.saved.x1,
				    svgaCursor.depth);
    } else {
	widthDst = svga_width;
	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) {
	starty -= svgaCursor.saved.y1;
	startx -= svgaCursor.saved.x1;
	where += starty * widthDst + startx;
    } else {
	/* compute area in screen memory */
	bytenum = (u_long)starty * svga_width + startx;
	page = (u_short)(bytenum >> 16);
	if (svga_curpage != page) {
	    svga_curpage = page;
	    SetPage();
	}
	pdst = vga_mem((u_short)bytenum);
    }
/* printf("%d ", startx); */
    widthDst -= endx;
    while (endy-- > 0) {
	u_char mask;
	u_char *psource, *pmask;

	mask = (u_char)(0x80 >> x);
	psource = pSource;
	pmask = pMask;
	if (where) {
	    for (startx = 0; startx < endx; startx++) {
		if (*pmask & mask) {
		    *where = *psource & mask ?
			svgaCursor.fore_color : svgaCursor.back_color;
		}
		where++;
		mask >>= 1;
		if (!mask) {
		    mask = 0x80;
		    pmask++;
		    psource++;
		}
	    }
	    where += widthDst;
	} else {
	    for (startx = 0; startx < endx; startx++) {
		if (*pmask & mask) {
		    *pdst = *psource & mask ?
			svgaCursor.fore_color : svgaCursor.back_color;
		}
		pdst++;
		if ((u_short)pdst == 0) {
		    pdst = vga_mem(0);
		    svga_curpage++;
		    SetPage();
		}
		mask >>= 1;
		if (!mask) {
		    mask = 0x80;
		    pmask++;
		    psource++;
		}
	    }
	    if ((u_short)pdst + widthDst >= 0x10000) {
		pdst += widthDst;
		pdst -= 0x10000;
		svga_curpage++;
		SetPage();
	    } else
		pdst += widthDst;
	}
	pSource += widthSrc;
	pMask += widthSrc;
    }
}

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

Bool
mouseUnrealizeCursor(pScr, pCur)
ScreenPtr pScr;
CursorPtr pCur;
{
    pScr = pScr;
    pCur = 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 (!(mouse_flags & MOUSE_ON))
	return FALSE;
    if (svgaCursor.isUp && svgaCursor.pBits != pCur->bits) {
	svgaRestoreUnderCursor(svgaCursor.pSaved);	/* remove old cursor */
    }
    svgaCursor.pBits = pCur->bits;
    mxRecolorCursor(pScr, pCur, 1);
    return TRUE;
}

extern Bool Must_have_memory;

static void
svgaSaveUnderCursor()
{
    int start, end, nlines;
    u_char SVGA_BASED *cp;
    u_char *dp;
    u_long bytenum;		/* which byte on display */
    u_short page;		/* which page on display */

    if (!svgaCursor.pScreen || !svgaCursor.pBits)
	return;

    /* 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);
    if (!svgaCursor.pSaved) {
	Must_have_memory = TRUE;
	svgaCursor.pSaved = (u_char *)xalloc(start);
	Must_have_memory = FALSE;
	svgaCursor.save_size = start;
    } else if (svgaCursor.save_size < start) {
	Must_have_memory = TRUE;
	svgaCursor.pSaved = (u_char *)xrealloc(svgaCursor.pSaved, start);
	Must_have_memory = FALSE;
	svgaCursor.save_size = start;
    }

    dp = svgaCursor.pSaved;

    bytenum = (u_long)svgaCursor.saved.y1 * svga_width + svgaCursor.saved.x1;
    page = bytenum >> 16;
    if (svga_curpage != page) {
	svga_curpage = page;
	SetPage();
    }
    cp = vga_mem((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.
     */

    while (nlines--) {
	if ((u_short)cp + end > 0x10000) {
	    start = (u_short)-(short)cp;
	    vga_memcpy(dp, cp, start);	/* copy until end of page */
	    svga_curpage++;
	    SetPage();
	    cp = vga_mem(0);
	    vga_memcpy(dp + start, cp, end - start);
	    cp += svga_width - start;
	} else {
	    vga_memcpy(dp, cp, end);
	    if ((u_short)cp + svga_width >= 0x10000) {
		cp += svga_width;
		cp -= 0x10000;
		svga_curpage++;
		SetPage();
	    } else
		cp += svga_width;
	}
	dp += end;
    }
    svgaCursor.validSaved = 1;
}

static void
svgaRestoreUnderCursor(dp)
u_char *dp;
{
    int end, nlines;
    u_char SVGA_BASED *cp;
    u_long bytenum;		/* which byte on display */
    u_short page;		/* which page on display */

    bytenum = (u_long)svgaCursor.saved.y1 * svga_width + svgaCursor.saved.x1;
    page = (u_short)(bytenum >> 16);
    if (svga_curpage != page) {
	svga_curpage = page;
	SetPage();
    }
    cp = vga_mem((u_short)bytenum);

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

    while (nlines--) {
	if ((u_short)cp + end > 0x10000) {
	    int start = (u_short)-(short)cp;
	    vga_memcpy(cp, dp, start);		/* copy until end of page */
	    svga_curpage++;
	    SetPage();
	    cp = vga_mem(0);
	    vga_memcpy(cp, dp + start, end - start);
	    cp += svga_width - start;
	} else {
	    vga_memcpy(cp, dp, end);
	    if ((u_short)cp + svga_width >= 0x10000) {
		cp += svga_width;
		cp -= 0x10000;
		svga_curpage++;
		SetPage();
	    } else
		cp += svga_width;
	}
	dp += end;
    }
    svgaCursor.isUp = 0;
}

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

    mouse_moved = FALSE;
    if (!(mouse_flags & MOUSE_ON))
	return;
    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) {
/* printf("move\n"); */
	dp = ALLOCATE_LOCAL(svgaCursor.save_size);
	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((u_char *)0);
    }
    svgaCursor.reDraw = 0;
}

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

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

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

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

    FakeFreeColor(svga_colormap, svgaCursor.fore_color);
    FakeFreeColor(svga_colormap, svgaCursor.back_color);

    HideCursor();
}
