|
|
//
// CM.C
// Cursor Manager
//
// Copyright(c) 1997-
//
#include <as16.h>
//
// CM_DDProcessRequest()
// Handles CM escapes
//
BOOL CM_DDProcessRequest ( UINT fnEscape, LPOSI_ESCAPE_HEADER pResult, DWORD cbResult ) { BOOL rc;
DebugEntry(CM_DDProcessRequest);
switch (fnEscape) { case CM_ESC_XFORM: { ASSERT(cbResult == sizeof(CM_DRV_XFORM_INFO)); ((LPCM_DRV_XFORM_INFO)pResult)->result = CMDDSetTransform((LPCM_DRV_XFORM_INFO)pResult); rc = TRUE; } break;
default: { ERROR_OUT(("Unrecognized CM_ escape")); rc = FALSE; } break; }
DebugExitBOOL(CM_DDProcessRequest, rc); return(rc); }
//
// CM_DDInit()
//
BOOL CM_DDInit(HDC hdcScreen) { BOOL rc = FALSE; HGLOBAL hg; LPBYTE lpfnPatch;
DebugEntry(CM_DDInit);
//
// Get the size of the cursor
//
g_cxCursor = GetSystemMetrics(SM_CXCURSOR); g_cyCursor = GetSystemMetrics(SM_CYCURSOR);
//
// Create our work bit buffers
//
g_cmMonoByteSize = BitmapSize(g_cxCursor, g_cyCursor, 1, 1); g_cmColorByteSize = BitmapSize(g_cxCursor, g_cyCursor, g_osiScreenPlanes, g_osiScreenBitsPlane);
// This will hold a color cursor, mono is always <= to this
hg = GlobalAlloc(GMEM_FIXED | GMEM_SHARE, sizeof(CURSORSHAPE) + g_cmMonoByteSize + g_cmColorByteSize); g_cmMungedCursor = MAKELP(hg, 0);
// Always alloc mono Xform
hg = GlobalAlloc(GMEM_FIXED | GMEM_SHARE, 2 * g_cmMonoByteSize); g_cmXformMono = MAKELP(hg, 0);
if (!SELECTOROF(g_cmMungedCursor) || !SELECTOROF(g_cmXformMono)) { ERROR_OUT(("Couldn't allocate cursor xform buffers")); DC_QUIT; }
lpfnPatch = (LPBYTE)g_lpfnSetCursor;
// If color cursors supported, alloc color image bits, again 2x the size
if (GetDeviceCaps(hdcScreen, CAPS1) & C1_COLORCURSOR) { hg = GlobalAlloc(GMEM_FIXED | GMEM_SHARE, 2 * g_cmColorByteSize); if (!hg) { ERROR_OUT(("Couldn't allocate color cursor xform buffer")); DC_QUIT; }
g_cmXformColor = MAKELP(hg, 0); } else { //
// Older drivers (VGA and SUPERVGA e.g.) hook int2f and read their
// DS from the SetCursor ddi prolog code, in many places. Therefore,
// if we patch over this instruction, they will blow up. For these
// drivers, we patch 3 bytes after the start, which leaves
// mov ax, DGROUP
// intact and is harmless. When we call the original routine, we call
// back to the beginning, which will set up ax again before the body
// of the ddi code.
//
// NOTE:
// We use the color cursor caps for this detection. DRIVERVERSION
// doesn't work, VGA et al. got restamped in Win95. This is the
// most reliable way to decide if this is an older driver or not.
//
// NOTE 2:
// We still want to decode this routine to see if it is of the form
// mov ax, xxxx. If not, patch at the front anyway, or we'll write
// possibly into the middle of an instruction.
//
if (*lpfnPatch == OPCODE_MOVAX) lpfnPatch = lpfnPatch + 3; }
if (!CreateFnPatch(lpfnPatch, DrvSetPointerShape, &g_cmSetCursorPatch, 0)) { ERROR_OUT(("Couldn't get cursor routines")); DC_QUIT; }
g_cmSetCursorPatch.fInterruptable = TRUE;
rc = TRUE;
DC_EXIT_POINT: DebugExitBOOL(CM_DDInit, rc); return(rc); }
//
// CM_DDTerm()
//
void CM_DDTerm(void) { DebugEntry(CM_DDTerm);
//
// Clean up our patches
//
DestroyFnPatch(&g_cmSetCursorPatch);
g_cmXformOn = FALSE; g_cmCursorHidden = FALSE; //
// Free our memory blocks.
//
if (SELECTOROF(g_cmXformColor)) { GlobalFree((HGLOBAL)SELECTOROF(g_cmXformColor)); g_cmXformColor = NULL; }
if (SELECTOROF(g_cmXformMono)) { GlobalFree((HGLOBAL)SELECTOROF(g_cmXformMono)); g_cmXformMono = NULL; }
if (SELECTOROF(g_cmMungedCursor)) { GlobalFree((HGLOBAL)SELECTOROF(g_cmMungedCursor)); g_cmMungedCursor = NULL; }
DebugExitVOID(CM_DDTerm); }
//
// CMDDSetTransform()
//
BOOL CMDDSetTransform(LPCM_DRV_XFORM_INFO pResult) { BOOL rc = FALSE; LPBYTE lpAND;
DebugEntry(CMDDSetTransform);
//
// Turn off transform
//
// Do this first--that way if an interrupt comes in, we won't apply
// some half-copied xform to the cursor. This can only happen for
// an anicur. We jiggle the cursor below, which will reset the
// xform if necessary.
//
g_cmXformOn = FALSE;
//
// If AND bitmap is NULL, we are turning the transform off. We also
// do this if we can't get a 16:16 pointer to this memory
//
if (pResult->pANDMask == 0) { TRACE_OUT(("Clear transform")); rc = TRUE; } else { ASSERT(pResult->width == g_cxCursor); ASSERT(pResult->height == g_cyCursor); lpAND = MapLS(pResult->pANDMask); if (!SELECTOROF(lpAND)) { ERROR_OUT(("Couldn't get AND mask pointer")); DC_QUIT; }
hmemcpy(g_cmXformMono, lpAND, 2 * g_cmMonoByteSize); UnMapLS(lpAND);
if (SELECTOROF(g_cmXformColor)) { HBITMAP hbmMono = NULL; HBITMAP hbmMonoOld; HBITMAP hbmColorOld; HBITMAP hbmColor = NULL; HDC hdcMono = NULL; HDC hdcColor = NULL;
//
// Get color expanded version of the mask & image.
// We do this blting the mono bitmap into a color one, then
// getting the color bits.
//
hdcColor = CreateCompatibleDC(g_osiScreenDC); hbmColor = CreateCompatibleBitmap(g_osiScreenDC, g_cxCursor, 2*g_cyCursor);
if (!hdcColor || !hbmColor) goto ColorError;
hbmColorOld = SelectBitmap(hdcColor, hbmColor);
hdcMono = CreateCompatibleDC(hdcColor); hbmMono = CreateBitmap(g_cxCursor, 2*g_cyCursor, 1, 1, g_cmXformMono); hbmMonoOld = SelectBitmap(hdcMono, hbmMono);
if (!hdcMono || !hbmMono) goto ColorError;
//
// The defaults should be black & white for the text/back
// colors, since we just created these DCs
//
ASSERT(GetBkColor(hdcColor) == RGB(255, 255, 255)); ASSERT(GetTextColor(hdcColor) == RGB(0, 0, 0));
ASSERT(GetBkColor(hdcMono) == RGB(255, 255, 255)); ASSERT(GetTextColor(hdcMono) == RGB(0, 0, 0));
BitBlt(hdcColor, 0, 0, g_cxCursor, 2*g_cyCursor, hdcMono, 0, 0, SRCCOPY);
GetBitmapBits(hbmColor, 2*g_cmColorByteSize, g_cmXformColor);
g_cmXformOn = TRUE;
ColorError: if (hbmColor) { SelectBitmap(hdcColor, hbmColorOld); DeleteBitmap(hbmColor); } if (hdcColor) { DeleteDC(hdcColor); }
if (hbmMono) { SelectBitmap(hdcMono, hbmMonoOld); DeleteBitmap(hbmMono); } if (hdcMono) { DeleteDC(hdcMono); } } else g_cmXformOn = TRUE;
rc = (g_cmXformOn != 0); }
DC_EXIT_POINT: //
// Jiggle the cursor to get it to redraw with the new transform
//
CMDDJiggleCursor();
DebugExitBOOL(CMDDSetTransform, rc); return(rc); }
//
// CM_DDViewing()
//
// We install our hooks & jiggle the cursor, if starting.
// We remove our hooks, if stopping.
//
void CM_DDViewing(BOOL fViewers) { DebugEntry(CM_DDViewing);
//
// SetCursor() can be called at interrupt time for animated cursors.
// Fortunately, we don't have to really pagelock the data segments
// we touch. Animated cursors aren't allowed when you page through DOS.
// When paging in protected mode, the guts of Windows can handle
// page-ins during 16-bit ring3 reflected interrupts. Therfore
// GlobalFix() works just fine.
//
if (fViewers) { // Do this BEFORE enabling patch
GlobalFix(g_hInstAs16); GlobalFix((HGLOBAL)SELECTOROF((LPBYTE)DrvSetPointerShape));
GlobalFix((HGLOBAL)SELECTOROF(g_cmMungedCursor)); GlobalFix((HGLOBAL)SELECTOROF(g_cmXformMono));
if (SELECTOROF(g_cmXformColor)) GlobalFix((HGLOBAL)SELECTOROF(g_cmXformColor));
}
//
// This enable will disable interrupts while copying bytes back and
// forth. Animated cursors draw at interrupt time, and one could
// come in while we're in the middle of copying the patch. The code
// would blow up on half-copied instructions.
//
EnableFnPatch(&g_cmSetCursorPatch, (fViewers ? PATCH_ACTIVATE : PATCH_DEACTIVATE));
if (!fViewers) { // Do this AFTER disabling patch
if (SELECTOROF(g_cmXformColor)) GlobalUnfix((HGLOBAL)SELECTOROF(g_cmXformColor)); GlobalUnfix((HGLOBAL)SELECTOROF(g_cmXformMono)); GlobalUnfix((HGLOBAL)SELECTOROF(g_cmMungedCursor));
GlobalUnfix((HGLOBAL)SELECTOROF((LPBYTE)DrvSetPointerShape)); GlobalUnfix(g_hInstAs16); } else { //
// Jiggle the cursor to get the current image
//
CMDDJiggleCursor(); }
DebugExitVOID(CM_DDViewing); }
//
// CMDDJiggleCursor()
// This causes the cursor to redraw with/without our tag.
//
void CMDDJiggleCursor(void) { DebugEntry(CMDDJiggleCursor);
if (g_asSharedMemory) { //
// Toggle full screen via WinOldAppHackOMatic(). This is the most
// innocuous way I can come up with to force USER to refresh the
// cursor with all the right parameters.
//
// If a full screen dos box is currently up, we don't need to do
// anything--the user doesn't have a cursor, and the cursor will
// refesh when we go back to windows mode anyway.
//
// Sometimes 16-bit code is beautiful! We own the win16lock,
// so the two function calls below are atomic, and we know USER
// won't do any calculation that would check the fullscreen state
// while we're in the middle.
//
if (!g_asSharedMemory->fullScreen) { WinOldAppHackoMatic(WOAHACK_LOSINGDISPLAYFOCUS); WinOldAppHackoMatic(WOAHACK_GAININGDISPLAYFOCUS); } }
DebugExitVOID(CMDDJiggleCursor); }
//
// DrvSetPointerShape()
// This is the intercept for the display driver's SetCursor routine.
//
// NOTE THAT THIS CAN BE CALLED AT INTERRUPT TIME FOR ANIMATED CURSORS.
//
// While we can access our data (interrupt calls only happen when NOT
// paging thru DOS, and protected mode paging can take pagefaults in ring3
// reflected interrupt code), we can not call kernel routines that might
// access non-fixed things.
//
// THIS MEANS NO DEBUG TRACING AT ALL IN THIS FUNCTION. AND NO CALLS TO
// HMEMCPY.
//
// We must preserve EDX. Memphis display drivers get passed an instance
// value from USER in this register. We only trash DX, so that's all we
// need to save.
//
#pragma optimize("gle", off)
BOOL WINAPI DrvSetPointerShape(LPCURSORSHAPE lpcur) { UINT dxSave; BOOL rc; UINT i; LPDWORD lpDst; LPDWORD lpSrc; LPCURSORSHAPE lpcurNew; LPCM_FAST_DATA lpcmShared;
_asm mov dxSave, dx
//
// Call the original entry point in the driver with the xformed bits
// NOTE:
// For VGA/SUPERVGA et al, we patch at SetCursor+3 to leave the
// move ax, dgroup instruction intact. We call through the org
// routine to get ax reset up.
//
EnableFnPatch(&g_cmSetCursorPatch, PATCH_DISABLE);
lpcurNew = XformCursorBits(lpcur);
_asm mov dx, dxSave rc = g_lpfnSetCursor(lpcurNew);
EnableFnPatch(&g_cmSetCursorPatch, PATCH_ENABLE);
//
// Did it succeed?
//
if (!rc) DC_QUIT;
//
// Hiding the cursor is done on Win95 by calling with NULL
//
if (!SELECTOROF(lpcur)) { if (!g_cmCursorHidden) { CM_SHM_START_WRITING; g_asSharedMemory->cmCursorHidden = TRUE; CM_SHM_STOP_WRITING;
g_cmCursorHidden = TRUE; } } else { // Set the bits first, THEN show the cursor to avoid flicker
lpcmShared = CM_SHM_START_WRITING;
//
// NOTE: if this isn't the right size or a recognizable color
// format, set a NULL cursor. This should never happen, but Win95's
// own display driver has checks for it, and if it does we'll blue
// screen if we do nothing.
//
if ((lpcur->cx != g_cxCursor) || (lpcur->cy != g_cyCursor) || ((lpcur->BitsPixel != 1) && (lpcur->BitsPixel != g_osiScreenBitsPlane)) || ((lpcur->Planes != 1) && (lpcur->Planes != g_osiScreenPlanes))) { // Set 'null' cursor
lpcmShared->cmCursorShapeData.hdr.cPlanes = 0xFF; lpcmShared->cmCursorShapeData.hdr.cBitsPerPel = 0xFF; goto CursorDone; }
lpcmShared->cmCursorShapeData.hdr.ptHotSpot.x = lpcur->xHotSpot; lpcmShared->cmCursorShapeData.hdr.ptHotSpot.y = lpcur->yHotSpot; lpcmShared->cmCursorShapeData.hdr.cx = lpcur->cx; lpcmShared->cmCursorShapeData.hdr.cy = lpcur->cy; lpcmShared->cmCursorShapeData.hdr.cPlanes = lpcur->Planes; lpcmShared->cmCursorShapeData.hdr.cBitsPerPel = lpcur->BitsPixel; lpcmShared->cmCursorShapeData.hdr.cbRowWidth = lpcur->cbWidth;
//
// Can't call hmemcpy at interrupt time. So we copy a DWORD
// at a time.
//
// LAURABU: NM 2.0 did this too. But maybe we should right this
// in ASM for speed...
//
i = BitmapSize(lpcur->cx, lpcur->cy, 1, 1) + BitmapSize(lpcur->cx, lpcur->cy, lpcur->Planes, lpcur->BitsPixel); i >>= 2;
lpDst = (LPDWORD)lpcmShared->cmCursorShapeData.data; lpSrc = (LPDWORD)(lpcur+1);
while (i-- > 0) { *(lpDst++) = *(lpSrc++); }
if ((lpcur->Planes == 1) && (lpcur->BitsPixel == 1)) { //
// Mono color table
//
lpcmShared->colorTable[0].peRed = 0; lpcmShared->colorTable[0].peGreen = 0; lpcmShared->colorTable[0].peBlue = 0; lpcmShared->colorTable[0].peFlags = 0;
lpcmShared->colorTable[1].peRed = 255; lpcmShared->colorTable[1].peGreen = 255; lpcmShared->colorTable[1].peBlue = 255; lpcmShared->colorTable[1].peFlags = 0; } else if (g_osiScreenBPP <= 8) { UINT iBase;
//
// Color cursors for this depth only use VGA colors. So fill
// in LOW 8 and HIGH 8, skip rest. There will be garbage in
// the middle 256-16 colors for 256 color cursors, but none
// of those RGBs are referenced in the bitmap data.
//
for (i = 0; i < 8; i++) { lpcmShared->colorTable[i] = g_osiVgaPalette[i]; }
if (g_osiScreenBPP == 4) iBase = 8; else iBase = 0xF8;
for (i = 0; i < 8; i++) { lpcmShared->colorTable[i + iBase] = g_osiVgaPalette[i + 8]; } } else { lpcmShared->bitmasks[0] = g_osiScreenRedMask; lpcmShared->bitmasks[1] = g_osiScreenGreenMask; lpcmShared->bitmasks[2] = g_osiScreenBlueMask; }
CursorDone: lpcmShared->cmCursorStamp = g_cmNextCursorStamp++;
if (g_cmCursorHidden) { g_asSharedMemory->cmCursorHidden = FALSE; g_cmCursorHidden = FALSE; }
CM_SHM_STOP_WRITING; }
DC_EXIT_POINT: return(rc); } #pragma optimize("", on)
//
// XformCursorBits()
// This routine copies and transforms the cursor bits at the same time.
// We return either the same thing passed in (if we can't xform it) or
// our temp buffer g_cmXformMono.
//
LPCURSORSHAPE XformCursorBits ( LPCURSORSHAPE lpOrg ) { LPCURSORSHAPE lpResult; LPDWORD lpDst; LPDWORD lpSrc; LPDWORD lpXform; UINT cDwords; BOOL fColor;
lpResult = lpOrg;
//
// If no xform is on, bail out
//
if (!g_cmXformOn || !SELECTOROF(lpOrg)) DC_QUIT;
//
// If the cursor isn't the right size, forget it.
//
if ((lpOrg->cx != g_cxCursor) || (lpOrg->cy != g_cyCursor)) DC_QUIT;
//
// If the cursor isn't monochrome or the color depth of the display,
// forget it.
//
if ((lpOrg->Planes == 1) && (lpOrg->BitsPixel == 1)) { // We handle this
fColor = FALSE; } else if ((lpOrg->Planes == g_osiScreenPlanes) && (lpOrg->BitsPixel == g_osiScreenBitsPlane)) { // We handle this
fColor = TRUE; } else { // Unrecognized
DC_QUIT; }
//
// OK, we can handle this
//
lpResult = g_cmMungedCursor;
//
// COPY THE HEADER
//
*lpResult = *lpOrg;
//
// FIRST:
// AND the two masks together (both are mono)
//
lpDst = (LPDWORD)(lpResult+1); lpSrc = (LPDWORD)(lpOrg+1); lpXform = (LPDWORD)g_cmXformMono;
cDwords = g_cmMonoByteSize >> 2; while (cDwords-- > 0) { *lpDst = (*lpSrc) & (*lpXform);
lpDst++; lpSrc++; lpXform++; }
//
// SECOND:
// AND the mask of the xform with the image of the cursor. If the
// cursor is color, use the color-expanded mask of the xform
//
if (fColor) { lpXform = (LPDWORD)g_cmXformColor; cDwords = g_cmColorByteSize; } else { lpXform = (LPDWORD)g_cmXformMono; cDwords = g_cmMonoByteSize; } cDwords >>= 2;
while (cDwords-- > 0) { *lpDst = (*lpSrc) & (*lpXform);
lpDst++; lpSrc++; lpXform++; }
//
// LAST:
// XOR the image of the xform with the image of the cursor
//
if (fColor) { lpXform = (LPDWORD)(g_cmXformColor + g_cmColorByteSize); cDwords = g_cmColorByteSize; } else { lpXform = (LPDWORD)(g_cmXformMono + g_cmMonoByteSize); cDwords = g_cmMonoByteSize; } cDwords >>= 2;
lpDst = (LPDWORD)((LPBYTE)(lpResult+1) + g_cmMonoByteSize);
while (cDwords-- > 0) { *lpDst ^= (*lpXform);
lpDst++; lpXform++; }
DC_EXIT_POINT: return(lpResult); }
|