/****************************** Module Header ******************************\ * Module Name: acons.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * This module contains code for dealing with animated icons/cursors. * * History: * 10-02-91 DarrinM Created. * 07-30-92 DarrinM Unicodized. \***************************************************************************/ #include "precomp.h" #pragma hdrstop /***************************************************************************\ * zzzSetSystemCursor (API) * * Replace a system (aka 'public') cursor with a user provided one. The new * cursor is pulled from a file (.CUR, .ICO, or .ANI) specified in WIN.INI. * * History: * 12/26/1991 DarrinM Created. * 08/04/1992 DarrinM Recreated. * 10/14/1995 SanfordS Win95 support. \***************************************************************************/ BOOL zzzSetSystemCursor( PCURSOR pcur, DWORD id) { int i; if (!CheckWinstaWriteAttributesAccess()) { return FALSE; } UserAssert(pcur); /* * Check if this cursor is one of the replaceable ones. */ for (i = 0; i < COCR_CONFIGURABLE; i++) { if (gasyscur[i].Id == (WORD)id) { break; } } /* * Not replaceable, bail out. */ if (i == COCR_CONFIGURABLE) { RIPMSG1(RIP_WARNING, "_SetSystemCursor: called with bad id 0x%x", id); return FALSE; } return zzzSetSystemImage(pcur, gasyscur[i].spcur); } /***********************************************************************\ * zzzSetSystemImage * * Places the contents of pcur into pcurSys and destroys pcur. * * 10/14/1995 Created SanfordS \***********************************************************************/ BOOL zzzSetSystemImage( PCURSOR pcur, PCURSOR pcurSys) { #define CBCOPY (max(sizeof(CURSOR), sizeof(ACON)) - FIELD_OFFSET(CURSOR, CI_COPYSTART)) #define pacon ((PACON)pcur) char cbT[CBCOPY]; UINT CURSORF_flags; UserAssert(pcurSys); if (pcurSys == pcur) { return TRUE; } /* * All ssytem images being replaced should have ordinal names * and reference the USER module and be unowned. */ UserAssert(!IS_PTR(pcurSys->strName.Buffer)); UserAssert(pcurSys->atomModName == atomUSER32); /* * if pcur was an acon, transfer frame ownerships to pcurSys. */ UserAssert(pcurSys->head.ppi == NULL); if (pcur->CURSORF_flags & CURSORF_ACON && pcur->head.ppi != NULL) { int i; PHE phe = HMPheFromObject(pcurSys); PTHREADINFO ptiOwner = ((PPROCESSINFO)phe->pOwner)->ptiList; for (i = 0; i < pacon->cpcur; i++) { HMChangeOwnerProcess(pacon->aspcur[i], ptiOwner); pacon->aspcur[i]->head.ppi = NULL; } } /* * If this assert fails, the CURSOR and ACON structures were changed * incorrectly - read the comments in user.h and wingdi.w around * tagCURSOR, tagACON, and CURSINFO. */ UserAssert(FIELD_OFFSET(CURSOR, CI_FIRST) == FIELD_OFFSET(ACON, CI_FIRST)); /* * swap everything starting from CI_COPYSTART. */ RtlCopyMemory(cbT, &pcur->CI_COPYSTART, CBCOPY); RtlCopyMemory(&pcur->CI_COPYSTART, &pcurSys->CI_COPYSTART, CBCOPY); RtlCopyMemory(&pcurSys->CI_COPYSTART, cbT, CBCOPY); /* * Swap the CURSORF_ACON flags since they go with the swapped data. */ CURSORF_flags = pcur->CURSORF_flags & CURSORF_ACON; pcur->CURSORF_flags = (pcur->CURSORF_flags & ~CURSORF_ACON) | (pcurSys->CURSORF_flags & CURSORF_ACON); pcurSys->CURSORF_flags = (pcurSys->CURSORF_flags & ~CURSORF_ACON) | CURSORF_flags; /* * If we swapped acons into pcur, then we need to change the ownerhsip to * make sure they can get destroyed. */ if (pcur->CURSORF_flags & CURSORF_ACON) { int i; PTHREADINFO ptiCurrent = PtiCurrent(); for (i = 0; i < pacon->cpcur; i++) { HMChangeOwnerProcess(pacon->aspcur[i], ptiCurrent); } } /* * Use THREADCLEANUP so system cursors are not destroyed. */ _DestroyCursor(pcur, CURSOR_THREADCLEANUP); /* * If the current logical cursor is changing then force the current physical * cursor to change. */ if (gpcurLogCurrent == pcurSys) { gpcurLogCurrent = NULL; gpcurPhysCurrent = NULL; zzzUpdateCursorImage(); } /* * Mark the cursor as a system cursor that can be shadowed by GDI. */ pcurSys->CURSORF_flags |= CURSORF_SYSTEM; return TRUE; #undef pacon #undef CBCOPY } /***************************************************************************\ * _GetCursorFrameInfo (API) * * Example usage: * * hcur = _GetCursorFrameInfo(hacon, NULL, 4, &ccur); * hcur = _GetCursorFrameInfo(NULL, IDC_NORMAL, 0, &ccur); // get device's arrow * * History: * 08-05-92 DarrinM Created. \***************************************************************************/ PCURSOR _GetCursorFrameInfo( PCURSOR pcur, int iFrame, PJIF pjifRate, LPINT pccur) { /* * If this is only a single cursor (not an ACON) just return it and * a frame count of 1. */ if (!(pcur->CURSORF_flags & CURSORF_ACON)) { *pccur = 1; *pjifRate = 0; return pcur; } /* * Return the useful cursor information for the specified frame * of the ACON. */ #define pacon ((PACON)pcur) if (iFrame < 0 || iFrame >= pacon->cicur) { return NULL; } *pccur = pacon->cicur; *pjifRate = pacon->ajifRate[iFrame]; return pacon->aspcur[pacon->aicur[iFrame]]; #undef pacon } /***************************************************************************\ * DestroyAniIcon * * Free all the individual cursors that make up the frames of an animated * icon. * * WARNING: DestroyAniIcon assumes that all fields that an ACON shares with * a cursor will be freed by some cursor code (probably the cursor function * that calls this one). * * History: * 08-04-92 DarrinM Created. \***************************************************************************/ BOOL DestroyAniIcon( PACON pacon) { int i; PCURSOR pcur; for (i = 0; i < pacon->cpcur; i++) { UserAssert(pacon->aspcur[i]->CURSORF_flags & CURSORF_ACONFRAME); /* * This should not be a public acon; if it is, unlock won't be able * to destroy it. If destroy a public icon, ownership must be called * before calling this function (see zzzSetSystemImage). */ UserAssert(GETPPI(pacon->aspcur[i]) != NULL); pcur = Unlock(&pacon->aspcur[i]); if (pcur != NULL) { _DestroyCursor(pcur, CURSOR_ALWAYSDESTROY); } } UserFreePool(pacon->aspcur); return TRUE; } /***********************************************************************\ * LinkCursor * * Links unlinked cursor into the apropriate icon cache IFF its the * type of cursor that needs to be in the cache. * * Note that changing ownership if cursor objects needs to keep this * cache linking in mind. The unlink routine in * DestroyEmptyCursorObject() will handle public cursor objects made * local but that is all. * * 10/18/1995 Created SanfordS \***********************************************************************/ VOID LinkCursor( PCURSOR pcur) { /* * Should never try to link twice! */ UserAssert(!(pcur->CURSORF_flags & CURSORF_LINKED)); /* * We don't cache acon frames because they all belong to the * root acon object. * * We don't cache process owned objects that are not LRSHARED * either. */ if (!(pcur->CURSORF_flags & CURSORF_ACONFRAME)) { PPROCESSINFO ppi = pcur->head.ppi; if (ppi == NULL) { /* * Public cache object. */ pcur->pcurNext = gpcurFirst; gpcurFirst = pcur; pcur->CURSORF_flags |= CURSORF_LINKED; } else if (pcur->CURSORF_flags & CURSORF_LRSHARED) { /* * Private cache LR_SHARED object. */ pcur->pcurNext = ppi->pCursorCache; ppi->pCursorCache = pcur; pcur->CURSORF_flags |= CURSORF_LINKED; } } } /***************************************************************************\ * ProcessAlphaBitmap * * Examines the source bitmap to see if it supports and uses an alpha * channel. If it does, a new DIB section is created that contains a * premultiplied copy of the data from the source bitmap. * * If the source bitmap is not capable of supporting, or simply doesn't use, * an alpha channel, the return value is NULL. * * If an error occurs, the return value is NULL. * * 8/10/2000 Created DwayneN \***************************************************************************/ HBITMAP ProcessAlphaBitmap( HBITMAP hbmSource) { BITMAP bmp; BITMAPINFO bi; HBITMAP hbmAlpha; RGBQUAD * pAlphaBitmapBits; DWORD cPixels; DWORD i; RGBQUAD pixel; BOOL fAlphaChannel; /* * There are several code paths that end up calling us with a NULL * hbmSource. This is fine, in that it simply indicates that there * is no alpha channel. */ if (hbmSource == NULL) { return NULL; } if (GreExtGetObjectW(hbmSource, sizeof(BITMAP), &bmp) == 0) { return NULL; } /* * Only single plane, 32bpp bitmaps can even contain an alpha channel. */ if (bmp.bmPlanes != 1 || bmp.bmBitsPixel != 32) { return NULL; } /* * Allocate room to hold the source bitmap's bits for examination. * We actually allocate a DIB - that will be passed out if the * source bitmap does indeed contain an alpha channel. */ RtlZeroMemory(&bi, sizeof(bi)); bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth = bmp.bmWidth; bi.bmiHeader.biHeight = bmp.bmHeight; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; hbmAlpha = GreCreateDIBitmapReal(gpDispInfo->hdcScreen, 0, NULL, (LPBITMAPINFO)&bi, DIB_RGB_COLORS, sizeof(bi), 0, NULL, 0, NULL, 0, 0, &pAlphaBitmapBits); if (NULL != hbmAlpha) { /* * Set up the header again in case it was tweaked by GreCreateDIBitmapReal. */ RtlZeroMemory(&bi, sizeof(bi)); bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth = bmp.bmWidth; bi.bmiHeader.biHeight = bmp.bmHeight; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; /* * Copy the bitmap data from the source bitmap into our alpha DIB. */ if (0 == GreGetDIBitsInternal(gpDispInfo->hdcScreen, hbmSource, 0, bi.bmiHeader.biHeight, (LPBYTE) pAlphaBitmapBits, (LPBITMAPINFO)&bi, DIB_RGB_COLORS, BITMAPWIDTHSIZE(bi.bmiHeader.biWidth, bi.bmiHeader.biHeight,1,32), bi.bmiHeader.biSize)) { GreDeleteObject(hbmAlpha); return NULL; } /* * We need to examine the source bitmap to see if it contains an alpha * channel. This is simply a heuristic since there is no format difference * between 32bpp 888 RGB image and 32bpp 8888 ARGB image. What we do is look * for any non-0 alpha/reserved values. If all alpha/reserved values are 0, * then the image would be 100% invisible if blitted with alpha - which is * almost cerainly not the desired result. So we assume such bitmaps are * 32bpp non-alpha. */ cPixels = bi.bmiHeader.biWidth * bi.bmiHeader.biHeight; fAlphaChannel = FALSE; for (i = 0; i < cPixels; i++) { if (pAlphaBitmapBits[i].rgbReserved != 0) { fAlphaChannel = TRUE; break; } } if (fAlphaChannel == FALSE) { GreDeleteObject(hbmAlpha); return NULL; } /* * The source bitmap appears to use an alpha channel. Spin through our * copy of the bits and premultiply them. This is a necessary step to * prepare an alpha bitmap for use by GDI. */ for (i=0; i < cPixels; i++) { pixel = pAlphaBitmapBits[i]; pAlphaBitmapBits[i].rgbRed = (pixel.rgbRed * pixel.rgbReserved) / 0xFF; pAlphaBitmapBits[i].rgbGreen = (pixel.rgbGreen * pixel.rgbReserved) / 0xFF; pAlphaBitmapBits[i].rgbBlue = (pixel.rgbBlue * pixel.rgbReserved) / 0xFF; } } return hbmAlpha; } /***************************************************************************\ * _SetCursorIconData * * Initializes empty cursor/icons. Note that the string buffers and * pcurData are not captured. If a fault occurs in this routine, * all allocated memory will be freed when the cursors are destroyed. * * Critical side effect: If this function fails, the bitmaps must NOT * have been made public. (See CreateIconIndirect()). * * History: * 12-01-94 JimA Created. \***************************************************************************/ BOOL _SetCursorIconData( PCURSOR pcur, PUNICODE_STRING cczpstrModName, PUNICODE_STRING cczpstrName, PCURSORDATA pcurData, DWORD cbData) { #define pacon ((PACON)pcur) int i; #if DBG BOOL fSuccess; #endif pcur->CURSORF_flags |= pcurData->CURSORF_flags; pcur->rt = pcurData->rt; if (pcurData->CURSORF_flags & CURSORF_ACON) { UserAssert(pacon->aspcur == NULL); RtlCopyMemory(&pacon->cpcur, &pcurData->cpcur, sizeof(ACON) - FIELD_OFFSET(ACON, cpcur)); } else { RtlCopyMemory(&pcur->CI_COPYSTART, &pcurData->CI_COPYSTART, sizeof(CURSOR) - FIELD_OFFSET(CURSOR, CI_COPYSTART)); } /* * Save name of the cursor resource */ if (cczpstrName->Length != 0){ /* * AllocateUnicodeString guards access to src Buffer with * a try block. */ if (!AllocateUnicodeString(&pcur->strName, cczpstrName)) return FALSE; } else { pcur->strName = *cczpstrName; } /* * Save the module name */ if (cczpstrModName->Buffer) { /* * UserAddAtom guards access to the string with a try block. */ pcur->atomModName = UserAddAtom(cczpstrModName->Buffer, FALSE); if (pcur->atomModName == 0) { return FALSE; } } if (pcur->CURSORF_flags & CURSORF_ACON) { /* * Stash away animated icon info. */ pacon = (PACON)pcur; pacon->aspcur = UserAllocPool(cbData, TAG_CURSOR); if (pacon->aspcur == NULL) return FALSE; /* * Copy the handle array. Do this in a try/except so the * buffer will be freed if pcurData goes away. Even though * cursor destruction would free the array, a fault will * leave the contents in an undetermined state and cause * problems during cursor destruction. */ try { RtlCopyMemory(pacon->aspcur, pcurData->aspcur, cbData); pacon->aicur = (DWORD *)((PBYTE)pacon->aspcur + (ULONG_PTR)pcurData->aicur); pacon->ajifRate = (PJIF)((PBYTE)pacon->aspcur + (ULONG_PTR)pcurData->ajifRate); } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { UserFreePool(pacon->aspcur); pacon->aspcur = NULL; return FALSE; } /* * Convert handles into pointers and lock them in. */ for (i = 0; i < pacon->cpcur; i++) { PCURSOR pcurT; pcurT = (PCURSOR) HMValidateHandle(pacon->aspcur[i], TYPE_CURSOR); if (pcurT) { pacon->aspcur[i] = NULL; Lock(&pacon->aspcur[i], pcurT); } else { while (--i >= 0) { Unlock(&pacon->aspcur[i]); } UserFreePool(pacon->aspcur); pacon->aspcur = NULL; RIPMSG0(RIP_WARNING, "SetCursorIconData: invalid cursor handle for animated cursor"); return FALSE; } } } else { PW32PROCESS W32Process = W32GetCurrentProcess(); /* * If the icon's color bitmap has an alpha channel, pre-process it * and cache it in our hbmUserAlpha field. */ pcur->hbmUserAlpha = ProcessAlphaBitmap(pcur->hbmColor); /* * Make the cursor and its bitmaps public - LAST THING! */ UserAssert(pcur->hbmMask); UserAssert(pcur->cx); UserAssert(pcur->cy); /* * Make the cursor public so that it can be shared across processes. * Charge the curson to this very process GDI quota even if it's public. */ #if DBG fSuccess = #endif GreSetBitmapOwner(pcur->hbmMask, OBJECT_OWNER_PUBLIC); UserAssert(fSuccess); GreIncQuotaCount(W32Process); if (pcur->hbmColor) { #if DBG fSuccess = #endif GreSetBitmapOwner(pcur->hbmColor, OBJECT_OWNER_PUBLIC); UserAssert(fSuccess); GreIncQuotaCount(W32Process); } if (pcur->hbmUserAlpha != NULL) { #if DBG fSuccess = #endif GreSetBitmapOwner(pcur->hbmUserAlpha, OBJECT_OWNER_PUBLIC); UserAssert(fSuccess); GreIncQuotaCount(W32Process); } } LinkCursor(pcur); return TRUE; #undef pacon }