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

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

extern int near mouse_X, near mouse_Y;
static void _near i8SaveUnderCursor(void);
static void _fastcall _near i8PutUpCursor(char *);
static void _near i8RestoreUnderCursor(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
i8PutUpCursor(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(i8Cursor.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(i8Cursor.saved.x2 - i8Cursor.saved.x1,
				    i8Cursor.depth);
    } else {
	widthDst = i8priv.devKind;
	base = i8priv.fb;
	cp = 0;
	i8Cursor.isUp = 1;
    }

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

    startx = i8Cursor.pos_x - i8Cursor.pBits->xhot;
    endx = startx + i8Cursor.pBits->width;
    if (endx > i8Cursor.screen_width)
	endx = i8Cursor.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 (i8priv.curpage != page) {
	    i8priv.curpage = page;
	    (*vga_conf.page)(page);
	}
*/
	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 -= i8Cursor.saved.y1;
	startx -= i8Cursor.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 ?
		    i8Cursor.fore_color : i8Cursor.back_color;
	    }
	    pcp++;
	    if (overlap) {
/*
		if (pcp < startcp) {
		    i8priv.curpage++;
		    (*vga_conf.page)(i8priv.curpage);
		}
*/
		startcp = pcp;
	    }
	    mask >>= 1;
	    if (!mask) {
		mask = 0x80;
		pmask++;
		psource++;
		startcp = pcp;
	    }
	}
	cp += widthDst;
	if (overlap) {
/*
	    if (cp < startcp) {
		i8priv.curpage++;
		(*vga_conf.page)(i8priv.curpage);
	    }
*/
	}
	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;
{
    i8Cursor.pScreen = pScr;
    i8Cursor.screen_width = pScr->width;
    i8Cursor.screen_height = pScr->height;

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

extern Bool Must_have_memory;

static void _near
i8SaveUnderCursor()
{
    int start, end, nlines;
    _segment		base;
    unsigned char _based(base)	*cp, _based(base) *startcp;
    char *dp;
    int widthDst = i8priv.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 (!i8Cursor.pScreen || !i8Cursor.pBits)
	return;
    base = i8priv.fb;

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

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

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

    dp = i8Cursor.pSaved;

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

    end = i8Cursor.saved.x2 - i8Cursor.saved.x1;
    nlines = i8Cursor.saved.y2 - i8Cursor.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 */
/*
	    i8priv.curpage++;
	    (*vga_conf.page)(i8priv.curpage);
*/
	    startcp = 0;
	    memcpy(dp - (short)cp, startcp, end + (short)cp);
	} else
	    memcpy(dp, cp, end);
	dp += end;
	cp += widthDst;
	if (overlap) {
/*
	    if (cp < startcp ) {
		i8priv.curpage++;
		(*vga_conf.page)(i8priv.curpage);
	    }
*/
	    startcp = cp;
	}
    }
    i8Cursor.validSaved = 1;
}

static void _near
i8RestoreUnderCursor(dp)
char *dp;
{
    int end, nlines;
    _segment		base;
    u_char _based(base)	*cp, _based(base) *startcp;
    int widthDst = i8priv.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 = i8priv.fb;
    cp = 0;

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

    end = i8Cursor.saved.x2 - i8Cursor.saved.x1;
    nlines = i8Cursor.saved.y2 - i8Cursor.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 */
/*
	    i8priv.curpage++;
	    (*vga_conf.page)(i8priv.curpage);
*/
	    startcp = 0;
	    memcpy(startcp, dp - (short)cp, end + (short)cp);
	} else
	    memcpy(cp, dp, end);
	dp += end;
	cp += widthDst;
	if (overlap) {
/*
	    if (cp < startcp) {
		i8priv.curpage++;
		(*vga_conf.page)(i8priv.curpage);
	    }
*/
	    startcp = cp;
	}
    }
    i8Cursor.isUp = 0;
}

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

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

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

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

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

void _near
HideCursor()
{
    if (i8Cursor.isUp)
	i8RestoreUnderCursor(i8Cursor.pSaved);
    i8Cursor.validSaved = 0;
}

void
i8RecolorCursor(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(i8priv.colormap, &item);
    i8Cursor.fore_color = (u_char)item.pixel;

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

    FakeFreeColor(i8priv.colormap, i8Cursor.fore_color);
    FakeFreeColor(i8priv.colormap, i8Cursor.back_color);

    HideCursor();
}
