// // CM.C // Cursor Manager // // Copyright(c) 1997- // #include // // 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); }