/* **********************************************************
 * Copyright (C) 1998-2001 VMware, Inc.
 * All Rights Reserved
 * **********************************************************/
#ifdef VMX86_DEVEL
char rcsId_vmwarecurs[] =

    "Id: vmwarecurs.c,v 1.5 2001/01/30 23:33:02 bennett Exp $";
#endif
/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/vmware/vmwarecurs.c,v 1.3 2001/09/13 08:36:24 alanh Exp $ */

#include "vmware.h"
#include "cursorstr.h"
#include "bits2pixels.h"

typedef struct _VMwareCursPriv {
    uint8 *bSource;
    uint8 *bMask;
    uint8 *pSource;
    uint8 *pMask;
} VMwareCursPriv;

static int vmwareCursGeneration = -1;
static CursorPtr vmwareSaveCursors[MAXSCREENS];

static miPointerSpriteFuncRec vmwarePointerSpriteFuncs = {
    vmwareRealizeCursor,
    vmwareUnrealizeCursor,
    vmwareSetCursor,
    vmwareMoveCursor,
};

Bool
vmwareCursorInit(char *pm, 
		 ScreenPtr pScr)
{
    TRACEPOINT
    if (vmwareCursGeneration != serverGeneration) {
	miPointerScreenFuncPtr xf86scrn = xf86GetPointerScreenFuncs();
	
	pScr->QueryBestSize = vmwareQueryBestSize;
	if (!(miPointerInitialize(pScr, &vmwarePointerSpriteFuncs,
	    xf86scrn, FALSE)))
	    return FALSE;

	pScr->RecolorCursor = vmwareRecolorCursor;
	vmwareCursGeneration = serverGeneration;
    }
    return TRUE;
}

static void ZeroBits(uint8 *buf, int preserveBits, int totalBits) {
    while (preserveBits > 8) {
	buf++;
	preserveBits -= 8;
	totalBits -= 8;
    }
    *buf &= ~(~0 << preserveBits);
    buf++;
    preserveBits -= 8;
    totalBits -= 8;
    while (totalBits > 0) {
	*buf++ = 0;
	totalBits -= 8;
    }
}

