|
|
/******************************Module*Header*******************************\
* Module Name: curseng.cxx * * Engine cursor support. These routines are only called by USER to * set the cursor shape and move it about the screen. This is not the * engine simulation of the pointer. * * Created: 18-Mar-1991 11:39:40 * Author: Tue 12-May-1992 01:49:04 -by- Charles Whitmer [chuckwh] * * Copyright (c) 1991-1999 Microsoft Corporation \**************************************************************************/
#include "precomp.hxx"
// We always create the alpha bitmap from the master shape with two
// rows of pixels on every side to give room for two pixels of blur and
// for an edge of work space.
#define ALPHA_EDGE 3
// Some constants defining the shadow cursor:
#define SHADOW_X_OFFSET 3
#define SHADOW_Y_OFFSET 1
#define SHADOW_ALPHA 0x40000000
/******************************Public*Routine******************************\
* vDetermineSurfaceBounds * * Calculate a tight bounds for the surface, given a byte-value that is * evaluated as the 'space' on the edges. * * History: * 20-Oct-1996 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
LONG galBitsPerPixel[] = { 0, 1, 4, 8, 16, 24, 32 };
VOID vDetermineSurfaceBounds( SURFOBJ* pso, BYTE jBlank, LONG yTop, // Start scan
LONG yBottom, // End scan
RECTL* prclBounds) // Output
{ LONG lDelta; BYTE* pjScan0; BYTE* pj; ULONG cj; ULONG cy; ULONG i; BYTE* pjTop; BYTE* pjBottom; BYTE* pjLeft; BYTE* pjRight; BYTE jStamp; LONG iLeft; LONG iRight; LONG xRight; LONG cBitsPerPixel;
ASSERTGDI(pso->iType == STYPE_BITMAP, "Can directly access only STYPE_BITMAP surfaces");
lDelta = pso->lDelta; cBitsPerPixel = galBitsPerPixel[pso->iBitmapFormat]; xRight = pso->sizlBitmap.cx; cj = (xRight * cBitsPerPixel + 7) >> 3; pjScan0 = (BYTE*) pso->pvScan0; iLeft = 0; iRight = cj;
// Blank the right bits up to the byte multiple:
if ((pso->iBitmapFormat == BMF_1BPP) && (xRight & 7)) { jStamp = (0x100 >> (xRight & 7)) - 1; // 1 -> 0x7f, 2 -> 0x3f ...7 -> 0x01
pjRight = pjScan0 + lDelta * yTop + cj - 1; if (jBlank) { for (pj = pjRight, i = (yBottom - yTop); i != 0; pj += lDelta, i--) { *pj |= jStamp; } } else { jStamp = ~jStamp; for (pj = pjRight, i = (yBottom - yTop); i != 0; pj += lDelta, i--) { *pj &= jStamp; } } }
// Trim the top:
pjTop = pjScan0 + lDelta * yTop; while (TRUE) { for (pj = pjTop, i = cj; i != 0; pj++, i--) { if (*pj != jBlank) goto Done_Top_Trim; }
pjTop += lDelta; yTop++;
// Catch the case where the surface is completely empty:
if (yTop >= yBottom) { prclBounds->left = LONG_MAX; prclBounds->top = LONG_MAX; prclBounds->right = LONG_MIN; prclBounds->bottom = LONG_MIN; return; } }
Done_Top_Trim:
// Trim the bottom:
pjBottom = pjScan0 + lDelta * (yBottom - 1); while (TRUE) { for (pj = pjBottom, i = cj; i != 0; pj++, i--) { if (*pj != jBlank) goto Done_Bottom_Trim; }
pjBottom -= lDelta; yBottom--; ASSERTGDI(yTop < yBottom, "Empty cursor should have been caught above"); }
Done_Bottom_Trim:
// Trim the left side:
cy = yBottom - yTop; pjLeft = pjTop; while (TRUE) { for (pj = pjLeft, i = cy; i != 0; pj += lDelta, i--) { if (*pj != jBlank) goto Done_Left_Trim; }
pjLeft++; iLeft++; ASSERTGDI(iLeft < iRight, "Empty cursor should have been caught above"); }
Done_Left_Trim:
// Trim the right side:
pjRight = pjTop + cj - 1; while (TRUE) { for (pj = pjRight, i = cy; i != 0; pj += lDelta, i--) { if (*pj != jBlank) goto Done_Right_Trim; }
pjRight--; iRight--; ASSERTGDI(iLeft < iRight, "Empty cursor should have been caught above"); }
Done_Right_Trim:
prclBounds->top = yTop; prclBounds->bottom = yBottom; prclBounds->left = (8 * iLeft) / cBitsPerPixel; // Floor
prclBounds->right = (8 * iRight + cBitsPerPixel - 1) / cBitsPerPixel; // Ceiling
// Account for the fact that we are checking on bytes worth at a time, and
// so might actually end up somewhat past the right edge of the bitmap:
prclBounds->right = min(prclBounds->right, pso->sizlBitmap.cx); }
/******************************Public*Routine******************************\
* vCalculateCursorBounds * * Calculate a tight bounds for the specified cursor shape. * * History: * 20-Oct-1996 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vCalculateCursorBounds( SURFOBJ* psoMask, SURFOBJ* psoColor, RECTL* prclBounds) { LONG cyMask; ERECTL erclBounds; ERECTL erclMask;
cyMask = psoMask->sizlBitmap.cy >> 1; vDetermineSurfaceBounds(psoMask, 0xff, 0, cyMask, &erclMask);
if (psoColor == NULL) { vDetermineSurfaceBounds(psoMask, 0x00, cyMask, 2 * cyMask, &erclBounds);
if (!erclBounds.bWrapped()) { erclBounds.top -= cyMask; erclBounds.bottom -= cyMask; } } else { vDetermineSurfaceBounds(psoColor, 0x00, 0, cyMask, &erclBounds); }
// The bounds are the union of the AND mask bounds and the OR mask bounds:
erclBounds |= erclMask; if (erclBounds.bWrapped()) { erclBounds.left = 0; erclBounds.top = 0; erclBounds.right = 1; erclBounds.bottom = 1; }
// KdPrint((">> Cursor bounds: (%li, %li, %li, %li)\n",
// erclBounds.left, erclBounds.top, erclBounds.right, erclBounds.bottom));
//
// Stash the bounds away for next time:
*prclBounds = erclBounds; }
/******************************Public*Routine******************************\
* bBlurCursorShadow * * Do an in-place blur of the cursor shadow (i.e., the blurred image will * replace the image passed in). * * This is done by applying an approximation of a 3x3 boxcar convolution. * * The cursor shadow is assumed to be in the MSB of the surface. The surface * is assumed to be a 32bpp BGRA surface. * * History: * 03-Mar-1998 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/
BOOL bBlurCursorShadow( SURFOBJ* pso) { USHORT aus0[64]; USHORT aus1[64]; USHORT aus2[64]; PUSHORT apus[3];
// Function assumes caller will pass a 32bpp surface.
ASSERTGDI(pso->iBitmapFormat == BMF_32BPP, "bBlurCursorShadow: bad iBitmapFormat\n");
// Must at least be 3x3. Don't bother if it's too small.
if ((pso->sizlBitmap.cx < 3) || (pso->sizlBitmap.cy < 3)) { WARNING("bBlurCursorShadow: bitmap too small\n"); return(FALSE); }
// Setup temporary scanline memory. If it will fit, use the stack
// buffer. Otherwise allocate pool memory.
if (pso->sizlBitmap.cx > 64) { apus[0] = (PUSHORT) PALLOCMEM(pso->sizlBitmap.cx * sizeof(USHORT) * 3, 'pmtG'); if (apus[0]) { apus[1] = apus[0] + pso->sizlBitmap.cx; apus[2] = apus[1] + pso->sizlBitmap.cx; } } else { apus[0] = aus0; apus[1] = aus1; apus[2] = aus2; }
if (apus[0] == NULL) { WARNING("bBlurCursorShadow: mem alloc failure\n"); return(FALSE); }
// Fill up the scanline memory with 3x1 boxcar sums for the
// first three scanlines.
PULONG pulIn = (PULONG) pso->pvScan0; PULONG pulTmp; USHORT usLast; USHORT *pus, *pusEnd; ULONG j;
for (j = 0; j < 3; j++) { // Compute the scanline sum. Note that output is two pixels
// smaller than the input.
pus = apus[j]; pusEnd = pus + (pso->sizlBitmap.cx - 2); pulTmp = pulIn;
while (pus < pusEnd) { *pus = (USHORT) ((pulTmp[0] >> 24) + (pulTmp[1] >> 24) + (pulTmp[2] >> 24)); pus += 1; pulTmp += 1; }
// Next scanline.
pulIn = (PULONG) (((PBYTE) pulIn) + pso->lDelta); }
// Compute the average (3x3 boxcar convolution) for each output
// scanline.
PULONG pulOut = ((PULONG) (((PBYTE) pso->pvScan0) + pso->lDelta)) + 1; ULONG ulNumScans = pso->sizlBitmap.cy - 2; ULONG ulNext = 0;
while (ulNumScans--) { // Setup output pointers.
PULONG pulAvg = pulOut; PULONG pulAvgEnd = pulAvg + (pso->sizlBitmap.cx - 2);
// Setup pointers to run the scanline 3x1 sums.
PUSHORT pusTmp[3];
pusTmp[0] = apus[0]; pusTmp[1] = apus[1]; pusTmp[2] = apus[2];
// Compute the average scanline.
while (pulAvg < pulAvgEnd) { USHORT usSum;
// Strictly speaking we should divide the sum by 9, but since
// this is just for looks, we can approximate as a divide by 8
// minus a divide by 64 (will produce in a slightly too small
// result).
//
// 1/9 = 0.111111111... in decimal
// = 0.000111000111... in binary
//
// Approximations:
//
// 1/8 - 1/64 = 0.109375
// 1/8 - 1/64 + 1/512 = 0.111328125
// 1/8 - 1/64 + 1/512 - 1/4096 = 0.111083984
usSum = *pusTmp[0] + *pusTmp[1] + *pusTmp[2]; //*pulAvg = (usSum / 9) << 24;
//*pulAvg = ((usSum >> 3) - (usSum >> 6)) << 24;
*pulAvg = (usSum >> 3) << 24;
pulAvg += 1; pusTmp[0] += 1; pusTmp[1] += 1; pusTmp[2] += 1; }
// Next output scanline.
pulOut = (PULONG) (((PBYTE) pulOut) + pso->lDelta);
// Need to compute 3x1 boxcar sum for the next scanline.
if (ulNumScans) { // Compute the scanline sum. Note that output is two pixels
// smaller than the input.
pus = apus[ulNext]; pusEnd = pus + (pso->sizlBitmap.cx - 2); pulTmp = pulIn;
while (pus < pusEnd) { *pus = (USHORT) ((pulTmp[0] >> 24) + (pulTmp[1] >> 24) + (pulTmp[2] >> 24)); pus += 1; pulTmp += 1; }
// Next scanline.
pulIn = (PULONG) (((PBYTE) pulIn) + pso->lDelta);
// Next scanline summation buffer.
ulNext++; if (ulNext >= 3) ulNext = 0; } }
// Cleanup temporary memory.
if (apus[0] != aus0) { VFREEMEM(apus[0]); }
return(TRUE); }
/******************************Public*Routine******************************\
* bApplicationAlphaCursor * * This routine will create a pre-muliplied alpha version of the specified * cursor if the cursor has non-pre-mulitplied alpha values in it. * * History: * 20-Oct-1996 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bApplicationAlphaCursor( SURFOBJ* psoAlpha, SURFOBJ* psoColor, RECTL* prclBounds) { ULONG ulAlpha; ULONG ul; ULONG* pulScan0; ULONG* pul; ULONG cPixels; BOOL bPerPixelAlpha; ULONG i; RECTL rclDst;
ASSERTGDI(psoColor->iBitmapFormat == BMF_32BPP, "Expected only BGRA 32-bpp bitmaps");
// Copy the cursor to our alpha surface, accounting for the same
// offset that is needed for shadowed cursors:
rclDst.left = ALPHA_EDGE; rclDst.top = ALPHA_EDGE; rclDst.right = ALPHA_EDGE + prclBounds->right; rclDst.bottom = ALPHA_EDGE + prclBounds->bottom;
EngCopyBits(psoAlpha, psoColor, NULL, NULL, &rclDst, &gptlZero);
// Check for any pixels with non-zero alpha:
cPixels = psoAlpha->sizlBitmap.cx * psoAlpha->sizlBitmap.cy; pulScan0 = (ULONG*) psoAlpha->pvBits;
bPerPixelAlpha = FALSE; for (pul = pulScan0, i = cPixels; i != 0; pul++, i--) { if ((*pul & 0xff000000) != 0) { bPerPixelAlpha = TRUE; break; } }
if (bPerPixelAlpha) { // Okay, we can now assume that the application gave us a per-pixel
// alpha cursor shape. Now go through and convert it to pre-multiplied
// alpha.
for (pul = pulScan0, i = cPixels; i != 0; pul++, i--) { ul = *pul; ulAlpha = (ul & 0xff000000) >> 24; *pul = (((ul & 0xff000000))) | ((((ul & 0xff0000) * ulAlpha) >> 8) & 0xff0000) | ((((ul & 0xff00) * ulAlpha) >> 8) & 0xff00) | ((((ul & 0xff) * ulAlpha) >> 8) & 0xff); } }
return(bPerPixelAlpha); }
/******************************Public*Routine******************************\
* bShadowAlphaCursor * * This routine will create a pre-multiplied alpha version of the specified * cursor along with a shadow mask, assuming that the specified cursor does * not try to do 'XOR'. * * History: * 20-Oct-1996 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL bShadowAlphaCursor( SURFOBJ* psoAlpha, SURFOBJ* psoMask, SURFOBJ* psoColor, XLATEOBJ* pxloMask, XLATEOBJ* pxloColor, RECTL* prclBounds) { BOOL bRet = FALSE; // Assume failure
ULONG cxMask; ULONG cyMask; RECTL rclDst; XLATEOBJ xlo; ULONG aulXlate[2]; ULONG* pul; ULONG i; POINTL ptlSrc;
cyMask = psoMask->sizlBitmap.cy >> 1; cxMask = psoMask->sizlBitmap.cx; xlo.pulXlate = aulXlate;
// We can only generate a shadow cursor if there are no XOR pixels
// in the definition of the cursor (because we use an AlphaBlend
// function which has no concept of XOR). So check if there are
// any XOR pixels. We do this by ANDing the XOR mask with the AND
// mask; if any part of the result is non-zero, then there is an
// XOR component to the cursor.
//
// First, make a copy of the AND bits, converting it to 32bpp for
// convenience:
rclDst.left = 0; rclDst.top = 0; rclDst.right = cxMask; rclDst.bottom = cyMask;
if (psoColor != NULL) { EngBitBlt(psoAlpha, psoColor, NULL, NULL, pxloColor, &rclDst, &gptlZero, NULL, NULL, NULL, 0xeeee); // SRCPAINT
} else { ptlSrc.x = 0; ptlSrc.y = cyMask;
EngBitBlt(psoAlpha, psoMask, NULL, NULL, pxloMask, &rclDst, &ptlSrc, NULL, NULL, NULL, 0xeeee); // SRCPAINT
}
// Now, AND in the XOR mask. Use a fake XLATEOBJ to do the color
// expansion:
aulXlate[0] = 0x00000000; aulXlate[1] = 0xffffffff; EngBitBlt(psoAlpha, psoMask, NULL, NULL, &xlo, &rclDst, &gptlZero, NULL, NULL, NULL, 0x8888); // SRCAND
// Now check for any non-zero resulting pixels:
pul = (ULONG*) psoAlpha->pvBits; i = psoAlpha->sizlBitmap.cy * abs(psoAlpha->lDelta) / 4;
for (; i != 0; pul++, i--) { if (*pul != 0) { return(FALSE); } }
// Okay, we're golden for adding the shadow. First, construct a
// rectangle that represents the shadow's position:
rclDst.left = ALPHA_EDGE + SHADOW_X_OFFSET; rclDst.top = ALPHA_EDGE + SHADOW_Y_OFFSET; rclDst.right = ALPHA_EDGE + SHADOW_X_OFFSET + prclBounds->right; rclDst.bottom = ALPHA_EDGE + SHADOW_Y_OFFSET + prclBounds->bottom;
// Create a copy of the mask, giving the mask a particular alpha
// value:
aulXlate[0] = SHADOW_ALPHA; aulXlate[1] = 0x0; EngCopyBits(psoAlpha, psoMask, NULL, &xlo, &rclDst, &gptlZero);
// Create the blur from the copy. We invoke the 3x3 blur twice
// to get a better blur (note that we changed ALPHA_EDGE to
// accomodate this).
if (bBlurCursorShadow(psoAlpha) && bBlurCursorShadow(psoAlpha)) { rclDst.left = ALPHA_EDGE; rclDst.top = ALPHA_EDGE; rclDst.right = ALPHA_EDGE + prclBounds->right; rclDst.bottom = ALPHA_EDGE + prclBounds->bottom;
// Now, zero the opaque pixels:
aulXlate[0] = 0x00000000; aulXlate[1] = 0xffffffff; EngBitBlt(psoAlpha, psoMask, NULL, NULL, &xlo, &rclDst, &gptlZero, NULL, NULL, NULL, 0x8888); // SRCAND
// Now, give all the opaque pixels an alpha value of 0xff:
aulXlate[0] = 0xff000000; aulXlate[1] = 0x00000000; EngBitBlt(psoAlpha, psoMask, NULL, NULL, &xlo, &rclDst, &gptlZero, NULL, NULL, NULL, 0xeeee); // SRCPAINT
// Finally, OR in the opaque bits on top of the shadow:
if (psoColor != NULL) { EngBitBlt(psoAlpha, psoColor, NULL, NULL, pxloColor, &rclDst, &gptlZero, NULL, NULL, NULL, 0xeeee); // SRCPAINT
} else { ptlSrc.x = 0; ptlSrc.y = cyMask;
EngBitBlt(psoAlpha, psoMask, NULL, NULL, pxloMask, &rclDst, &ptlSrc, NULL, NULL, NULL, 0xeeee); // SRCPAINT
}
bRet = TRUE; }
return(bRet); }
/******************************Public*Routine******************************\
* vProcessCursorShape * * This routine will: * * 1. Calculate the bounds on the cursor shape; * 2. Create a pre-multiplied alpha surface if the cursor shape has per- * pixel alpha; * 3. Create a pre-multiplied alpha shadow surface for the cursor if * we've been asked to create a 'shadow'. * * History: * 20-Oct-1996 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID vProcessCursorShape( HDEV hdev, BOOL bShadow, SURFOBJ* psoMask, SURFOBJ* psoColor, PALETTE* ppalColor, RECTL* prclBounds, HBITMAP* phbmAlpha) { SPRITESTATE* pSpriteState; SURFACE* psurfColor; SURFMEM SurfDimo; BOOL bAlpha; DEVBITMAPINFO dbmi; SURFOBJ* psoAlpha; RECTL rclAlpha; RECTL rclColor;
// Delete the existing hbmAlpha, so that it can be regenerated.
if (*phbmAlpha != NULL) { GreDeleteObject(*phbmAlpha); *phbmAlpha = NULL; }
// Do a first-pass bounds calculation taking into account only
// the monochrome mask:
vCalculateCursorBounds(psoMask, NULL, prclBounds);
// Create a 32bpp DIB to hold the alpha bits. We add in 4 to
// the dimensions to account for the one pixel extra on every
// side that the blurring result needs, plus the one extra
// pixel on every side that the blurring process needs as work-
// space.
rclAlpha.left = 0; rclAlpha.top = 0; rclAlpha.right = SHADOW_X_OFFSET + (2 * ALPHA_EDGE) + (psoMask->sizlBitmap.cx); rclAlpha.bottom = SHADOW_Y_OFFSET + (2 * ALPHA_EDGE) + (psoMask->sizlBitmap.cy >> 1);
dbmi.cxBitmap = rclAlpha.right; dbmi.cyBitmap = rclAlpha.bottom; dbmi.iFormat = BMF_32BPP; dbmi.fl = BMF_TOPDOWN; dbmi.hpal = NULL;
if (SurfDimo.bCreateDIB(&dbmi, NULL)) { psoAlpha = SurfDimo.pSurfobj(); psurfColor = SURFOBJ_TO_SURFACE_NOT_NULL(psoColor);
EXLATEOBJ xloMono; EXLATEOBJ xloColor; XEPALOBJ palColor(ppalColor); XEPALOBJ palDefault(ppalDefault); XEPALOBJ palRGB(gppalRGB); XEPALOBJ palMono(ppalMono);
if (xloMono.bInitXlateObj(NULL, DC_ICM_OFF, palMono, palRGB, palDefault, palDefault, 0, 0xffffff, 0)) { if ((psoColor == NULL) || (xloColor.bInitXlateObj(NULL, DC_ICM_OFF, palColor, palRGB, palDefault, palDefault, 0, 0, 0))) { bAlpha = FALSE;
if (psoColor != NULL) { // Do a second-pass bounds accumulation, taking into
// account both the monochrome and color masks. We
// use the Alpha surface to create a directly-readable
// copy of 'psoColor' (it might be STYPE_DEVBITMAP):
rclColor.left = 0; rclColor.top = 0; rclColor.right = psoMask->sizlBitmap.cx; rclColor.bottom = psoMask->sizlBitmap.cy >> 1; EngCopyBits(psoAlpha, psoColor, NULL, xloColor.pxlo(), &rclColor, &gptlZero); vCalculateCursorBounds(psoMask, psoAlpha, prclBounds); } if ((psoColor != NULL) && (xloColor.pxlo()->flXlate & XO_TRIVIAL) && (psoColor->iBitmapFormat == BMF_32BPP)) { EngEraseSurface(psoAlpha, &rclAlpha, 0); bAlpha = bApplicationAlphaCursor(psoAlpha, psoColor, prclBounds); }
if ((!bAlpha) && (bShadow)) { EngEraseSurface(psoAlpha, &rclAlpha, 0);
bAlpha = bShadowAlphaCursor(psoAlpha, psoMask, psoColor, xloMono.pxlo(), xloColor.pxlo(), prclBounds); }
if (bAlpha) { SurfDimo.vKeepIt(); SurfDimo.vSetPID(OBJECT_OWNER_PUBLIC); *phbmAlpha = (HBITMAP) SurfDimo.ps->hsurf(); } } } } }
/******************************Public*Routine******************************\
* vSetPointer * * Set the cursor shape, position and hot spot. * * History: * Sat 25-Apr-1998 -by- J. Andrew Goossen [andrewgo] * Re-wrote it to handle sprites, shadows, and alpha cursors. * * Sun 09-Aug-1992 -by- Patrick Haluptzok [patrickh] * add engine pointer simulations, validate data from USER. * * Tue 12-May-1992 01:49:04 -by- Charles Whitmer [chuckwh] * Wrote it. \**************************************************************************/
VOID vSetPointer(HDEV hdev, PCURSINFO pci, ULONG fl, ULONG ulTrailLength, ULONG ulFreq) { BOOL bShadow = (fl & SPS_ALPHA); PDEVOBJ po(hdev);
ASSERTGDI(po.bValid() && !po.bMetaDriver(), "Invalid HDEV");
if (po.bDisabled()) return;
// Perhaps we're being told to tear the pointer down.
if (pci == (PCURSINFO) NULL) { if (po.bSoftwarePointer()) { EngSetPointerShape(po.pSurface()->pSurfobj(), NULL, NULL, NULL, 0, 0, 0, 0, NULL, 0); } if (po.bHardwarePointer()) { PPFNDRV(po, MovePointer)(po.pSurface()->pSurfobj(), -1, -1, NULL); }
po.bHardwarePointer(FALSE); po.bSoftwarePointer(FALSE); po.bMouseTrails(FALSE);
return; }
// OK, now we have some serious work to be done. We have to have a mask.
// Lock down and validate the cursor.
SURFREF soMask((HSURF) pci->hbmMask);
if (!soMask.bValid() || (soMask.ps->iFormat() != BMF_1BPP) || (soMask.ps->sizl().cy & 0x0001)) { WARNING("GreSetPointer failed because of weird cursor\n"); return; }
XEPALOBJ palSrc; XEPALOBJ palDisp; XEPALOBJ palDispDC(ppalDefault);
SURFACE *psurfColor = NULL; SURFACE *psurfAlpha = NULL; XLATEOBJ *pxlo = NULL; EXLATEOBJ xlo; SURFREF soColor; SURFREF soAlpha; PPALETTE ppalSrc; FLONG flDriver; RECTL* prclOpaqueBounds; RECTL rclAlphaBounds;
// We can reference pSurface() only once the Devlock is acquired,
// for dynamic mode changing.
SURFOBJ* psoDisplay = po.pSurface()->pSurfobj();
if (pci->hbmColor) { soColor.vAltCheckLock((HSURF) pci->hbmColor);
if (soColor.bValid()) { if (soColor.ps->sizl().cy != (soMask.ps->sizl().cy >> 1)) { WARNING("GreSetPointer failed color not half height mask\n"); return; }
// Handle the weird case where we get a compatible bitmap created
// for the parent:
ppalSrc = soColor.ps->ppal(); if ((ppalSrc == NULL) && (po.hdevParent() != po.hdev())) { PDEVOBJ poParent(po.hdevParent()); ppalSrc = poParent.ppalSurf(); }
if (!bIsCompatible(&ppalSrc, ppalSrc, soColor.ps, po.hdev())) { WARNING("GreSetPointer failed - bitmap not compatible with surface\n"); return; }
palSrc.ppalSet(ppalSrc); palDisp.ppalSet(po.ppalSurf());
if (xlo.bInitXlateObj(NULL, DC_ICM_OFF, palSrc, palDisp, palDispDC, palDispDC, 0x000000L, 0xFFFFFFL, 0)) { pxlo = xlo.pxlo(); psurfColor = soColor.ps; } } }
// If we've never seen this cursor before, compute the bounds and
// create the alpha shadow if necessary.
prclOpaqueBounds = (RECTL*) &pci->rcBounds; if ((prclOpaqueBounds->bottom == 0) || ((pci->CURSORF_flags & CURSORF_SHADOW) && !bShadow) || (!(pci->CURSORF_flags & CURSORF_SHADOW) && bShadow)) { vProcessCursorShape(hdev, bShadow, soMask.pSurfobj(), psurfColor->pSurfobj(), ppalSrc, (RECTL*) &pci->rcBounds, (HBITMAP*) &pci->hbmAlpha);
if (bShadow) { pci->CURSORF_flags |= CURSORF_SHADOW; } else { pci->CURSORF_flags &= ~CURSORF_SHADOW; } } // Ignore the alpha flag now and use the existence of 'hbmAlpha' to
// determine whether the cursor should use the pre-generated alpha
// surface.
fl &= ~SPS_ALPHA;
if ((pci->hbmAlpha != NULL) && (po.iDitherFormat() > BMF_8BPP)) { soAlpha.vAltCheckLock((HSURF) pci->hbmAlpha); psurfAlpha = soAlpha.ps;
rclAlphaBounds.left = pci->rcBounds.left; rclAlphaBounds.top = pci->rcBounds.top; rclAlphaBounds.right = pci->rcBounds.right + SHADOW_X_OFFSET + (2 * ALPHA_EDGE); rclAlphaBounds.bottom = pci->rcBounds.bottom + SHADOW_Y_OFFSET + (2 * ALPHA_EDGE);
// Trim off the outside edge on all sides, which was used only
// temporarily by the bBlurCursorShadow routine.
rclAlphaBounds.left++; rclAlphaBounds.top++; rclAlphaBounds.right--; rclAlphaBounds.bottom--; }
ULONG iMode; LONG xPointer; LONG yPointer; RECTL rclBoundsCopy; BOOL bHardwarePointer; BOOL bSoftwarePointer; BOOL bSynchronousPointer; BOOL bMouseTrails;
if (!po.bDisabled()) { po.ptlHotSpot(pci->xHotspot, pci->yHotspot);
xPointer = po.ptlPointer().x; yPointer = po.ptlPointer().y;
flDriver = SPS_CHANGE | (fl & (SPS_ANIMATESTART | SPS_ANIMATEUPDATE));
bHardwarePointer = FALSE; bSoftwarePointer = TRUE; bMouseTrails = FALSE; bSynchronousPointer = FALSE;
if(ulTrailLength != 0 && ulFreq != 0) { ulTrailLength = MIN(ulTrailLength, 16); ulFreq = MIN(ulFreq, 255); flDriver |= ((ulTrailLength << 8) & SPS_LENGTHMASK); flDriver |= ((ulFreq <<12) & SPS_FREQMASK); bMouseTrails = TRUE; }
if (PPFNVALID(po, SetPointerShape) && (!bMouseTrails || (po.flGraphicsCaps2() & GCAPS2_MOUSETRAILS)) ) { // Give the hardware the first crack at the alpha surface (if
// there is one).
if (psurfAlpha != NULL) { if (po.flGraphicsCaps2() & GCAPS2_ALPHACURSOR) { rclBoundsCopy = rclAlphaBounds; iMode = PPFNDRV(po, SetPointerShape) (psoDisplay, NULL, psurfAlpha->pSurfobj(), NULL, pci->xHotspot + ALPHA_EDGE, pci->yHotspot + ALPHA_EDGE, xPointer, yPointer, &rclBoundsCopy, flDriver | SPS_ALPHA);
// SPS_ACCEPT_EXCLUDE is obsolete ... force software
// iMode is now treated as a set of flags
if(iMode == SPS_ACCEPT_EXCLUDE) bHardwarePointer = FALSE; else bHardwarePointer = (iMode & SPS_ACCEPT_NOEXCLUDE ? TRUE : FALSE);
bSoftwarePointer = !bHardwarePointer;
bSynchronousPointer = (iMode & SPS_ACCEPT_SYNCHRONOUS ? TRUE : FALSE);
} } else { // Handle the normal, no-alpha, cursor case:
rclBoundsCopy = *prclOpaqueBounds; iMode = PPFNDRV(po, SetPointerShape) (psoDisplay, soMask.pSurfobj(), psurfColor->pSurfobj(), pxlo, pci->xHotspot, pci->yHotspot, xPointer, yPointer, &rclBoundsCopy, flDriver);
// Since the introduction of 'sprites', we no longer
// support SPS_ACCEPT_EXCLUDE.
if (iMode == SPS_ACCEPT_EXCLUDE) { PPFNDRV(po, MovePointer)(psoDisplay, -1, -1, NULL); iMode = SPS_DECLINE; }
bHardwarePointer = (iMode & SPS_ACCEPT_NOEXCLUDE ? TRUE : FALSE);
bSoftwarePointer = !bHardwarePointer;
bSynchronousPointer = (iMode & SPS_ACCEPT_SYNCHRONOUS ? TRUE : FALSE); } }
if (bSoftwarePointer) { if (psurfAlpha != NULL) { EngSetPointerShape(psoDisplay, NULL, psurfAlpha->pSurfobj(), NULL, pci->xHotspot + ALPHA_EDGE, pci->yHotspot + ALPHA_EDGE, xPointer, yPointer, &rclAlphaBounds, flDriver | SPS_ALPHA); } else { EngSetPointerShape(psoDisplay, soMask.pSurfobj(), psurfColor->pSurfobj(), pxlo, pci->xHotspot, pci->yHotspot, xPointer, yPointer, prclOpaqueBounds, flDriver); } }
if ((!bSoftwarePointer) && (po.bSoftwarePointer())) { // Turn off software cursor:
EngMovePointer(psoDisplay, -1, -1, NULL); } if ((!bHardwarePointer) && (po.bHardwarePointer())) { // Turn off hardware cursor:
PPFNDRV(po, MovePointer)(psoDisplay, -1, -1, NULL); }
po.bSoftwarePointer(bSoftwarePointer); po.bHardwarePointer(bHardwarePointer); po.bMouseTrails(bMouseTrails);
// Synchronous pointer flag forces synchronization of pointer even if
// GCAPS_ASYNCMOVE was specified allowing drivers to accept some pointers
// but have them drawn synchronously.
po.bSynchronousPointer(bSynchronousPointer); } }
/******************************Public*Routine******************************\
* vMovePointer * * Move the Pointer to the specified location. This is called only by * USER. * * History: * Thu 14-Apr-1994 -by- Patrick Haluptzok [patrickh] * Optimize / make Async pointers work * * See GreMovePointer for info on ulFlags * * Tue 12-May-1992 02:11:51 -by- Charles Whitmer [chuckwh] * Wrote it. \**************************************************************************/
VOID vMovePointer(HDEV hdev, int x, int y, LONG ulFlags) { RECTL *prcl; SURFOBJ *pso;
PDEVOBJ po(hdev); ASSERTGDI(po.bValid() && !po.bMetaDriver(), "Invalid HDEV");
// bDisabled can't change with hsemDevLock and hsemPointer both held.
// bPtrHidden can't change unless hsemPointer is held if the
// pointer is currently Async and the device supports Async movement.
if ((po.ptlPointer().x != x) || (po.ptlPointer().y != y) || po.bMouseTrails()) { po.ptlPointer(x, y); if (!po.bDisabled()) { pso = po.pSurface()->pSurfobj(); if (po.bHardwarePointer()) { if (!PPFNVALID(po, MovePointerEx)) { PPFNDRV(po, MovePointer)(pso, x, y, NULL); } else { // Use the Ex entry point to pass additional info
// to the driver. E.g. TS uses this to pass source
// information about mouse moves so that programmatic
// mouse moves that originate at the server are always
// sent down to the client.
PPFNDRV(po, MovePointerEx)(pso, x, y, ulFlags); } } if (po.bSoftwarePointer()) { EngMovePointer(pso, x, y, NULL); }
// Panning drivers want to see the pointer update even
// if the pointer isn't enabled, because the position
// is used to update the visible rectangle within the
// virtual desktop space.
//
// Note: There are cases where ntuser will turn off the
// cursor expecting us to ignore pointer moves.
// Unfortunately, this can happen during mode changes where
// ntuser may have a temporarily bigger desktop than
// physically exists in ntgdi. Therefore, we must check
// the pointer position against the virtual desktop bounds.
if ((po.flGraphicsCaps() & GCAPS_PANNING) && (y != -1) && (x < pso->sizlBitmap.cx) && (y < pso->sizlBitmap.cy)) { // The driver wants to be notified of the pointer
// position even if a pointer isn't actually visible
// (invisible panning!). Let it know the pointer is
// still turned off by giving it a negative 'y':
PPFNDRV(po, MovePointer)(pso, x, y - pso->sizlBitmap.cy, NULL); } } } }
/******************************Public*Routine******************************\
* GreSetPointer * * Set the cursor shape and hot spot. * \**************************************************************************/
VOID GreSetPointer(HDEV hdev, PCURSINFO pci, ULONG fl, ULONG ulTrailLength, ULONG ulFreq) { PVDEV pvdev; PDISPSURF pds; LONG csurf;
PDEVOBJ po(hdev);
DEVLOCKOBJ dlo(po); SEMOBJ so(po.hsemPointer());
// Just pass directly to 'bSetPointer' when not running multi-mon:
if (!po.bMetaDriver()) { vSetPointer(hdev, pci, fl, ulTrailLength, ulFreq); } else { // Okay, we've got work to do.
pvdev = (VDEV*) po.dhpdev(); pds = pvdev->pds; csurf = pvdev->cSurfaces;
do { vSetPointer(pds->hdev, pci, fl, ulTrailLength, ulFreq);
pds = pds->pdsNext; } while (--csurf); } }
/******************************Public*Routine******************************\
* GreMovePointer * * Set the cursor position. * * * ulFlags specified additional information about the move. * E.g. Termsrv uses this to determine when to send updates to the client. * * If you make a programmatic mouse move that originates at the server * use MP_PROCEDURAL * * \**************************************************************************/
VOID GreMovePointer(HDEV hdev, int x, int y, ULONG ulFlags) { GDIFunctionID(GreMovePointer);
PVDEV pvdev; PDISPSURF pds; LONG csurf; BOOL bUnlockBoth = FALSE;
PDEVOBJ po(hdev); // If the driver has indicated it has bAsyncPointerMove capabilities
// and it currently is managing the pointer, and the pointer is
// supported in hardware so it doesn't need to be excluded
// (indicated by bPtrNeedsExcluding) then we only need to grab the pointer
// mutex which is only grabbed by people trying to make the pointer
// shape change and a few other odd ball places.
//
// Otherwise we grab the DEVLOCK and the pointer mutex which
// ensures nobody else is drawing, changing the pointer shape,
// etc.
if (po.bAsyncPointerMove() && !po.bSoftwarePointer()) { //
// Note: We pass in NULL for parent semaphore here because we have
// no yet acquired the dev lock and thus under global ordering
// restrictions.
//
GreAcquireSemaphoreEx(po.hsemPointer(), SEMORDER_POINTER, NULL);
// Make sure we really got it, bAsyncPointerMove may change if you
// don't hold the DEVLOCK or the POINTER mutex.
if (!po.bAsyncPointerMove() || po.bSoftwarePointer()) { // Release and regrab everything, for sure we are safe with
// both of them.
GreReleaseSemaphoreEx(po.hsemPointer()); GreAcquireSemaphoreEx(po.hsemDevLock(), SEMORDER_DEVLOCK, NULL); GreAcquireSemaphoreEx(po.hsemPointer(), SEMORDER_POINTER, po.hsemDevLock()); bUnlockBoth = TRUE; } } else { GreAcquireSemaphoreEx(po.hsemDevLock(), SEMORDER_DEVLOCK, NULL); GreAcquireSemaphoreEx(po.hsemPointer(), SEMORDER_POINTER, po.hsemDevLock()); bUnlockBoth = TRUE; }
GreEnterMonitoredSection(po.ppdev, WD_POINTER);
// Just pass directly to 'vMovePointer' when not running multi-mon:
if (!po.bMetaDriver()) { vMovePointer(hdev, x, y, ulFlags); } else { // Okay, we've got work to do.
pvdev = (VDEV*) po.dhpdev(); pds = pvdev->pds; csurf = pvdev->cSurfaces; do { if ((x >= pds->rcl.left) && (x < pds->rcl.right) && (y >= pds->rcl.top) && (y < pds->rcl.bottom)) { vMovePointer(pds->hdev, x - pds->rcl.left, y - pds->rcl.top, ulFlags); } else { vMovePointer(pds->hdev, -1, -1, ulFlags); }
pds = pds->pdsNext; } while (--csurf); } po.ptlPointer(x, y);
GreExitMonitoredSection(po.ppdev, WD_POINTER); GreReleaseSemaphoreEx(po.hsemPointer()); if (bUnlockBoth) { GreReleaseSemaphoreEx(po.hsemDevLock()); } }
/******************************Public*Routine******************************\
* EngSetPointerTag * * This is the engine entry point that allows device drivers to create a * 'tag' that will be combined with the pointer shape whenever it's sent * to other drivers in the DDML chain via the DrvSetPointerShape call. * * This functionality is primarily intended to allow remoting DDML drivers * to allow a name be added to the pointer to signify who owns 'control' of * the machine's input. But they don't want to transmit the shape with the * name over the wire, which is why we adopt the convention that this applies * to every *other* device in the DDML chain. * * History: * 20-Oct-1996 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
BOOL EngSetPointerTag( HDEV hdev, // Identifies device's PDEV. This is not the MDEV!
SURFOBJ* psoInputMask, // May be NULL when resetting the tag
SURFOBJ* psoInputColor, // Will be NULL if monochrome
XLATEOBJ* pxlo, // Reserved, must be NULL for now
FLONG fl) // Reserved, must be zero for now
{ // We shipped NT4 SP3 with this functionality, but it's obsolete as
// of NT5.
return(FALSE); }
/******************************Public*Routine******************************\
* DEVLOCKOBJ::bLock * * Device locking object. Optionally computes the Rao region. * * History: * Sun 30-Aug-1992 -by- Patrick Haluptzok [patrickh] * change to boolean return * * Mon 27-Apr-1992 22:46:41 -by- Charles Whitmer [chuckwh] * Clean up again. * * Tue 16-Jul-1991 -by- Patrick Haluptzok [patrickh] * Clean up. * * 15-Sep-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL DEVLOCKOBJ::bLock(XDCOBJ& dco) { GDIFunctionID(DEVLOCKOBJ::bLock);
hsemTrg = NULL; // Remember the semaphore we're waiting on.
ppdevTrg = NULL; // Remember pdev we're monitoring.
fl = DLO_VALID; // Remember if it is valid.
// We lock the semphore on direct display DCs and DFB's if
// the device has set GCAPS_SYNCHRONIZEACCESS set.
if (dco.bSynchronizeAccess()) { // make sure we don't have any wrong sequence of acquiring locks
// should always acquire a DEVLOCK before we have the palette semaphore
ASSERTGDI ( (!GreIsSemaphoreOwnedByCurrentThread(ghsemPalette)) || (GreIsSemaphoreOwnedByCurrentThread(dco.hsemDcDevLock())), "potential deadlock!\n");
// Grab the display semaphore
if(dco.bShareAccess()) { GreAcquireSemaphoreShared(ghsemShareDevLock); fl |= DLO_SHAREDACCESS; } else { hsemTrg = dco.hsemDcDevLock(); ppdevTrg = dco.pdc->ppdev(); GreAcquireSemaphoreEx(hsemTrg, SEMORDER_DEVLOCK, NULL); GreEnterMonitoredSection(ppdevTrg, WD_DEVLOCK); }
if (dco.pdc->bInFullScreen()) { fl &= ~(DLO_VALID); return(FALSE); } }
// Compute the new Rao region if it's dirty.
if (dco.pdc->bDirtyRao()) { if (!dco.pdc->bCompute()) { fl &= ~(DLO_VALID); return(FALSE); } }
return(TRUE); }
/******************************Public*Routine******************************\
* DEVLOCKOBJ::vLockNoDrawing * * Device locking object for when no drawing will take place. * * Used primarily to protect against dynamic mode changing when looking at * surface fields. Because no drawing will take place, the rao region * computations and full-screen checks need not be made. * * History: * Thu 8-Feb-1996 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/
VOID DEVLOCKOBJ::vLockNoDrawing(XDCOBJ& dco) { GDIFunctionID(DEVLOCKOBJ::vLockNoDrawing);
hsemTrg = NULL; // Remember the semaphore we're waiting on.
ppdevTrg = NULL; // Remember pdev we're monitoring.
fl = DLO_VALID; // Remember if it is valid.
// We lock display DC's even if bSynchronizeAccess() isn't set so that
// the surface palette will still be locked down even for device-
// dependent-bitmaps.
PDEVOBJ po(dco.hdev());
if (po.bDisplayPDEV()) { // Grab the display semaphore
hsemTrg = dco.hsemDcDevLock(); ppdevTrg = dco.pdc->ppdev(); GreAcquireSemaphoreEx(hsemTrg, SEMORDER_DEVLOCK, NULL); GreEnterMonitoredSection(ppdevTrg, WD_DEVLOCK); } }
/******************************Public*Routine******************************\
* DEVLOCKBLTOBJ::bLock * * Device locking object. Optionally computes the Rao region. * * History: * Sun 30-Aug-1992 -by- Patrick Haluptzok [patrickh] * change to boolean return * * Mon 27-Apr-1992 22:46:41 -by- Charles Whitmer [chuckwh] * Clean up again. * * Tue 16-Jul-1991 -by- Patrick Haluptzok [patrickh] * Clean up. * * 15-Sep-1990 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL DEVLOCKBLTOBJ::bLock(XDCOBJ& dco) { GDIFunctionID(DEVLOCKBLTOBJ::bLock);
hsemTrg = NULL; // Remember the semaphore we're waiting on.
hsemSrc = NULL; // Remember the semaphore we're waiting on.
ppdevTrg = NULL; // Remember pdev we're monitoring.
ppdevSrc = NULL; // Remember pdev we're monitoring.
fl = DLO_VALID; // Remember if it is valid.
// We lock the semphore on direct display DCs and DFB's if
// the device has set GCAPS_SYNCHRONIZEACCESS set.
if (dco.bSynchronizeAccess()) { // Grab the display semaphore
if(dco.bShareAccess()) { GreAcquireSemaphoreShared(ghsemShareDevLock); fl |= DLO_SHAREDACCESS; } else { hsemTrg = dco.hsemDcDevLock(); ppdevTrg = dco.pdc->ppdev(); GreAcquireSemaphoreEx(hsemTrg, SEMORDER_DEVLOCK, NULL); GreEnterMonitoredSection(ppdevTrg, WD_DEVLOCK); }
// Check if we are in full screen and drawing
// to the Display, this may just be a DFB.
if (dco.bInFullScreen()) { fl &= ~(DLO_VALID); return(FALSE); } }
// Compute the new Rao region if it's dirty.
if (dco.pdc->bDirtyRao()) { if (!dco.pdc->bCompute()) { fl &= ~(DLO_VALID); return(FALSE); } }
return(TRUE); }
/******************************Public*Routine******************************\
* DEVLOCKBLTOBJ::bLock * * Lock both a source and target DC. Used by StretchBlt, PlgBlt and such. * * We must check to see if we are in full screen and fail if we are. * * History: * Mon 18-Apr-1994 -by- Patrick Haluptzok [patrickh] * bSynchronize Checks * * 16-Feb-1993 -by- Eric Kutter [erick] * Added full screen checks * * 11-Nov-1992 -by- Donald Sidoroff [donalds] * Wrote it. \**************************************************************************/
BOOL DEVLOCKBLTOBJ::bLock(XDCOBJ& dcoTrg, XDCOBJ& dcoSrc) { GDIFunctionID(DEVLOCKBLTOBJ::bLock);
hsemTrg = NULL; hsemSrc = NULL; ppdevTrg = NULL; // Remember pdev we're monitoring.
ppdevSrc = NULL; // Remember pdev we're monitoring.
fl = DLO_VALID;
if(dcoSrc.bShareAccess() || dcoTrg.bShareAccess()) { GreAcquireSemaphoreShared(ghsemShareDevLock); fl |= DLO_SHAREDACCESS; }
if (dcoSrc.bSynchronizeAccess()) { // Grab the display semaphore
if(!dcoSrc.bShareAccess()) { hsemSrc = dcoSrc.hsemDcDevLock(); ppdevSrc = dcoSrc.pdc->ppdev(); GreAcquireSemaphoreEx(hsemSrc, SEMORDER_DEVLOCK, NULL); GreEnterMonitoredSection(ppdevSrc, WD_DEVLOCK); }
// Check if we are in full screen and drawing
// to the Display, this may just be a DFB.
if (dcoSrc.bInFullScreen()) { fl &= ~(DLO_VALID); return(FALSE); } }
if (dcoTrg.bSynchronizeAccess()) { // Grab the display semaphore
if(!dcoTrg.bShareAccess()) { hsemTrg = dcoTrg.hsemDcDevLock(); ppdevTrg = dcoTrg.pdc->ppdev(); GreAcquireSemaphoreEx(hsemTrg, SEMORDER_DEVLOCK, NULL); GreEnterMonitoredSection(ppdevTrg, WD_DEVLOCK); }
// Check if we are in full screen and drawing
// to the Display, this may just be a DFB.
if (dcoTrg.bInFullScreen()) { fl = 0; return(FALSE); } }
// Compute the new Rao regions.
if (dcoTrg.pdc->bDirtyRao()) { if (!dcoTrg.pdc->bCompute()) { fl &= ~(DLO_VALID); return(FALSE); } }
if (dcoSrc.pdc->bDirtyRao()) { if (!dcoSrc.pdc->bCompute()) { fl &= ~(DLO_VALID); return(FALSE); } }
return(TRUE); }
|