Bool
vmwareRealizeCursor(ScreenPtr pScr, 
		    CursorPtr pCurs)
{
    pointer *pPriv = &pCurs->bits->devPriv[pScr->myNum];
    VMwareCursPriv *cursPriv;
    int i;
    uint8 *bits;
    VMWAREPtr pVMWARE;

    TRACEPOINT
    if (pCurs->bits->refcnt > 1)
	return TRUE;
    cursPriv = (VMwareCursPriv *) xcalloc(1, sizeof(VMwareCursPriv));
    if (!cursPriv)
	return FALSE;
    cursPriv->bSource = (uint8 *) xcalloc(1, PixmapBytePad(pCurs->bits->width, 1) * pCurs->bits->height);
    if (!cursPriv->bSource) {
	xfree(cursPriv);
	return FALSE;
    }
    pVMWARE = VMWAREPTR(infoFromScreen(pScr));

    memcpy(cursPriv->bSource, pCurs->bits->source, PixmapBytePad(pCurs->bits->width, 1) * pCurs->bits->height);
    bits = cursPriv->bSource;
    /* Handle cursors that do not have zero bits beyond the right edge of each scanline */
    for (i = 0; i < pCurs->bits->height; i++) {
	ZeroBits(bits, pCurs->bits->width, PixmapBytePad(pCurs->bits->width, 1) * 8);
	bits += PixmapBytePad(pCurs->bits->width, 1);
    }
    /* Raster_BitsToPixels expects most significant bit first */
    for (i = 0; i < PixmapBytePad(pCurs->bits->width, 1) * pCurs->bits->height; i++) {
	cursPriv->bSource[i] = 
	    (cursPriv->bSource[i] & 0x01) << 7 |
	    (cursPriv->bSource[i] & 0x02) << 5 |
	    (cursPriv->bSource[i] & 0x04) << 3 |
	    (cursPriv->bSource[i] & 0x08) << 1 |
	    (cursPriv->bSource[i] & 0x10) >> 1 |
	    (cursPriv->bSource[i] & 0x20) >> 3 |
	    (cursPriv->bSource[i] & 0x40) >> 5 |
	    (cursPriv->bSource[i] & 0x80) >> 7;
    }
    cursPriv->bMask = (uint8 *) xcalloc(1, PixmapBytePad(pCurs->bits->width, 1) * pCurs->bits->height);
    if (!cursPriv->bMask) {
	xfree(cursPriv->bSource);
	xfree(cursPriv);
	return FALSE;
    }
    memcpy(cursPriv->bMask, pCurs->bits->mask, PixmapBytePad(pCurs->bits->width, 1) * pCurs->bits->height);
    bits = cursPriv->bMask;
    /* Handle cursors that do not have zero bits beyond the right edge of each scanline */
    for (i = 0; i < pCurs->bits->height; i++) {
	ZeroBits(bits, pCurs->bits->width, PixmapBytePad(pCurs->bits->width, 1) * 8);
	bits += PixmapBytePad(pCurs->bits->width, 1);
    }
    for (i = 0; i < PixmapBytePad(pCurs->bits->width, 1) * pCurs->bits->height; i++) {
	cursPriv->bMask[i] =
	    (cursPriv->bMask[i] & 0x01) << 7 |
	    (cursPriv->bMask[i] & 0x02) << 5 |
	    (cursPriv->bMask[i] & 0x04) << 3 |
	    (cursPriv->bMask[i] & 0x08) << 1 |
	    (cursPriv->bMask[i] & 0x10) >> 1 |
	    (cursPriv->bMask[i] & 0x20) >> 3 |
	    (cursPriv->bMask[i] & 0x40) >> 5 |
	    (cursPriv->bMask[i] & 0x80) >> 7;
    }
    cursPriv->pSource = (uint8 *) xcalloc(4, SVGA_PIXMAP_SIZE(pCurs->bits->width, pCurs->bits->height, pVMWARE->bitsPerPixel));
    if (!cursPriv->pSource) {
	xfree(cursPriv->bMask);
	xfree(cursPriv->bSource);
	xfree(cursPriv);
	return FALSE;
    }
    cursPriv->pMask = (uint8 *) xcalloc(4, SVGA_PIXMAP_SIZE(pCurs->bits->width, pCurs->bits->height, pVMWARE->bitsPerPixel));
    if (!cursPriv->pMask) {
	xfree(cursPriv->pSource);
	xfree(cursPriv->bMask);
	xfree(cursPriv->bSource);
	xfree(cursPriv);
	return FALSE;
    }
    Raster_BitsToPixels(cursPriv->bMask, PixmapBytePad(pCurs->bits->width, 1),
			cursPriv->pMask, 4 * SVGA_PIXMAP_SCANLINE_SIZE(pCurs->bits->width, pVMWARE->bitsPerPixel),
			pVMWARE->bitsPerPixel / 8, 
			pCurs->bits->width, pCurs->bits->height, 0, ~0);
    *pPriv = (pointer) cursPriv;
    return TRUE;
}

Bool
vmwareUnrealizeCursor(ScreenPtr pScr, 
		      CursorPtr pCurs)
{
    VMwareCursPriv *cursPriv;

    TRACEPOINT
    if (pCurs->bits->refcnt <= 1 &&
	(cursPriv = pCurs->bits->devPriv[pScr->myNum])) {
	xfree(cursPriv->pMask);
	xfree(cursPriv->pSource);
	xfree(cursPriv->bMask);
	xfree(cursPriv->bSource);
	xfree(cursPriv);
    }
    return TRUE;
}

static void
vmwareLoadCursor(ScreenPtr pScr, 
		 CursorPtr pCurs, 
		 int x, 
		 int y)
{
    VMWAREPtr pVMWARE;

    TRACEPOINT

    if (!pCurs)
	return;

    pVMWARE = VMWAREPTR(infoFromScreen(pScr));

    pVMWARE->Mouse.Width = pCurs->bits->width;
    pVMWARE->Mouse.Height = pCurs->bits->height;
    pVMWARE->Mouse.XHot = pCurs->bits->xhot;
    pVMWARE->Mouse.YHot = pCurs->bits->yhot;
    vmwareRecolorCursor(pScr, pCurs, TRUE);
    vmwareMoveCursor(pScr, x, y);

    if (!(pVMWARE->vmwareCapability & SVGA_CAP_CURSOR_BYPASS)) {
	vmwareWriteWordToFIFO(pVMWARE, SVGA_CMD_DISPLAY_CURSOR);
	vmwareWriteWordToFIFO(pVMWARE, MOUSE_ID);
	vmwareWriteWordToFIFO(pVMWARE, 1);
    }
}

void
vmwareSetCursor(ScreenPtr pScr, 
		CursorPtr pCurs, 
		int x, 
		int y)
{
    int index = pScr->myNum;

    TRACEPOINT
    
    if (!pCurs)
	return;

    vmwareSaveCursors[index] = pCurs;
    vmwareLoadCursor(pScr, pCurs, x, y);
}

void
vmwareRestoreCursor(ScreenPtr pScr)
{
    int index = pScr->myNum;
    int x, y;

    TRACEPOINT
    
    miPointerPosition(&x, &y);
    vmwareLoadCursor(pScr, vmwareSaveCursors[index], x, y);
}

void
vmwareMoveCursor(ScreenPtr pScr,
		 int x, 
		 int y)
{
    VMWAREPtr pVMWARE;
    
    TRACEPOINT

    pVMWARE = VMWAREPTR(infoFromScreen(pScr));

    pVMWARE->Mouse.Box.x1 = x - pVMWARE->Mouse.XHot;
    if (pVMWARE->Mouse.Box.x1 < 0) pVMWARE->Mouse.Box.x1 = 0;
    pVMWARE->Mouse.Box.y1 = y - pVMWARE->Mouse.YHot;
    if (pVMWARE->Mouse.Box.y1 < 0) pVMWARE->Mouse.Box.y1 = 0;

    pVMWARE->Mouse.Box.x2 = pVMWARE->Mouse.Box.x1 + pVMWARE->Mouse.Width;
    pVMWARE->Mouse.Box.y2 = pVMWARE->Mouse.Box.y1 + pVMWARE->Mouse.Height;

    if (pVMWARE->cursorDefined && !pVMWARE->mouseHidden) {
	if (pVMWARE->vmwareCapability & SVGA_CAP_CURSOR_BYPASS) {
	    vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_ID, MOUSE_ID);
	    vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_X, x);
	    vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_Y, y);
	    vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_ON, SVGA_CURSOR_ON_SHOW);
	} else {
	    vmwareWriteWordToFIFO(pVMWARE, SVGA_CMD_MOVE_CURSOR);
	    vmwareWriteWordToFIFO(pVMWARE, x);
	    vmwareWriteWordToFIFO(pVMWARE, y);
	    UPDATE_ACCEL_AREA(pVMWARE, pVMWARE->Mouse.Box);
	}
    }
}

void
vmwareRecolorCursor(ScreenPtr pScr, 
		    CursorPtr pCurs, 
		    Bool displayed)
{
    xColorItem	sourceColor;
    xColorItem	maskColor;
    VMwareCursPriv *cursPriv = pCurs->bits->devPriv[pScr->myNum];
    uint32 *b1, *b2;
    int i;
    VMWAREPtr pVMWARE;

    TRACEPOINT

    if (!displayed)
	return;

    pVMWARE = VMWAREPTR(infoFromScreen(pScr));

    sourceColor.red = pCurs->foreRed;
    sourceColor.green = pCurs->foreGreen;
    sourceColor.blue = pCurs->foreBlue;

    maskColor.red = pCurs->backRed;
    maskColor.green = pCurs->backGreen;
    maskColor.blue = pCurs->backBlue;

    if (pScr->rootDepth > 8) 
    {
	sourceColor.pixel = (sourceColor.red >> (16 - pVMWARE->weight.red)) << pVMWARE->offset.red |
	    (sourceColor.green >> (16 - pVMWARE->weight.green)) << pVMWARE->offset.green |
	    (sourceColor.blue >> (16 - pVMWARE->weight.blue)) << pVMWARE->offset.blue;
	maskColor.pixel = (maskColor.red >> (16 - pVMWARE->weight.red)) << pVMWARE->offset.red |
	    (maskColor.green >> (16 - pVMWARE->weight.green)) << pVMWARE->offset.green |
	    (maskColor.blue >> (16 - pVMWARE->weight.blue)) << pVMWARE->offset.blue;
    } 
    else
    {
	ColormapPtr	pmap;

	pmap = miInstalledMaps[pScr->myNum];
    
	FakeAllocColor(pmap, &sourceColor);
	FakeAllocColor(pmap, &maskColor);

	FakeFreeColor(pmap, sourceColor.pixel);
	FakeFreeColor(pmap, maskColor.pixel);

	maskColor.red = maskColor.red >> 8;
	maskColor.green = maskColor.green >> 8;
	maskColor.blue = maskColor.blue >> 8;

	sourceColor.red = sourceColor.red >> 8;
	sourceColor.green = sourceColor.green >> 8;
	sourceColor.blue = sourceColor.blue >> 8;
    }

    /* Calculate XOR mask */
    Raster_BitsToPixels(cursPriv->bSource, PixmapBytePad(pCurs->bits->width, 1),
			cursPriv->pSource, 
			4 * SVGA_PIXMAP_SCANLINE_SIZE(pCurs->bits->width, pVMWARE->bitsPerPixel), 
			pVMWARE->bitsPerPixel / 8,
			pCurs->bits->width, pCurs->bits->height, sourceColor.pixel, maskColor.pixel);
    b1 = (uint32 *) cursPriv->pSource;
    b2 = (uint32 *) cursPriv->pMask;
    for (i = 0; i < SVGA_PIXMAP_SIZE(pCurs->bits->width, pCurs->bits->height, pVMWARE->bitsPerPixel); i++) {
	*b1++ &= ~*b2++;
    }

    /* Define cursor */
    vmwareWriteWordToFIFO(pVMWARE, SVGA_CMD_DEFINE_CURSOR);
    vmwareWriteWordToFIFO(pVMWARE, MOUSE_ID);
    vmwareWriteWordToFIFO(pVMWARE, pCurs->bits->xhot);
    vmwareWriteWordToFIFO(pVMWARE, pCurs->bits->yhot);
    vmwareWriteWordToFIFO(pVMWARE, pCurs->bits->width);
    vmwareWriteWordToFIFO(pVMWARE, pCurs->bits->height);
    vmwareWriteWordToFIFO(pVMWARE, pVMWARE->bitsPerPixel);
    vmwareWriteWordToFIFO(pVMWARE, pVMWARE->bitsPerPixel);
    b1 = (uint32 *) cursPriv->pSource;
    b2 = (uint32 *) cursPriv->pMask;
    for (i = 0; i < SVGA_PIXMAP_SIZE(pCurs->bits->width, pCurs->bits->height, pVMWARE->bitsPerPixel); i++) {
	vmwareWriteWordToFIFO(pVMWARE, *b2++);
    }
    for (i = 0; i < SVGA_PIXMAP_SIZE(pCurs->bits->width, pCurs->bits->height, pVMWARE->bitsPerPixel); i++) {
      vmwareWriteWordToFIFO(pVMWARE, *b1++);
	/*	vmwareWriteWordToFIFO(~0); */
    }
    pVMWARE->checkCursorColor = FALSE;

    /* Sync the FIFO, so that the definition preceeds any use of the cursor */
    UPDATE_ACCEL_AREA(pVMWARE, pVMWARE->Mouse.Box);
    vmwareWaitForFB(pVMWARE);
    pVMWARE->cursorDefined = TRUE;
}

void
vmwareRenewCursorColor(ScreenPtr pScr)
{
    TRACEPOINT

    if (vmwareSaveCursors[pScr->myNum])
	vmwareRecolorCursor(pScr, vmwareSaveCursors[pScr->myNum], TRUE);
}

void
vmwareBlockHandler(i, blockData, pTimeout, pReadmask)
    int i;
    pointer blockData;
    pointer pTimeout;
    pointer pReadmask;
{
    VMWAREPtr pVMWARE;

    TRACEPOINT

    pVMWARE = VMWAREPTR(xf86Screens[i]);
    if (pVMWARE->checkCursorColor)
	vmwareRenewCursorColor(screenInfo.screens[i]);
}

void
vmwareQueryBestSize(class, pwidth, pheight, pScr)
    int class;
    unsigned short *pwidth;
    unsigned short *pheight;
    ScreenPtr pScr;
{
    TRACEPOINT

    if (*pwidth > 0) {

	switch (class) {

	case CursorShape:
	    if (*pwidth > 64)
		*pwidth = 64;
	    if (*pheight > 64)
		*pheight = 64;
	    break;
	default:
	    fbQueryBestSize(class, pwidth, pheight, pScr);
	    break;
	}
    }
}

void
vmwareCursorOff(VMWAREPtr pVMWARE)
{
    TRACEPOINT

    if (pVMWARE->cursorDefined) {
	if (pVMWARE->vmwareCapability & SVGA_CAP_CURSOR_BYPASS) {
	    vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_ID, MOUSE_ID);
	    vmwareWriteReg(pVMWARE, SVGA_REG_CURSOR_ON, SVGA_CURSOR_ON_HIDE);
	} else {
	    vmwareWriteWordToFIFO(pVMWARE, SVGA_CMD_DISPLAY_CURSOR);
	    vmwareWriteWordToFIFO(pVMWARE, MOUSE_ID);
	    vmwareWriteWordToFIFO(pVMWARE, 0);
	    UPDATE_ACCEL_AREA(pVMWARE, pVMWARE->Mouse.Box);
	}
    }
}

void
vmwareClearSavedCursor(int scr_index)
{
    TRACEPOINT

    vmwareSaveCursors[scr_index] = NULL;
}


