[WIN32SS]

- Commit everything I got on new cursor/icon implementation
 - Implement loading animated cursors (no display yet)
 - Get rid of this m:n relation between cursors and processes. (ie. proper implementation of LR_SHARED)
 - Get rid of the global linked list of cursors (ditto)
 - Use atoms instead of copying the module name into the object. (aka stop using non paged pool for nothing)
 - Misc fixes

svn path=/trunk/; revision=60978
This commit is contained in:
Jérôme Gardou 2013-11-13 20:06:01 +00:00
parent 6e769a014e
commit 26b89c0907
12 changed files with 967 additions and 479 deletions

View file

@ -2000,8 +2000,8 @@ NTAPI
NtUserGetCursorFrameInfo( NtUserGetCursorFrameInfo(
HCURSOR hCursor, HCURSOR hCursor,
DWORD istep, DWORD istep,
PDWORD rate_jiffies, INT* rate_jiffies,
INT *num_steps); DWORD* num_steps);
BOOL BOOL
NTAPI NTAPI
@ -2791,7 +2791,7 @@ NtUserSetCursorIconData(
_In_ HCURSOR hCursor, _In_ HCURSOR hCursor,
_In_ PUNICODE_STRING pustrModule, _In_ PUNICODE_STRING pustrModule,
_In_ PUNICODE_STRING puSrcName, _In_ PUNICODE_STRING puSrcName,
_In_ PCURSORDATA pCursorData); _In_ const CURSORDATA* pCursorData);
typedef struct _tagFINDEXISTINGCURICONPARAM typedef struct _tagFINDEXISTINGCURICONPARAM
{ {

View file

@ -90,7 +90,7 @@ typedef struct
{ union { union
{ {
ICONRESDIR icon; ICONRESDIR icon;
CURSORDIR cursor; CURSORRESDIR cursor;
} ResInfo; } ResInfo;
WORD wPlanes; WORD wPlanes;
WORD wBitCount; WORD wBitCount;

View file

@ -1728,7 +1728,11 @@ IntSetClassMenuName(IN PCLS Class,
} }
//// Do this for now in anticipation of new cursor icon code. //// Do this for now in anticipation of new cursor icon code.
#ifndef NEW_CURSORICON
BOOLEAN FASTCALL IntDestroyCurIconObject(PCURICON_OBJECT, PPROCESSINFO); BOOLEAN FASTCALL IntDestroyCurIconObject(PCURICON_OBJECT, PPROCESSINFO);
#else
BOOLEAN FASTCALL IntDestroyCurIconObject(PCURICON_OBJECT, BOOLEAN);
#endif
BOOL FASTCALL BOOL FASTCALL
IntClassDestroyIcon(HANDLE hCurIcon) IntClassDestroyIcon(HANDLE hCurIcon)
@ -1742,8 +1746,13 @@ IntClassDestroyIcon(HANDLE hCurIcon)
ERR("hCurIcon was not found!\n"); ERR("hCurIcon was not found!\n");
return FALSE; return FALSE;
} }
#ifndef NEW_CURSORICON
Ret = IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process()); Ret = IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process());
/* Note: IntDestroyCurIconObject will remove our reference for us! */ /* Note: IntDestroyCurIconObject will remove our reference for us! */
#else
/* Note: IntDestroyCurIconObject will remove our reference for us! */
Ret = IntDestroyCurIconObject(CurIcon, TRUE);
#endif
if (!Ret) if (!Ret)
{ {
ERR("hCurIcon was not Destroyed!\n"); ERR("hCurIcon was not Destroyed!\n");

View file

@ -203,7 +203,7 @@ IntFindExistingCurIconObject(HMODULE hModule,
return NULL; return NULL;
} }
PCURICON_OBJECT HANDLE
IntCreateCurIconHandle(DWORD dwNumber) IntCreateCurIconHandle(DWORD dwNumber)
{ {
PCURICON_OBJECT CurIcon; PCURICON_OBJECT CurIcon;
@ -220,7 +220,7 @@ IntCreateCurIconHandle(DWORD dwNumber)
CurIcon->Self = hCurIcon; CurIcon->Self = hCurIcon;
InitializeListHead(&CurIcon->ProcessList); InitializeListHead(&CurIcon->ProcessList);
if (! ReferenceCurIconByProcess(CurIcon)) if (!ReferenceCurIconByProcess(CurIcon))
{ {
ERR("Failed to add process\n"); ERR("Failed to add process\n");
UserDeleteObject(hCurIcon, TYPE_CURSOR); UserDeleteObject(hCurIcon, TYPE_CURSOR);
@ -230,7 +230,9 @@ IntCreateCurIconHandle(DWORD dwNumber)
InsertHeadList(&gCurIconList, &CurIcon->ListEntry); InsertHeadList(&gCurIconList, &CurIcon->ListEntry);
return CurIcon; UserDereferenceObject(CurIcon);
return hCurIcon;
} }
BOOLEAN FASTCALL BOOLEAN FASTCALL
@ -1519,4 +1521,20 @@ NtUserDrawIconEx(
return Ret; return Ret;
} }
/*
* @unimplemented
*/
HCURSOR
NTAPI
NtUserGetCursorFrameInfo(
HCURSOR hCursor,
DWORD istep,
INT* rate_jiffies,
DWORD* num_steps)
{
STUB
return 0;
}
/* EOF */ /* EOF */

View file

@ -2,40 +2,53 @@
#define MAXCURICONHANDLES 4096 #define MAXCURICONHANDLES 4096
#ifdef NEW_CURSORICON
typedef struct _CURICON_OBJECT
{
PROCMARKHEAD head;
struct _CURICON_OBJECT* pcurNext;
UNICODE_STRING strName;
USHORT atomModName;
USHORT rt;
ULONG CURSORF_flags;
SHORT xHotspot;
SHORT yHotspot;
HBITMAP hbmMask;
HBITMAP hbmColor;
HBITMAP hbmAlpha;
RECT rcBounds;
HBITMAP hbmUserAlpha;
ULONG bpp;
ULONG cx;
ULONG cy;
} CURICON_OBJECT, *PCURICON_OBJECT;
typedef struct tagACON
{
PROCMARKHEAD head;
struct _CURICON_OBJECT* pcurNext;
UNICODE_STRING strName;
USHORT atomModName;
USHORT rt;
ULONG CURSORF_flags;
INT cpcur;
INT cicur;
PCURICON_OBJECT * aspcur;
DWORD * aicur;
INT * ajifRate;
INT iicur;
} ACON, *PACON;
C_ASSERT(FIELD_OFFSET(ACON, cpcur) == FIELD_OFFSET(CURICON_OBJECT, xHotspot));
#else
typedef struct tagCURICON_PROCESS typedef struct tagCURICON_PROCESS
{ {
LIST_ENTRY ListEntry; LIST_ENTRY ListEntry;
PPROCESSINFO Process; PPROCESSINFO Process;
} CURICON_PROCESS, *PCURICON_PROCESS; } CURICON_PROCESS, *PCURICON_PROCESS;
#ifdef NEW_CURSORICON
typedef struct _CURICON_OBJECT
{
PROCMARKHEAD head;
struct _tagCURSOR* pcurNext;
UNICODE_STRING strName;
USHORT atomModName;
USHORT rt;
ULONG CURSORF_flags;
SHORT xHotspot;
SHORT yHotspot;
HBITMAP hbmMask;
HBITMAP hbmColor;
HBITMAP hbmAlpha;
RECT rcBounds;
HBITMAP hbmUserAlpha;
ULONG bpp;
ULONG cx;
ULONG cy;
/* ReactOS specific, to be deleted */
LIST_ENTRY ListEntry;
HANDLE Self;
LIST_ENTRY ProcessList;
UNICODE_STRING ustrModule;
} CURICON_OBJECT, *PCURICON_OBJECT;
#else
typedef struct _CURICON_OBJECT typedef struct _CURICON_OBJECT
{ {
PROCMARKHEAD head; PROCMARKHEAD head;
@ -91,7 +104,7 @@ typedef struct _SYSTEM_CURSORINFO
} SYSTEM_CURSORINFO, *PSYSTEM_CURSORINFO; } SYSTEM_CURSORINFO, *PSYSTEM_CURSORINFO;
BOOL InitCursorImpl(VOID); BOOL InitCursorImpl(VOID);
PCURICON_OBJECT IntCreateCurIconHandle(DWORD dwNumber); HANDLE IntCreateCurIconHandle(BOOLEAN Anim);
VOID FASTCALL IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process); VOID FASTCALL IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process);
BOOL UserDrawIconEx(HDC hDc, INT xLeft, INT yTop, PCURICON_OBJECT pIcon, INT cxWidth, BOOL UserDrawIconEx(HDC hDc, INT xLeft, INT yTop, PCURICON_OBJECT pIcon, INT cxWidth,

View file

@ -17,37 +17,16 @@
* Possibly shared by multiple processes * Possibly shared by multiple processes
* Immune to NtDestroyCursorIcon() * Immune to NtDestroyCursorIcon()
* CurIcon->hModule, CurIcon->hRsrc and CurIcon->hGroupRsrc are valid * CurIcon->hModule, CurIcon->hRsrc and CurIcon->hGroupRsrc are valid
* There's a M:N relationship between processes and (shared) cursor/icons.
* A process can have multiple cursor/icons and a cursor/icon can be used
* by multiple processes. To keep track of this we keep a list of all
* cursor/icons (CurIconList) and per cursor/icon we keep a list of
* CURICON_PROCESS structs starting at CurIcon->ProcessList.
*/ */
#include <win32k.h> #include <win32k.h>
DBG_DEFAULT_CHANNEL(UserIcon); DBG_DEFAULT_CHANNEL(UserIcon);
static LIST_ENTRY gCurIconList;
static PAGED_LOOKASIDE_LIST *pgProcessLookasideList;
SYSTEM_CURSORINFO gSysCursorInfo; SYSTEM_CURSORINFO gSysCursorInfo;
BOOL BOOL
InitCursorImpl() InitCursorImpl()
{ {
pgProcessLookasideList = ExAllocatePool(NonPagedPool, sizeof(PAGED_LOOKASIDE_LIST));
if(!pgProcessLookasideList)
return FALSE;
ExInitializePagedLookasideList(pgProcessLookasideList,
NULL,
NULL,
0,
sizeof(CURICON_PROCESS),
TAG_DIB,
128);
InitializeListHead(&gCurIconList);
gSysCursorInfo.Enabled = FALSE; gSysCursorInfo.Enabled = FALSE;
gSysCursorInfo.ButtonsDown = 0; gSysCursorInfo.ButtonsDown = 0;
gSysCursorInfo.bClipped = FALSE; gSysCursorInfo.bClipped = FALSE;
@ -84,6 +63,13 @@ PCURICON_OBJECT FASTCALL UserGetCurIconObject(HCURSOR hCurIcon)
return NULL; return NULL;
} }
if(UserObjectInDestroy(hCurIcon))
{
ERR("Requesting destroyed cursor.\n");
EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
return NULL;
}
CurIcon = (PCURICON_OBJECT)UserReferenceObjectByHandle(hCurIcon, TYPE_CURSOR); CurIcon = (PCURICON_OBJECT)UserReferenceObjectByHandle(hCurIcon, TYPE_CURSOR);
if (!CurIcon) if (!CurIcon)
{ {
@ -138,98 +124,19 @@ BOOL UserSetCursorPos( INT x, INT y, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Ho
return TRUE; return TRUE;
} }
/* HANDLE
* We have to register that this object is in use by the current IntCreateCurIconHandle(BOOLEAN Animated)
* process. The only way to do that seems to be to walk the list
* of cursor/icon objects starting at W32Process->CursorIconListHead.
* If the object is already present in the list, we don't have to do
* anything, if it's not present we add it and inc the ProcessCount
* in the object. Having to walk the list kind of sucks, but that's
* life...
*/
static BOOLEAN FASTCALL
ReferenceCurIconByProcess(PCURICON_OBJECT CurIcon)
{
PPROCESSINFO Win32Process;
PCURICON_PROCESS Current;
Win32Process = PsGetCurrentProcessWin32Process();
LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
{
if (Current->Process == Win32Process)
{
/* Already registered for this process */
return TRUE;
}
}
/* Not registered yet */
Current = ExAllocateFromPagedLookasideList(pgProcessLookasideList);
if (NULL == Current)
{
return FALSE;
}
InsertHeadList(&CurIcon->ProcessList, &Current->ListEntry);
Current->Process = Win32Process;
return TRUE;
}
static
PCURICON_OBJECT
FASTCALL
IntFindExistingCurIconObject(
PUNICODE_STRING pustrModule,
PUNICODE_STRING pustrRsrc,
FINDEXISTINGCURICONPARAM* param)
{
PCURICON_OBJECT CurIcon;
LIST_FOR_EACH(CurIcon, &gCurIconList, CURICON_OBJECT, ListEntry)
{
/* See if we are looking for an icon or a cursor */
if(MAKEINTRESOURCE(CurIcon->rt) != (param->bIcon ? RT_ICON : RT_CURSOR))
continue;
/* See if module names match */
if(RtlCompareUnicodeString(pustrModule, &CurIcon->ustrModule, TRUE) == 0)
{
/* They do. Now see if this is the same resource */
if(IS_INTRESOURCE(CurIcon->strName.Buffer) && IS_INTRESOURCE(pustrRsrc->Buffer))
{
if(CurIcon->strName.Buffer != pustrRsrc->Buffer)
continue;
}
else if(IS_INTRESOURCE(CurIcon->strName.Buffer) || IS_INTRESOURCE(pustrRsrc->Buffer))
continue;
else if(RtlCompareUnicodeString(pustrRsrc, &CurIcon->strName, TRUE) != 0)
continue;
if ((param->cx == CurIcon->cx) && (param->cy == CurIcon->cy))
{
if (! ReferenceCurIconByProcess(CurIcon))
{
return NULL;
}
return CurIcon;
}
}
}
return NULL;
}
PCURICON_OBJECT
IntCreateCurIconHandle(DWORD dwNumber)
{ {
PCURICON_OBJECT CurIcon; PCURICON_OBJECT CurIcon;
HANDLE hCurIcon; HANDLE hCurIcon;
if(dwNumber == 0)
dwNumber = 1;
CurIcon = UserCreateObject(gHandleTable, NULL, NULL, &hCurIcon, TYPE_CURSOR, sizeof(CURICON_OBJECT)); CurIcon = UserCreateObject(
gHandleTable,
NULL,
NULL,
&hCurIcon,
TYPE_CURSOR,
Animated ? sizeof(ACON) : sizeof(CURICON_OBJECT));
if (!CurIcon) if (!CurIcon)
{ {
@ -237,134 +144,102 @@ IntCreateCurIconHandle(DWORD dwNumber)
return FALSE; return FALSE;
} }
CurIcon->Self = hCurIcon; UserDereferenceObject(CurIcon);
InitializeListHead(&CurIcon->ProcessList);
if (! ReferenceCurIconByProcess(CurIcon)) return hCurIcon;
{
ERR("Failed to add process\n");
UserDereferenceObject(CurIcon);
UserDeleteObject(hCurIcon, TYPE_CURSOR);
return NULL;
}
InsertHeadList(&gCurIconList, &CurIcon->ListEntry);
return CurIcon;
} }
BOOLEAN FASTCALL BOOLEAN FASTCALL
IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, PPROCESSINFO ppi, BOOLEAN bForce) IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, BOOLEAN bForce)
{ {
HBITMAP bmpMask, bmpColor, bmpAlpha;
BOOLEAN Ret, bListEmpty, bFound = FALSE;
PCURICON_PROCESS Current = NULL;
/* Check if this is the current cursor */
if(CurIcon->CURSORF_flags & CURSORF_CURRENT) if(CurIcon->CURSORF_flags & CURSORF_CURRENT)
{ {
UserDereferenceObject(CurIcon); /* Mark the object as destroyed, and fail, as per wine tests */
UserDeleteObject(CurIcon->head.h, TYPE_CURSOR);
return FALSE; return FALSE;
} }
/* For handles created without any data (error handling) */
if(IsListEmpty(&CurIcon->ProcessList))
goto emptyList;
/* Now find this process in the list of processes referencing this object and if(CurIcon->head.ppi != PsGetCurrentProcessWin32Process())
remove it from that list */
LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
{ {
if (Current->Process == ppi) /* This object doesn't belong to the current process */
{ WARN("Trying to delete foreign cursor!\n");
bFound = TRUE;
bListEmpty = RemoveEntryList(&Current->ListEntry);
break;
}
}
if(!bFound)
{
/* This object doesn't belong to this process */
EngSetLastError(ERROR_INVALID_HANDLE);
/* Caller expects us to dereference! */
UserDereferenceObject(CurIcon); UserDereferenceObject(CurIcon);
EngSetLastError(ERROR_DESTROY_OBJECT_OF_OTHER_THREAD);
return FALSE; return FALSE;
} }
/* We found our process, but we're told to not destroy it in case it is shared */ /* Do not destroy it if it is shared. (And we're not forced to) */
if((CurIcon->ustrModule.Buffer != NULL) && !bForce) if((CurIcon->CURSORF_flags & CURSORF_LRSHARED) && !bForce)
{ {
/* Tests show this is a valid call */ /* Tests show this is a valid call */
WARN("Trying to destroy shared cursor!\n");
UserDereferenceObject(CurIcon); UserDereferenceObject(CurIcon);
return TRUE; return TRUE;
} }
ExFreeToPagedLookasideList(pgProcessLookasideList, Current); if(!(CurIcon->CURSORF_flags & CURSORF_ACON))
/* If there are still processes referencing this object we can't destroy it yet */
if (!bListEmpty)
{ {
if(CurIcon->head.ppi == ppi) HBITMAP bmpMask = CurIcon->hbmMask;
HBITMAP bmpColor = CurIcon->hbmColor;
HBITMAP bmpAlpha = CurIcon->hbmAlpha;
/* Delete bitmaps */
if (bmpMask)
{ {
/* Set the first process of the list as owner */ GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED);
Current = CONTAINING_RECORD(CurIcon->ProcessList.Flink, CURICON_PROCESS, ListEntry); GreDeleteObject(bmpMask);
UserSetObjectOwner(CurIcon, TYPE_CURSOR, Current->Process); CurIcon->hbmMask = NULL;
}
if (bmpColor)
{
GreSetObjectOwner(bmpColor, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(bmpColor);
CurIcon->hbmColor = NULL;
}
if (bmpAlpha)
{
GreSetObjectOwner(bmpAlpha, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(bmpAlpha);
CurIcon->hbmAlpha = NULL;
} }
UserDereferenceObject(CurIcon);
return TRUE;
} }
else
emptyList:
/* Remove it from the list */
RemoveEntryList(&CurIcon->ListEntry);
bmpMask = CurIcon->hbmMask;
bmpColor = CurIcon->hbmColor;
bmpAlpha = CurIcon->hbmAlpha;
/* Delete bitmaps */
if (bmpMask)
{ {
GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED); PACON AniCurIcon = (PACON)CurIcon;
GreDeleteObject(bmpMask); UINT i;
CurIcon->hbmMask = NULL;
}
if (bmpColor)
{
GreSetObjectOwner(bmpColor, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(bmpColor);
CurIcon->hbmColor = NULL;
}
if (bmpAlpha)
{
GreSetObjectOwner(bmpAlpha, GDI_OBJ_HMGR_POWNED);
GreDeleteObject(bmpAlpha);
CurIcon->hbmAlpha = NULL;
}
if(!IS_INTRESOURCE(CurIcon->strName.Buffer))
ExFreePoolWithTag(CurIcon->strName.Buffer, TAG_STRING);
if(CurIcon->ustrModule.Buffer)
ReleaseCapturedUnicodeString(&CurIcon->ustrModule, UserMode);
/* We were given a pointer, no need to keep the reference anylonger! */ for(i = 0; i < AniCurIcon->cpcur; i++)
IntDestroyCurIconObject(AniCurIcon->aspcur[i], TRUE);
ExFreePoolWithTag(AniCurIcon->aspcur, USERTAG_CURSOR);
}
if (CurIcon->CURSORF_flags & CURSORF_LRSHARED)
{
if (!IS_INTRESOURCE(CurIcon->strName.Buffer))
ExFreePoolWithTag(CurIcon->strName.Buffer, TAG_STRING);
if (CurIcon->atomModName)
RtlDeleteAtomFromAtomTable(gAtomTable, CurIcon->atomModName);
CurIcon->strName.Buffer = NULL;
CurIcon->atomModName = 0;
}
/* We were given a pointer, no need to keep the reference any longer! */
UserDereferenceObject(CurIcon); UserDereferenceObject(CurIcon);
Ret = UserDeleteObject(CurIcon->Self, TYPE_CURSOR); return UserDeleteObject(CurIcon->head.h, TYPE_CURSOR);
return Ret;
} }
VOID FASTCALL VOID FASTCALL
IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process) IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process)
{ {
PCURICON_OBJECT CurIcon, tmp; PCURICON_OBJECT CurIcon;
/* Run through the list of icon objects */ /* Run through the list of icon objects */
LIST_FOR_EACH_SAFE(CurIcon, tmp, &gCurIconList, CURICON_OBJECT, ListEntry) while(Win32Process->pCursorCache)
{ {
UserReferenceObject(CurIcon); CurIcon = Win32Process->pCursorCache;
IntDestroyCurIconObject(CurIcon, Win32Process, TRUE); Win32Process->pCursorCache = CurIcon->pcurNext;
ASSERT(CurIcon->head.cLockObj == 2);
IntDestroyCurIconObject(CurIcon, TRUE);
} }
} }
@ -410,17 +285,24 @@ NtUserGetIconInfo(
/* Give back the icon information */ /* Give back the icon information */
if(IconInfo) if(IconInfo)
{ {
PCURICON_OBJECT FrameCurIcon = CurIcon;
if(CurIcon->CURSORF_flags & CURSORF_ACON)
{
/* Get information from first frame. */
FrameCurIcon = ((PACON)CurIcon)->aspcur[0];
}
/* Fill data */ /* Fill data */
ii.fIcon = is_icon(CurIcon); ii.fIcon = is_icon(FrameCurIcon);
ii.xHotspot = CurIcon->xHotspot; ii.xHotspot = FrameCurIcon->xHotspot;
ii.yHotspot = CurIcon->yHotspot; ii.yHotspot = FrameCurIcon->yHotspot;
/* Copy bitmaps */ /* Copy bitmaps */
ii.hbmMask = BITMAP_CopyBitmap(CurIcon->hbmMask); ii.hbmMask = BITMAP_CopyBitmap(FrameCurIcon->hbmMask);
GreSetObjectOwner(ii.hbmMask, GDI_OBJ_HMGR_POWNED); GreSetObjectOwner(ii.hbmMask, GDI_OBJ_HMGR_POWNED);
ii.hbmColor = BITMAP_CopyBitmap(CurIcon->hbmColor); ii.hbmColor = BITMAP_CopyBitmap(FrameCurIcon->hbmColor);
GreSetObjectOwner(ii.hbmColor, GDI_OBJ_HMGR_POWNED); GreSetObjectOwner(ii.hbmColor, GDI_OBJ_HMGR_POWNED);
colorBpp = CurIcon->bpp; colorBpp = FrameCurIcon->bpp;
/* Copy fields */ /* Copy fields */
_SEH2_TRY _SEH2_TRY
@ -439,63 +321,72 @@ NtUserGetIconInfo(
Status = _SEH2_GetExceptionCode(); Status = _SEH2_GetExceptionCode();
} }
_SEH2_END _SEH2_END
}
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
WARN("Status: 0x%08x.\n", Status); WARN("Status: 0x%08x.\n", Status);
SetLastNtError(Status); SetLastNtError(Status);
goto leave; goto leave;
}
} }
/* Give back the module name */ /* Give back the module name */
if(lpModule) if(lpModule)
{ {
if(!CurIcon->ustrModule.Buffer) ULONG BufLen = 0;
{ if (!CurIcon->atomModName)
EngSetLastError(ERROR_INVALID_HANDLE);
goto leave; goto leave;
}
/* Copy what we can */ RtlQueryAtomInAtomTable(gAtomTable, CurIcon->atomModName, NULL, NULL, NULL, &BufLen);
/* Get the module name from the atom table */
_SEH2_TRY _SEH2_TRY
{ {
ProbeForWrite(lpModule, sizeof(UNICODE_STRING), 1); if (BufLen > (lpModule->MaximumLength * sizeof(WCHAR)))
ProbeForWrite(lpModule->Buffer, lpModule->MaximumLength, 1); {
lpModule->Length = min(lpModule->MaximumLength, CurIcon->ustrModule.Length); lpModule->Length = 0;
RtlCopyMemory(lpModule->Buffer, CurIcon->ustrModule.Buffer, lpModule->Length); }
else
{
ProbeForWrite(lpModule->Buffer, lpModule->MaximumLength, 1);
BufLen = lpModule->MaximumLength * sizeof(WCHAR);
RtlQueryAtomInAtomTable(gAtomTable, CurIcon->atomModName, NULL, NULL, lpModule->Buffer, &BufLen);
lpModule->Length = BufLen/sizeof(WCHAR);
}
} }
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{ {
Status = _SEH2_GetExceptionCode(); Status = _SEH2_GetExceptionCode();
} }
_SEH2_END _SEH2_END
}
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{
SetLastNtError(Status);
goto leave;
}
if(lpResName)
{
if(!CurIcon->strName.Buffer)
{ {
EngSetLastError(ERROR_INVALID_HANDLE); SetLastNtError(Status);
goto leave; goto leave;
} }
}
if (lpResName)
{
if (!CurIcon->strName.Buffer)
goto leave;
/* Copy it */ /* Copy it */
_SEH2_TRY _SEH2_TRY
{ {
ProbeForWrite(lpResName, sizeof(UNICODE_STRING), 1); ProbeForWrite(lpResName, sizeof(UNICODE_STRING), 1);
if(IS_INTRESOURCE(CurIcon->strName.Buffer)) if (IS_INTRESOURCE(CurIcon->strName.Buffer))
{ {
lpResName->Buffer = CurIcon->strName.Buffer; lpResName->Buffer = CurIcon->strName.Buffer;
lpResName->Length = 0; lpResName->Length = 0;
} }
else if (lpResName->MaximumLength < CurIcon->strName.Length)
{
lpResName->Length = 0;
}
else else
{ {
lpResName->Length = min(lpResName->MaximumLength, CurIcon->strName.Length); ProbeForWrite(lpResName->Buffer, lpResName->MaximumLength * sizeof(WCHAR), 1);
RtlCopyMemory(lpResName->Buffer, CurIcon->strName.Buffer, lpResName->Length); RtlCopyMemory(lpResName->Buffer, CurIcon->strName.Buffer, lpResName->Length);
} }
} }
@ -547,6 +438,15 @@ NtUserGetIconSize(
goto cleanup; goto cleanup;
} }
if(CurIcon->CURSORF_flags & CURSORF_ACON)
{
/* Use first frame for animated cursors */
PACON AniCurIcon = (PACON)CurIcon;
CurIcon = AniCurIcon->aspcur[0];
UserDereferenceObject(AniCurIcon);
UserReferenceObject(CurIcon);
}
_SEH2_TRY _SEH2_TRY
{ {
ProbeForWrite(plcx, sizeof(LONG), 1); ProbeForWrite(plcx, sizeof(LONG), 1);
@ -597,7 +497,7 @@ NtUserGetCursorInfo(
SafeCi.cbSize = sizeof(CURSORINFO); SafeCi.cbSize = sizeof(CURSORINFO);
SafeCi.flags = ((CurIcon && CurInfo->ShowingCursor >= 0) ? CURSOR_SHOWING : 0); SafeCi.flags = ((CurIcon && CurInfo->ShowingCursor >= 0) ? CURSOR_SHOWING : 0);
SafeCi.hCursor = (CurIcon ? (HCURSOR)CurIcon->Self : (HCURSOR)0); SafeCi.hCursor = (CurIcon ? CurIcon->head.h : NULL);
SafeCi.ptScreenPos = gpsi->ptCursor; SafeCi.ptScreenPos = gpsi->ptCursor;
@ -739,7 +639,7 @@ NtUserDestroyCursor(
RETURN(FALSE); RETURN(FALSE);
} }
ret = IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process(), bForce); ret = IntDestroyCurIconObject(CurIcon, bForce);
/* Note: IntDestroyCurIconObject will remove our reference for us! */ /* Note: IntDestroyCurIconObject will remove our reference for us! */
RETURN(ret); RETURN(ret);
@ -766,16 +666,11 @@ NtUserFindExistingCursorIcon(
UNICODE_STRING ustrModuleSafe, ustrRsrcSafe; UNICODE_STRING ustrModuleSafe, ustrRsrcSafe;
FINDEXISTINGCURICONPARAM paramSafe; FINDEXISTINGCURICONPARAM paramSafe;
NTSTATUS Status; NTSTATUS Status;
PPROCESSINFO pProcInfo = PsGetCurrentProcessWin32Process();
RTL_ATOM atomModName;
TRACE("Enter NtUserFindExistingCursorIcon\n"); TRACE("Enter NtUserFindExistingCursorIcon\n");
/* Capture resource name (it can be an INTRESOURCE == ATOM) */
Status = ProbeAndCaptureUnicodeStringOrAtom(&ustrRsrcSafe, pustrRsrc);
if(!NT_SUCCESS(Status))
return NULL;
Status = ProbeAndCaptureUnicodeString(&ustrModuleSafe, UserMode, pustrModule);
if(!NT_SUCCESS(Status))
goto done;
_SEH2_TRY _SEH2_TRY
{ {
@ -788,16 +683,65 @@ NtUserFindExistingCursorIcon(
} }
_SEH2_END _SEH2_END
/* Capture resource name (it can be an INTRESOURCE == ATOM) */
Status = ProbeAndCaptureUnicodeStringOrAtom(&ustrRsrcSafe, pustrRsrc);
if(!NT_SUCCESS(Status))
return NULL;
Status = ProbeAndCaptureUnicodeString(&ustrModuleSafe, UserMode, pustrModule);
if(!NT_SUCCESS(Status))
goto done;
Status = RtlLookupAtomInAtomTable(gAtomTable, ustrModuleSafe.Buffer, &atomModName);
ReleaseCapturedUnicodeString(&ustrModuleSafe, UserMode);
if(!NT_SUCCESS(Status))
{
/* The module is not in the atom table. No chance to find the cursor */
goto done;
}
UserEnterExclusive(); UserEnterExclusive();
CurIcon = IntFindExistingCurIconObject(&ustrModuleSafe, &ustrRsrcSafe, &paramSafe); CurIcon = pProcInfo->pCursorCache;
if (CurIcon) while(CurIcon)
Ret = CurIcon->Self; {
/* Icon/cursor */
if (paramSafe.bIcon != is_icon(CurIcon))
{
CurIcon = CurIcon->pcurNext;
continue;
}
/* See if module names match */
if (atomModName == CurIcon->atomModName)
{
/* They do. Now see if this is the same resource */
if (IS_INTRESOURCE(CurIcon->strName.Buffer) != IS_INTRESOURCE(ustrRsrcSafe.Buffer))
{
/* One is an INT resource and the other is not -> no match */
CurIcon = CurIcon->pcurNext;
continue;
}
if (IS_INTRESOURCE(CurIcon->strName.Buffer))
{
if (CurIcon->strName.Buffer == ustrRsrcSafe.Buffer)
{
/* INT resources match */
break;
}
}
else if (RtlCompareUnicodeString(&ustrRsrcSafe, &CurIcon->strName, TRUE) == 0)
{
/* Resource name strings match */
break;
}
}
CurIcon = CurIcon->pcurNext;
}
if(CurIcon)
Ret = CurIcon->head.h;
UserLeave(); UserLeave();
done: done:
if(!IS_INTRESOURCE(ustrRsrcSafe.Buffer)) if(!IS_INTRESOURCE(ustrRsrcSafe.Buffer))
ExFreePoolWithTag(ustrRsrcSafe.Buffer, TAG_STRING); ExFreePoolWithTag(ustrRsrcSafe.Buffer, TAG_STRING);
ReleaseCapturedUnicodeString(&ustrModuleSafe, UserMode);
return Ret; return Ret;
} }
@ -884,9 +828,18 @@ NtUserSetCursor(
pcurOld = UserSetCursor(pcurNew, FALSE); pcurOld = UserSetCursor(pcurNew, FALSE);
if (pcurOld) if (pcurOld)
{ {
hOldCursor = (HCURSOR)pcurOld->Self; hOldCursor = pcurOld->head.h;
pcurOld->CURSORF_flags &= ~CURSORF_CURRENT; pcurOld->CURSORF_flags &= ~CURSORF_CURRENT;
UserDereferenceObject(pcurOld); if(UserObjectInDestroy(hOldCursor))
{
/* Destroy it once and for all */
IntDestroyCurIconObject(pcurOld, TRUE);
hOldCursor = NULL;
}
else
{
UserDereferenceObject(pcurOld);
}
} }
leave: leave:
@ -918,18 +871,17 @@ NtUserSetCursorIconData(
_In_ HCURSOR Handle, _In_ HCURSOR Handle,
_In_opt_ PUNICODE_STRING pustrModule, _In_opt_ PUNICODE_STRING pustrModule,
_In_opt_ PUNICODE_STRING pustrRsrc, _In_opt_ PUNICODE_STRING pustrRsrc,
_In_ PCURSORDATA pCursorData) _In_ const CURSORDATA* pCursorData)
{ {
PCURICON_OBJECT CurIcon; PCURICON_OBJECT CurIcon;
NTSTATUS Status = STATUS_SUCCESS; NTSTATUS Status = STATUS_SUCCESS;
BOOL Ret = FALSE; BOOLEAN Ret = FALSE;
BOOLEAN IsShared = FALSE, IsAnim = FALSE;
DWORD numFrames;
UINT i = 0;
TRACE("Enter NtUserSetCursorIconData\n"); TRACE("Enter NtUserSetCursorIconData\n");
/* If a module name is provided, we need a resource name, and vice versa */
if((pustrModule && !pustrRsrc) || (!pustrModule && pustrRsrc))
return FALSE;
UserEnterExclusive(); UserEnterExclusive();
if (!(CurIcon = UserGetCurIconObject(Handle))) if (!(CurIcon = UserGetCurIconObject(Handle)))
@ -942,15 +894,49 @@ NtUserSetCursorIconData(
_SEH2_TRY _SEH2_TRY
{ {
ProbeForRead(pCursorData, sizeof(*pCursorData), 1); ProbeForRead(pCursorData, sizeof(*pCursorData), 1);
CurIcon->xHotspot = pCursorData->xHotspot; if(pCursorData->CURSORF_flags & CURSORF_ACON)
CurIcon->yHotspot = pCursorData->yHotspot; {
CurIcon->cx = pCursorData->cx; /* This is an animated cursor */
CurIcon->cy = pCursorData->cy; PACON AniCurIcon = (PACON)CurIcon;
CurIcon->rt = pCursorData->rt; DWORD numSteps;
CurIcon->bpp = pCursorData->bpp;
CurIcon->hbmMask = pCursorData->hbmMask; numFrames = AniCurIcon->cpcur = pCursorData->cpcur;
CurIcon->hbmColor = pCursorData->hbmColor; numSteps = AniCurIcon->cicur = pCursorData->cicur;
CurIcon->hbmAlpha = pCursorData->hbmAlpha; AniCurIcon->iicur = pCursorData->iicur;
AniCurIcon->rt = pCursorData->rt;
/* Calculate size: one cursor object for each frame, and a frame index and jiffies for each "step" */
AniCurIcon->aspcur = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, /* Let SEH catch allocation failures */
numFrames * sizeof(CURICON_OBJECT*) + numSteps * (sizeof(DWORD) + sizeof(INT)),
USERTAG_CURSOR);
AniCurIcon->aicur = (DWORD*)(AniCurIcon->aspcur + numFrames);
AniCurIcon->ajifRate = (INT*)(AniCurIcon->aicur + numSteps);
RtlZeroMemory(AniCurIcon->aspcur, numFrames * sizeof(CURICON_OBJECT*));
ProbeForRead(pCursorData->aicur, numSteps * sizeof(DWORD), 1);
RtlCopyMemory(AniCurIcon->aicur, pCursorData->aicur, numSteps * sizeof(DWORD));
ProbeForRead(pCursorData->ajifRate, numSteps * sizeof(INT), 1);
RtlCopyMemory(AniCurIcon->ajifRate, pCursorData->ajifRate, numSteps * sizeof(INT));
AniCurIcon->CURSORF_flags = pCursorData->CURSORF_flags;
pCursorData = pCursorData->aspcur;
IsAnim = TRUE;
}
else
{
CurIcon->xHotspot = pCursorData->xHotspot;
CurIcon->yHotspot = pCursorData->yHotspot;
CurIcon->cx = pCursorData->cx;
CurIcon->cy = pCursorData->cy;
CurIcon->rt = pCursorData->rt;
CurIcon->bpp = pCursorData->bpp;
CurIcon->hbmMask = pCursorData->hbmMask;
CurIcon->hbmColor = pCursorData->hbmColor;
CurIcon->hbmAlpha = pCursorData->hbmAlpha;
CurIcon->CURSORF_flags = pCursorData->CURSORF_flags;
}
} }
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{ {
@ -963,18 +949,42 @@ NtUserSetCursorIconData(
SetLastNtError(Status); SetLastNtError(Status);
goto done; goto done;
} }
if(pustrModule)
{
/* We use this convenient function, because INTRESOURCEs and ATOMs are the same */
Status = ProbeAndCaptureUnicodeStringOrAtom(&CurIcon->strName, pustrRsrc);
if(!NT_SUCCESS(Status))
goto done;
Status = ProbeAndCaptureUnicodeString(&CurIcon->ustrModule, UserMode, pustrModule);
if(!NT_SUCCESS(Status))
goto done;
}
if(IsAnim)
{
PACON AniCurIcon = (PACON)CurIcon;
/* This is an animated cursor. Create a cursor object for each frame and set up the data */
for(i = 0; i < numFrames; i++)
{
HANDLE hCurFrame = IntCreateCurIconHandle(FALSE);
if(!NtUserSetCursorIconData(hCurFrame, NULL, NULL, pCursorData))
goto done;
AniCurIcon->aspcur[i] = UserGetCurIconObject(hCurFrame);
if(!AniCurIcon->aspcur[i])
goto done;
pCursorData++;
}
}
if(CurIcon->CURSORF_flags & CURSORF_LRSHARED)
{
IsShared = TRUE;
if(pustrRsrc && pustrModule)
{
UNICODE_STRING ustrModuleSafe;
/* We use this convenient function, because INTRESOURCEs and ATOMs are the same */
Status = ProbeAndCaptureUnicodeStringOrAtom(&CurIcon->strName, pustrRsrc);
if(!NT_SUCCESS(Status))
goto done;
Status = ProbeAndCaptureUnicodeString(&ustrModuleSafe, UserMode, pustrModule);
if(!NT_SUCCESS(Status))
goto done;
Status = RtlAddAtomToAtomTable(gAtomTable, ustrModuleSafe.Buffer, &CurIcon->atomModName);
ReleaseCapturedUnicodeString(&ustrModuleSafe, UserMode);
if(!NT_SUCCESS(Status))
goto done;
}
}
if(!CurIcon->hbmMask) if(!CurIcon->hbmMask)
{ {
@ -990,17 +1000,41 @@ NtUserSetCursorIconData(
if(CurIcon->hbmAlpha) if(CurIcon->hbmAlpha)
GreSetObjectOwner(CurIcon->hbmAlpha, GDI_OBJ_HMGR_PUBLIC); GreSetObjectOwner(CurIcon->hbmAlpha, GDI_OBJ_HMGR_PUBLIC);
if(IsShared)
{
/* Update process cache in case of shared cursor */
UserReferenceObject(CurIcon);
PPROCESSINFO ppi = CurIcon->head.ppi;
CurIcon->pcurNext = ppi->pCursorCache;
ppi->pCursorCache = CurIcon;
}
Ret = TRUE; Ret = TRUE;
done: done:
if(!Ret) if(!Ret && IsShared)
{ {
if(!IS_INTRESOURCE(CurIcon->strName.Buffer)) if(!IS_INTRESOURCE(CurIcon->strName.Buffer))
ExFreePoolWithTag(CurIcon->strName.Buffer, TAG_STRING); ExFreePoolWithTag(CurIcon->strName.Buffer, TAG_STRING);
if(CurIcon->ustrModule.Buffer)
ReleaseCapturedUnicodeString(&CurIcon->ustrModule, UserMode);
} }
if(!Ret && IsAnim)
{
PACON AniCurIcon = (PACON)CurIcon;
for(i = 0; i < numFrames; i++)
{
if(AniCurIcon->aspcur[i])
IntDestroyCurIconObject(AniCurIcon->aspcur[i], TRUE);
}
AniCurIcon->cicur = 0;
AniCurIcon->cpcur = 0;
ExFreePoolWithTag(AniCurIcon->aspcur, USERTAG_CURSOR);
AniCurIcon->aspcur = NULL;
AniCurIcon->aicur = NULL;
AniCurIcon->ajifRate = NULL;
}
UserDereferenceObject(CurIcon); UserDereferenceObject(CurIcon);
TRACE("Leave NtUserSetCursorIconData, ret=%i\n",Ret); TRACE("Leave NtUserSetCursorIconData, ret=%i\n",Ret);
UserLeave(); UserLeave();
@ -1044,13 +1078,21 @@ UserDrawIconEx(
return FALSE; return FALSE;
} }
if (pIcon->CURSORF_flags & CURSORF_ACON)
{
ACON* pAcon = (ACON*)pIcon;
if(istepIfAniCur >= pAcon->cicur)
{
ERR("NtUserDrawIconEx: istepIfAniCur too big!\n");
return FALSE;
}
pIcon = pAcon->aspcur[pAcon->aicur[istepIfAniCur]];
}
hbmMask = pIcon->hbmMask; hbmMask = pIcon->hbmMask;
hbmColor = pIcon->hbmColor; hbmColor = pIcon->hbmColor;
hbmAlpha = pIcon->hbmAlpha; hbmAlpha = pIcon->hbmAlpha;
if (istepIfAniCur)
ERR("NtUserDrawIconEx: istepIfAniCur is not supported!\n");
/* /*
* Get our objects. * Get our objects.
* Shared locks are enough, we are only reading those bitmaps * Shared locks are enough, we are only reading those bitmaps
@ -1445,4 +1487,74 @@ NtUserDrawIconEx(
return Ret; return Ret;
} }
/*
* @unimplemented
*/
HCURSOR
NTAPI
NtUserGetCursorFrameInfo(
HCURSOR hCursor,
DWORD istep,
INT* rate_jiffies,
DWORD* num_steps)
{
PCURICON_OBJECT CurIcon;
HCURSOR ret;
INT jiffies = 0;
DWORD steps = 1;
NTSTATUS Status = STATUS_SUCCESS;
TRACE("Enter NtUserGetCursorFrameInfo\n");
UserEnterExclusive();
if (!(CurIcon = UserGetCurIconObject(hCursor)))
{
UserLeave();
return NULL;
}
ret = CurIcon->head.h;
if(CurIcon->CURSORF_flags & CURSORF_ACON)
{
PACON AniCurIcon = (PACON)CurIcon;
if(istep >= AniCurIcon->cicur)
{
UserDereferenceObject(CurIcon);
UserLeave();
return NULL;
}
jiffies = AniCurIcon->ajifRate[istep];
steps = AniCurIcon->cicur;
ret = AniCurIcon->aspcur[AniCurIcon->aicur[istep]]->head.h;
}
_SEH2_TRY
{
ProbeForWrite(rate_jiffies, sizeof(INT), 1);
ProbeForWrite(num_steps, sizeof(DWORD), 1);
*rate_jiffies = jiffies;
*num_steps = steps;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
if (!NT_SUCCESS(Status))
{
WARN("Status: 0x%08x.\n", Status);
SetLastNtError(Status);
ret = NULL;
}
UserDereferenceObject(CurIcon);
UserLeave();
TRACE("Leaving NtUserGetCursorFrameInfo, ret = 0x%08x\n", ret);
return ret;
}
/* EOF */ /* EOF */

View file

@ -1247,22 +1247,6 @@ BOOL APIENTRY NtUserGetUpdatedClipboardFormats(
return FALSE; return FALSE;
} }
/*
* @unimplemented
*/
HCURSOR
NTAPI
NtUserGetCursorFrameInfo(
HCURSOR hCursor,
DWORD istep,
PDWORD rate_jiffies,
INT *num_steps)
{
STUB
return 0;
}
/* /*
* @unimplemented * @unimplemented
*/ */

View file

@ -237,17 +237,13 @@ NtUserCallOneParam(
case ONEPARAM_ROUTINE_CREATEEMPTYCUROBJECT: case ONEPARAM_ROUTINE_CREATEEMPTYCUROBJECT:
{ {
PCURICON_OBJECT CurIcon;
DWORD_PTR Result ; DWORD_PTR Result ;
if (!(CurIcon = IntCreateCurIconHandle((DWORD)Param))) if (!(Result = (DWORD_PTR)IntCreateCurIconHandle((DWORD)Param)))
{ {
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
RETURN(0); RETURN(0);
} }
Result = (DWORD_PTR)CurIcon->Self;
UserDereferenceObject(CurIcon);
RETURN(Result); RETURN(Result);
} }

View file

@ -235,6 +235,7 @@ typedef struct _PROCESSINFO
ACCESS_MASK amwinsta; ACCESS_MASK amwinsta;
DWORD dwHotkey; DWORD dwHotkey;
HMONITOR hMonitor; HMONITOR hMonitor;
struct _CURICON_OBJECT* pCursorCache;
LUID luidSession; LUID luidSession;
USERSTARTUPINFO usi; USERSTARTUPINFO usi;
DWORD dwLayout; DWORD dwLayout;

View file

@ -540,15 +540,6 @@ DeviceEventWorker(DWORD dw1, DWORD dw2, DWORD dw3, DWORD dw4, DWORD dw5)
return FALSE; return FALSE;
} }
HCURSOR
WINAPI
GetCursorFrameInfo(HCURSOR hCursor, LPCWSTR name, DWORD istep, PDWORD rate_jiffies, INT *num_steps)
{
if (hCursor) return NtUserGetCursorFrameInfo(hCursor, istep, rate_jiffies, num_steps);
return LoadImageW( NULL, name, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE );
}
BOOL BOOL
WINAPI WINAPI
GetReasonTitleFromReasonCode(DWORD dw1, DWORD dw2, DWORD dw3) GetReasonTitleFromReasonCode(DWORD dw1, DWORD dw2, DWORD dw3)

View file

@ -2207,3 +2207,10 @@ User32CallCopyImageFromKernel(PVOID Arguments, ULONG ArgumentLength)
return ZwCallbackReturn(&Result, sizeof(HANDLE), STATUS_SUCCESS); return ZwCallbackReturn(&Result, sizeof(HANDLE), STATUS_SUCCESS);
} }
HCURSOR
WINAPI
GetCursorFrameInfo(HCURSOR hCursor, DWORD reserved, DWORD istep, PINT rate_jiffies, DWORD *num_steps)
{
return NtUserGetCursorFrameInfo(hCursor, istep, rate_jiffies, num_steps);
}

View file

@ -14,6 +14,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(cursor);
WINE_DECLARE_DEBUG_CHANNEL(icon); WINE_DECLARE_DEBUG_CHANNEL(icon);
//WINE_DECLARE_DEBUG_CHANNEL(resource); //WINE_DECLARE_DEBUG_CHANNEL(resource);
/* We only use Wide string functions */
#undef MAKEINTRESOURCE
#define MAKEINTRESOURCE MAKEINTRESOURCEW
/************* USER32 INTERNAL FUNCTIONS **********/ /************* USER32 INTERNAL FUNCTIONS **********/
/* This callback routine is called directly after switching to gui mode */ /* This callback routine is called directly after switching to gui mode */
@ -28,14 +32,14 @@ User32SetupDefaultCursors(PVOID Arguments,
if(*DefaultCursor) if(*DefaultCursor)
{ {
/* set default cursor */ /* set default cursor */
hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW); hCursor = LoadCursorW(0, IDC_ARROW);
SetCursor(hCursor); SetCursor(hCursor);
} }
else else
{ {
/* FIXME load system cursor scheme */ /* FIXME load system cursor scheme */
SetCursor(0); SetCursor(0);
hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW); hCursor = LoadCursorW(0, IDC_ARROW);
SetCursor(hCursor); SetCursor(hCursor);
} }
@ -304,6 +308,113 @@ done:
return alpha; return alpha;
} }
#include "pshpack1.h"
typedef struct {
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD xHotspot;
WORD yHotspot;
DWORD dwDIBSize;
DWORD dwDIBOffset;
} CURSORICONFILEDIRENTRY;
typedef struct
{
WORD idReserved;
WORD idType;
WORD idCount;
CURSORICONFILEDIRENTRY idEntries[1];
} CURSORICONFILEDIR;
#include "poppack.h"
static
const CURSORICONFILEDIRENTRY*
get_best_icon_file_entry(
_In_ const CURSORICONFILEDIR* dir,
_In_ DWORD dwFileSize,
_In_ int cxDesired,
_In_ int cyDesired,
_In_ BOOL bIcon,
_In_ DWORD fuLoad
)
{
CURSORICONDIR* fakeDir;
CURSORICONDIRENTRY* fakeEntry;
WORD i;
const CURSORICONFILEDIRENTRY* entry;
if ( dwFileSize < sizeof(*dir) )
return NULL;
if ( dwFileSize < (sizeof(*dir) + sizeof(dir->idEntries[0])*(dir->idCount-1)) )
return NULL;
/*
* Cute little hack:
* We allocate a buffer, fake it as if it was a pointer to a resource in a module,
* pass it to LookupIconIdFromDirectoryEx and get back the index we have to use
*/
fakeDir = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(CURSORICONDIR, idEntries[dir->idCount]));
if(!fakeDir)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
fakeDir->idReserved = 0;
fakeDir->idType = dir->idType;
fakeDir->idCount = dir->idCount;
for(i = 0; i<dir->idCount; i++)
{
fakeEntry = &fakeDir->idEntries[i];
entry = &dir->idEntries[i];
/* Take this as an occasion to perform a size check */
if((entry->dwDIBOffset + entry->dwDIBSize) > dwFileSize)
{
ERR("Corrupted icon file?.\n");
HeapFree(GetProcessHeap(), 0, fakeDir);
return NULL;
}
/* File icon/cursors are not like resource ones */
if(bIcon)
{
fakeEntry->ResInfo.icon.bWidth = entry->bWidth;
fakeEntry->ResInfo.icon.bHeight = entry->bHeight;
fakeEntry->ResInfo.icon.bColorCount = 0;
fakeEntry->ResInfo.icon.bReserved = 0;
}
else
{
fakeEntry->ResInfo.cursor.wWidth = entry->bWidth;
fakeEntry->ResInfo.cursor.wHeight = entry->bHeight;
}
/* Let's assume there's always one plane */
fakeEntry->wPlanes = 1;
/* We must get the bitcount from the BITMAPINFOHEADER itself */
fakeEntry->wBitCount = ((BITMAPINFOHEADER *)((char *)dir + entry->dwDIBOffset))->biBitCount;
fakeEntry->dwBytesInRes = entry->dwDIBSize;
fakeEntry->wResId = i + 1;
}
/* Now call LookupIconIdFromResourceEx */
i = LookupIconIdFromDirectoryEx((PBYTE)fakeDir, bIcon, cxDesired, cyDesired, fuLoad & LR_MONOCHROME);
/* We don't need this anymore */
HeapFree(GetProcessHeap(), 0, fakeDir);
if(i == 0)
{
WARN("Unable to get a fit entry index.\n");
return NULL;
}
/* We found it */
return &dir->idEntries[i-1];
}
/************* IMPLEMENTATION CORE ****************/ /************* IMPLEMENTATION CORE ****************/
static BOOL CURSORICON_GetCursorDataFromBMI( static BOOL CURSORICON_GetCursorDataFromBMI(
@ -509,6 +620,257 @@ static BOOL CURSORICON_GetCursorDataFromIconInfo(
return TRUE; return TRUE;
} }
#define RIFF_FOURCC( c0, c1, c2, c3 ) \
( (DWORD)(BYTE)(c0) | ( (DWORD)(BYTE)(c1) << 8 ) | \
( (DWORD)(BYTE)(c2) << 16 ) | ( (DWORD)(BYTE)(c3) << 24 ) )
#define ANI_RIFF_ID RIFF_FOURCC('R', 'I', 'F', 'F')
#define ANI_LIST_ID RIFF_FOURCC('L', 'I', 'S', 'T')
#define ANI_ACON_ID RIFF_FOURCC('A', 'C', 'O', 'N')
#define ANI_anih_ID RIFF_FOURCC('a', 'n', 'i', 'h')
#define ANI_seq__ID RIFF_FOURCC('s', 'e', 'q', ' ')
#define ANI_fram_ID RIFF_FOURCC('f', 'r', 'a', 'm')
#define ANI_rate_ID RIFF_FOURCC('r', 'a', 't', 'e')
#define ANI_FLAG_ICON 0x1
#define ANI_FLAG_SEQUENCE 0x2
#include <pshpack1.h>
typedef struct {
DWORD header_size;
DWORD num_frames;
DWORD num_steps;
DWORD width;
DWORD height;
DWORD bpp;
DWORD num_planes;
DWORD display_rate;
DWORD flags;
} ani_header;
typedef struct {
DWORD data_size;
const unsigned char *data;
} riff_chunk_t;
#include <poppack.h>
static void dump_ani_header( const ani_header *header )
{
TRACE(" header size: %d\n", header->header_size);
TRACE(" frames: %d\n", header->num_frames);
TRACE(" steps: %d\n", header->num_steps);
TRACE(" width: %d\n", header->width);
TRACE(" height: %d\n", header->height);
TRACE(" bpp: %d\n", header->bpp);
TRACE(" planes: %d\n", header->num_planes);
TRACE(" display rate: %d\n", header->display_rate);
TRACE(" flags: 0x%08x\n", header->flags);
}
/* Find an animated cursor chunk, given its type and ID */
static void riff_find_chunk( DWORD chunk_id, DWORD chunk_type, const riff_chunk_t *parent_chunk, riff_chunk_t *chunk )
{
const unsigned char *ptr = parent_chunk->data;
const unsigned char *end = parent_chunk->data + (parent_chunk->data_size - (2 * sizeof(DWORD)));
if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) end -= sizeof(DWORD);
while (ptr < end)
{
if ((!chunk_type && *(const DWORD *)ptr == chunk_id )
|| (chunk_type && *(const DWORD *)ptr == chunk_type && *((const DWORD *)ptr + 2) == chunk_id ))
{
ptr += sizeof(DWORD);
chunk->data_size = (*(const DWORD *)ptr + 1) & ~1;
ptr += sizeof(DWORD);
if (chunk_type == ANI_LIST_ID || chunk_type == ANI_RIFF_ID) ptr += sizeof(DWORD);
chunk->data = ptr;
return;
}
ptr += sizeof(DWORD);
ptr += (*(const DWORD *)ptr + 1) & ~1;
ptr += sizeof(DWORD);
}
}
static BOOL CURSORICON_GetCursorDataFromANI(
_Inout_ CURSORDATA* pCurData,
_In_ const BYTE *pData,
_In_ DWORD dwDataSize,
_In_ DWORD fuLoad
)
{
UINT i;
const ani_header *pHeader;
riff_chunk_t root_chunk = { dwDataSize, pData };
riff_chunk_t ACON_chunk = {0};
riff_chunk_t anih_chunk = {0};
riff_chunk_t fram_chunk = {0};
riff_chunk_t rate_chunk = {0};
riff_chunk_t seq_chunk = {0};
const unsigned char *icon_chunk;
const unsigned char *icon_data;
/* Find the root chunk */
riff_find_chunk( ANI_ACON_ID, ANI_RIFF_ID, &root_chunk, &ACON_chunk );
if (!ACON_chunk.data)
{
ERR("Failed to get root chunk.\n");
return FALSE;
}
/* Find the header chunk */
riff_find_chunk( ANI_anih_ID, 0, &ACON_chunk, &anih_chunk );
if (!ACON_chunk.data)
{
ERR("Failed to get header chunk.\n");
return FALSE;
}
pHeader = (ani_header*)anih_chunk.data;
dump_ani_header(pHeader);
/* Set up the master data */
pCurData->CURSORF_flags |= CURSORF_ACON;
pCurData->cpcur = pHeader->num_frames;
pCurData->cicur = pHeader->num_steps;
pCurData->iicur = pHeader->display_rate;
/* Get the sequences */
if (pHeader->flags & ANI_FLAG_SEQUENCE)
{
riff_find_chunk( ANI_seq__ID, 0, &ACON_chunk, &seq_chunk );
if (!seq_chunk.data)
{
ERR("No sequence data although the flag is set!\n");
return FALSE;
}
}
/* Get the frame rates */
riff_find_chunk( ANI_rate_ID, 0, &ACON_chunk, &rate_chunk );
if (rate_chunk.data)
pCurData->ajifRate = (INT*)rate_chunk.data;
/* Get the frames chunk */
riff_find_chunk( ANI_fram_ID, ANI_LIST_ID, &ACON_chunk, &fram_chunk );
if (!fram_chunk.data)
{
ERR("Failed to get icon list.\n");
return 0;
}
icon_chunk = fram_chunk.data;
icon_data = fram_chunk.data + (2 * sizeof(DWORD));
if(pHeader->num_frames > 1)
{
/* Allocate frame descriptors, step indices and rates */
pCurData->aspcur = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
pHeader->num_frames * sizeof(CURSORDATA) + pHeader->num_steps * (sizeof(DWORD) + sizeof(INT)));
if(!pCurData->aspcur)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
pCurData->aicur = (DWORD*)(pCurData->aspcur + pHeader->num_frames);
pCurData->ajifRate = (INT*)(pCurData->aicur + pHeader->num_steps);
}
for(i=0; i < pHeader->num_frames; i++)
{
CURSORDATA* pFrameData;
const DWORD chunk_size = *(const DWORD *)(icon_chunk + sizeof(DWORD));
const BITMAPINFO* pbmi;
if(pHeader->num_frames > 1)
pFrameData = &pCurData->aspcur[i];
else
pFrameData = pCurData;
pFrameData->rt = pCurData->rt;
if (pHeader->flags & ANI_FLAG_ICON)
{
/* The chunks describe an icon file */
const CURSORICONFILEDIRENTRY* pDirEntry = get_best_icon_file_entry(
(const CURSORICONFILEDIR *) icon_data,
chunk_size,
pCurData->cx,
pCurData->cy,
TRUE,
fuLoad);
if(!pDirEntry)
{
ERR("Unable to find the right file entry for frame %d.\n", i);
goto error;
}
pFrameData->xHotspot = pDirEntry->xHotspot;
pFrameData->yHotspot = pDirEntry->yHotspot;
if(!pHeader->width || !pHeader->height)
{
pFrameData->cx = pDirEntry->bWidth;
pFrameData->cy = pDirEntry->bHeight;
}
else
{
pFrameData->cx = pHeader->width;
pFrameData->cy = pHeader->height;
}
pbmi = (const BITMAPINFO *) (icon_data + pDirEntry->dwDIBOffset);
}
else
{
/* The chunks just describe bitmaps */
pbmi = (const BITMAPINFO *)icon_data;
pFrameData->xHotspot = pFrameData->yHotspot = 0;
}
/* Do the real work */
CURSORICON_GetCursorDataFromBMI(pFrameData, pbmi);
if(pHeader->num_frames > 1)
pFrameData->CURSORF_flags |= CURSORF_ACONFRAME;
else
pFrameData->CURSORF_flags &= ~CURSORF_ACON;
/* Next frame */
icon_chunk += chunk_size + (2 * sizeof(DWORD));
icon_data = icon_chunk + (2 * sizeof(DWORD));
}
if(pHeader->num_frames <= 1)
return TRUE;
if(rate_chunk.data)
CopyMemory(pCurData->ajifRate, rate_chunk.data, pHeader->num_steps * sizeof(INT));
else
{
for(i=0; i < pHeader->num_steps; i++)
pCurData->ajifRate[i] = pHeader->display_rate;
}
if (pHeader->flags & ANI_FLAG_SEQUENCE)
{
CopyMemory(pCurData->aicur, seq_chunk.data, pHeader->num_steps * sizeof(DWORD));
}
else
{
for(i=0; i < pHeader->num_steps; i++)
pCurData->aicur[i] = i;
}
return TRUE;
error:
HeapFree(GetProcessHeap(), 0, pCurData->aspcur);
ZeroMemory(pCurData, sizeof(CURSORDATA));
return FALSE;
}
static static
HBITMAP HBITMAP
BITMAP_LoadImageW( BITMAP_LoadImageW(
@ -561,7 +923,7 @@ BITMAP_LoadImageW(
/* Caller wants an OEM bitmap */ /* Caller wants an OEM bitmap */
if(!hinst) if(!hinst)
hinst = User32Instance; hinst = User32Instance;
hrsrc = FindResourceW(hinst, lpszName, (LPWSTR)RT_BITMAP); hrsrc = FindResourceW(hinst, lpszName, RT_BITMAP);
if(!hrsrc) if(!hrsrc)
return NULL; return NULL;
hgRsrc = LoadResource(hinst, hrsrc); hgRsrc = LoadResource(hinst, hrsrc);
@ -774,28 +1136,6 @@ end:
return hbmpRet; return hbmpRet;
} }
#include "pshpack1.h"
typedef struct {
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD xHotspot;
WORD yHotspot;
DWORD dwDIBSize;
DWORD dwDIBOffset;
} CURSORICONFILEDIRENTRY;
typedef struct
{
WORD idReserved;
WORD idType;
WORD idCount;
CURSORICONFILEDIRENTRY idEntries[1];
} CURSORICONFILEDIR;
#include "poppack.h"
static static
HANDLE HANDLE
@ -807,14 +1147,11 @@ CURSORICON_LoadFromFileW(
_In_ BOOL bIcon _In_ BOOL bIcon
) )
{ {
CURSORICONDIR* fakeDir; const CURSORICONFILEDIRENTRY *entry;
CURSORICONDIRENTRY* fakeEntry; const CURSORICONFILEDIR *dir;
CURSORICONFILEDIRENTRY *entry;
CURSORICONFILEDIR *dir;
DWORD filesize = 0; DWORD filesize = 0;
LPBYTE bits; LPBYTE bits;
HANDLE hCurIcon = NULL; HANDLE hCurIcon = NULL;
WORD i;
CURSORDATA cursorData; CURSORDATA cursorData;
TRACE("loading %s\n", debugstr_w( lpszName )); TRACE("loading %s\n", debugstr_w( lpszName ));
@ -831,63 +1168,10 @@ CURSORICON_LoadFromFileW(
} }
dir = (CURSORICONFILEDIR*) bits; dir = (CURSORICONFILEDIR*) bits;
if ( filesize < sizeof(*dir) ) entry = get_best_icon_file_entry(dir, filesize, cxDesired, cyDesired, bIcon, fuLoad);
goto end; if(!entry)
if ( filesize < (sizeof(*dir) + sizeof(dir->idEntries[0])*(dir->idCount-1)) )
goto end; goto end;
/*
* Cute little hack:
* We allocate a buffer, fake it as if it was a pointer to a resource in a module,
* pass it to LookupIconIdFromDirectoryEx and get back the index we have to use
*/
fakeDir = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(CURSORICONDIR, idEntries[dir->idCount]));
if(!fakeDir)
goto end;
fakeDir->idReserved = 0;
fakeDir->idType = dir->idType;
fakeDir->idCount = dir->idCount;
for(i = 0; i<dir->idCount; i++)
{
fakeEntry = &fakeDir->idEntries[i];
entry = &dir->idEntries[i];
/* Take this as an occasion to perform a size check */
if((entry->dwDIBOffset + entry->dwDIBSize) > filesize)
goto end;
/* File icon/cursors are not like resource ones */
if(bIcon)
{
fakeEntry->ResInfo.icon.bWidth = entry->bWidth;
fakeEntry->ResInfo.icon.bHeight = entry->bHeight;
fakeEntry->ResInfo.icon.bColorCount = 0;
fakeEntry->ResInfo.icon.bReserved = 0;
}
else
{
fakeEntry->ResInfo.cursor.wWidth = entry->bWidth;
fakeEntry->ResInfo.cursor.wHeight = entry->bHeight;
}
/* Let's assume there's always one plane */
fakeEntry->wPlanes = 1;
/* We must get the bitcount from the BITMAPINFOHEADER itself */
fakeEntry->wBitCount = ((BITMAPINFOHEADER *)((char *)dir + entry->dwDIBOffset))->biBitCount;
fakeEntry->dwBytesInRes = entry->dwDIBSize;
fakeEntry->wResId = i + 1;
}
/* Now call LookupIconIdFromResourceEx */
i = LookupIconIdFromDirectoryEx((PBYTE)fakeDir, bIcon, cxDesired, cyDesired, fuLoad & LR_MONOCHROME);
/* We don't need this anymore */
HeapFree(GetProcessHeap(), 0, fakeDir);
if(i == 0)
{
WARN("Unable to get a fit entry index.\n");
goto end;
}
/* Get our entry */
entry = &dir->idEntries[i-1];
/* Fix dimensions */ /* Fix dimensions */
if(!cxDesired) cxDesired = entry->bWidth; if(!cxDesired) cxDesired = entry->bWidth;
if(!cyDesired) cyDesired = entry->bHeight; if(!cyDesired) cyDesired = entry->bHeight;
@ -904,7 +1188,7 @@ CURSORICON_LoadFromFileW(
if(!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)&bits[entry->dwDIBOffset])) if(!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)&bits[entry->dwDIBOffset]))
goto end; goto end;
hCurIcon = NtUserxCreateEmptyCurObject(bIcon ? 0 : 1); hCurIcon = NtUserxCreateEmptyCurObject(FALSE);
if(!hCurIcon) if(!hCurIcon)
goto end_error; goto end_error;
@ -914,12 +1198,12 @@ CURSORICON_LoadFromFileW(
NtUserDestroyCursor(hCurIcon, TRUE); NtUserDestroyCursor(hCurIcon, TRUE);
goto end_error; goto end_error;
} }
end: end:
UnmapViewOfFile(bits); UnmapViewOfFile(bits);
return hCurIcon; return hCurIcon;
/* Clean up */ /* Clean up */
end_error: end_error:
DeleteObject(cursorData.hbmMask); DeleteObject(cursorData.hbmMask);
if(cursorData.hbmColor) DeleteObject(cursorData.hbmColor); if(cursorData.hbmColor) DeleteObject(cursorData.hbmColor);
@ -969,7 +1253,7 @@ CURSORICON_LoadImageW(
{ {
DWORD size = MAX_PATH; DWORD size = MAX_PATH;
FINDEXISTINGCURICONPARAM param; FINDEXISTINGCURICONPARAM param;
TRACE("Checking for an LR_SHARED cursor/icon.\n"); TRACE("Checking for an LR_SHARED cursor/icon.\n");
/* Prepare the resource name string */ /* Prepare the resource name string */
if(IS_INTRESOURCE(lpszName)) if(IS_INTRESOURCE(lpszName))
@ -1020,8 +1304,8 @@ CURSORICON_LoadImageW(
hrsrc = FindResourceW( hrsrc = FindResourceW(
hinst, hinst,
lpszName, lpszName,
(LPWSTR)(bIcon ? RT_GROUP_ICON : RT_GROUP_CURSOR)); bIcon ? RT_GROUP_ICON : RT_GROUP_CURSOR);
/* We let FindResource, LoadResource, etc. call SetLastError */ /* We let FindResource, LoadResource, etc. call SetLastError */
if(!hrsrc) if(!hrsrc)
goto done; goto done;
@ -1041,7 +1325,7 @@ CURSORICON_LoadImageW(
hrsrc = FindResourceW( hrsrc = FindResourceW(
hinst, hinst,
MAKEINTRESOURCEW(wResId), MAKEINTRESOURCEW(wResId),
(LPWSTR)(bIcon ? RT_ICON : RT_CURSOR)); bIcon ? RT_ICON : RT_CURSOR);
if(!hrsrc) if(!hrsrc)
goto done; goto done;
@ -1055,9 +1339,12 @@ CURSORICON_LoadImageW(
FreeResource(handle); FreeResource(handle);
goto done; goto done;
} }
ZeroMemory(&cursorData, sizeof(cursorData)); ZeroMemory(&cursorData, sizeof(cursorData));
/* This is from resource */
cursorData.CURSORF_flags = CURSORF_FROMRESOURCE;
if(dir->idType == 2) if(dir->idType == 2)
{ {
/* idType == 2 for cursor resources */ /* idType == 2 for cursor resources */
@ -1080,11 +1367,8 @@ CURSORICON_LoadImageW(
if(!bStatus) if(!bStatus)
goto done; goto done;
/* This is from resource */
cursorData.CURSORF_flags = CURSORF_FROMRESOURCE;
/* Create the handle */ /* Create the handle */
hCurIcon = NtUserxCreateEmptyCurObject(bIcon ? 0 : 1); hCurIcon = NtUserxCreateEmptyCurObject(FALSE);
if(!hCurIcon) if(!hCurIcon)
{ {
goto end_error; goto end_error;
@ -1352,21 +1636,21 @@ CURSORICON_CopyImage(
do do
{ {
if(!NtUserGetIconInfo(hicon, NULL, &ustrModule, &ustrRsrc, NULL, FALSE)) if (!NtUserGetIconInfo(hicon, NULL, &ustrModule, &ustrRsrc, NULL, FALSE))
{ {
HeapFree(GetProcessHeap(), 0, ustrModule.Buffer); HeapFree(GetProcessHeap(), 0, ustrModule.Buffer);
HeapFree(GetProcessHeap(), 0, pvBuf); HeapFree(GetProcessHeap(), 0, pvBuf);
return NULL; return NULL;
} }
if((ustrModule.Length < ustrModule.MaximumLength) && (ustrRsrc.Length < ustrRsrc.MaximumLength)) if (ustrModule.Length && (ustrRsrc.Length || IS_INTRESOURCE(ustrRsrc.Buffer)))
{ {
/* Buffers were big enough */ /* Buffers were big enough */
break; break;
} }
/* Find which buffer were too small */ /* Find which buffer were too small */
if(ustrModule.Length == ustrModule.MaximumLength) if (!ustrModule.Length)
{ {
PWSTR newBuffer; PWSTR newBuffer;
ustrModule.MaximumLength *= 2; ustrModule.MaximumLength *= 2;
@ -1379,7 +1663,7 @@ CURSORICON_CopyImage(
ustrModule.Buffer = newBuffer; ustrModule.Buffer = newBuffer;
} }
if(ustrRsrc.Length == ustrRsrc.MaximumLength) if (!ustrRsrc.Length)
{ {
ustrRsrc.MaximumLength *= 2; ustrRsrc.MaximumLength *= 2;
pvBuf = HeapReAlloc(GetProcessHeap(), 0, ustrRsrc.Buffer, ustrRsrc.MaximumLength); pvBuf = HeapReAlloc(GetProcessHeap(), 0, ustrRsrc.Buffer, ustrRsrc.MaximumLength);
@ -1400,7 +1684,7 @@ CURSORICON_CopyImage(
/* Get the module handle */ /* Get the module handle */
if(!GetModuleHandleExW(0, ustrModule.Buffer, &hModule)) if(!GetModuleHandleExW(0, ustrModule.Buffer, &hModule))
{ {
/* This hould never happen */ /* This should never happen */
ERR("Invalid handle?.\n"); ERR("Invalid handle?.\n");
SetLastError(ERROR_INVALID_PARAMETER); SetLastError(ERROR_INVALID_PARAMETER);
goto leave; goto leave;
@ -1489,8 +1773,7 @@ HICON WINAPI CopyIcon(
_In_ HICON hIcon _In_ HICON hIcon
) )
{ {
UNIMPLEMENTED; return CURSORICON_CopyImage(hIcon, FALSE, 0, 0, 0);
return NULL;
} }
BOOL WINAPI DrawIcon( BOOL WINAPI DrawIcon(
@ -1687,6 +1970,8 @@ HANDLE WINAPI LoadImageW(
_In_ UINT fuLoad _In_ UINT fuLoad
) )
{ {
TRACE("hinst 0x%p, name %s, uType 0x%08x, cxDesired %d, cyDesired %d, fuLoad 0x%08x.\n",
hinst, debugstr_w(lpszName), uType, cxDesired, cyDesired, fuLoad);
/* Redirect to each implementation */ /* Redirect to each implementation */
switch(uType) switch(uType)
{ {
@ -1735,7 +2020,7 @@ int WINAPI LookupIconIdFromDirectoryEx(
WARN("Invalid resource.\n"); WARN("Invalid resource.\n");
return 0; return 0;
} }
if(Flags & LR_MONOCHROME) if(Flags & LR_MONOCHROME)
bppDesired = 1; bppDesired = 1;
else else
@ -1900,45 +2185,103 @@ HICON WINAPI CreateIconFromResourceEx(
{ {
CURSORDATA cursorData; CURSORDATA cursorData;
HICON hIcon; HICON hIcon;
BOOL isAnimated;
TRACE("%p, %lu, %lu, %lu, %i, %i, %lu.\n", pbIconBits, cbIconBits, fIcon, dwVersion, cxDesired, cyDesired, uFlags); TRACE("%p, %lu, %lu, %lu, %i, %i, %lu.\n", pbIconBits, cbIconBits, fIcon, dwVersion, cxDesired, cyDesired, uFlags);
if(uFlags & ~LR_DEFAULTSIZE)
FIXME("uFlags 0x%08x ignored.\n", uFlags & ~LR_DEFAULTSIZE);
if(uFlags & LR_DEFAULTSIZE) if(uFlags & LR_DEFAULTSIZE)
{ {
if(!cxDesired) cxDesired = GetSystemMetrics(fIcon ? SM_CXICON : SM_CXCURSOR); if(!cxDesired) cxDesired = GetSystemMetrics(fIcon ? SM_CXICON : SM_CXCURSOR);
if(!cyDesired) cyDesired = GetSystemMetrics(fIcon ? SM_CYICON : SM_CYCURSOR); if(!cyDesired) cyDesired = GetSystemMetrics(fIcon ? SM_CYICON : SM_CYCURSOR);
} }
/* Check if this is an animated cursor */
if(!memcmp(pbIconBits, "RIFF", 4))
{
UNIMPLEMENTED;
return NULL;
}
ZeroMemory(&cursorData, sizeof(cursorData)); ZeroMemory(&cursorData, sizeof(cursorData));
cursorData.cx = cxDesired; cursorData.cx = cxDesired;
cursorData.cy = cyDesired; cursorData.cy = cyDesired;
cursorData.rt = (USHORT)((ULONG_PTR)(fIcon ? RT_ICON : RT_CURSOR)); cursorData.rt = (USHORT)((ULONG_PTR)(fIcon ? RT_ICON : RT_CURSOR));
if(!fIcon)
/* Convert to win32k-ready data */
if(!memcmp(pbIconBits, "RIFF", 4))
{ {
WORD* pt = (WORD*)pbIconBits; if(!CURSORICON_GetCursorDataFromANI(&cursorData, pbIconBits, cbIconBits, uFlags))
cursorData.xHotspot = *pt++; {
cursorData.yHotspot = *pt++; ERR("Could not get cursor data from .ani.\n");
pbIconBits = (PBYTE)pt; return NULL;
}
isAnimated = !!(cursorData.CURSORF_flags & CURSORF_ACON);
} }
else
if(!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)pbIconBits))
{ {
ERR("Couldn't fill the CURSORDATA structure.\n"); /* It is possible to pass Icon Directories to this API */
return NULL; int wResId = LookupIconIdFromDirectoryEx(pbIconBits, fIcon, cxDesired, cyDesired, uFlags);
HANDLE ResHandle = NULL;
if(wResId)
{
HINSTANCE hinst;
HRSRC hrsrc;
CURSORICONDIR* pCurIconDir = (CURSORICONDIR*)pbIconBits;
TRACE("Pointer points to a directory structure.\n");
/* So this is a pointer to an icon directory structure. Find the module */
if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCWSTR)pbIconBits,
&hinst))
{
return NULL;
}
/* Check we were given the right type of resource */
if((fIcon && pCurIconDir->idType == 2) || (!fIcon && pCurIconDir->idType == 1))
{
WARN("Got a %s directory pointer, but called for a %s", fIcon ? "cursor" : "icon", fIcon ? "icon" : "cursor");
return NULL;
}
/* Get the relevant resource pointer */
hrsrc = FindResourceW(
hinst,
MAKEINTRESOURCEW(wResId),
fIcon ? RT_ICON : RT_CURSOR);
if (!hrsrc)
return NULL;
ResHandle = LoadResource(hinst, hrsrc);
if (!ResHandle)
return NULL;
pbIconBits = LockResource(ResHandle);
if (!pbIconBits)
{
FreeResource(ResHandle);
return NULL;
}
}
if(!fIcon)
{
WORD* pt = (WORD*)pbIconBits;
cursorData.xHotspot = *pt++;
cursorData.yHotspot = *pt++;
pbIconBits = (PBYTE)pt;
}
if (!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)pbIconBits))
{
ERR("Couldn't fill the CURSORDATA structure.\n");
if (ResHandle)
FreeResource(ResHandle);
return NULL;
}
if (ResHandle)
FreeResource(ResHandle);
isAnimated = FALSE;
} }
if (uFlags & LR_SHARED)
cursorData.CURSORF_flags |= CURSORF_LRSHARED;
hIcon = NtUserxCreateEmptyCurObject(fIcon ? 0 : 1); hIcon = NtUserxCreateEmptyCurObject(isAnimated);
if(!hIcon) if (!hIcon)
goto end_error; goto end_error;
if(!NtUserSetCursorIconData(hIcon, NULL, NULL, &cursorData)) if(!NtUserSetCursorIconData(hIcon, NULL, NULL, &cursorData))
@ -1947,11 +2290,16 @@ HICON WINAPI CreateIconFromResourceEx(
NtUserDestroyCursor(hIcon, TRUE); NtUserDestroyCursor(hIcon, TRUE);
goto end_error; goto end_error;
} }
if(isAnimated)
HeapFree(GetProcessHeap(), 0, cursorData.aspcur);
return hIcon; return hIcon;
/* Clean up */ /* Clean up */
end_error: end_error:
if(isAnimated)
HeapFree(GetProcessHeap(), 0, cursorData.aspcur);
DeleteObject(cursorData.hbmMask); DeleteObject(cursorData.hbmMask);
if(cursorData.hbmColor) DeleteObject(cursorData.hbmColor); if(cursorData.hbmColor) DeleteObject(cursorData.hbmColor);
if(cursorData.hbmAlpha) DeleteObject(cursorData.hbmAlpha); if(cursorData.hbmAlpha) DeleteObject(cursorData.hbmAlpha);
@ -1968,11 +2316,13 @@ HICON WINAPI CreateIconIndirect(
CURSORDATA cursorData; CURSORDATA cursorData;
TRACE("%p.\n", piconinfo); TRACE("%p.\n", piconinfo);
ZeroMemory(&cursorData, sizeof(cursorData));
if(!CURSORICON_GetCursorDataFromIconInfo(&cursorData, piconinfo)) if(!CURSORICON_GetCursorDataFromIconInfo(&cursorData, piconinfo))
return NULL; return NULL;
hiconRet = NtUserxCreateEmptyCurObject(piconinfo->fIcon ? 0 : 1); hiconRet = NtUserxCreateEmptyCurObject(FALSE);
if(!hiconRet) if(!hiconRet)
goto end_error; goto end_error;
@ -2064,3 +2414,10 @@ BOOL WINAPI DestroyCursor(
{ {
return NtUserDestroyCursor(hCursor, FALSE); return NtUserDestroyCursor(hCursor, FALSE);
} }
HCURSOR
WINAPI
GetCursorFrameInfo(HCURSOR hCursor, DWORD reserved, DWORD istep, PINT rate_jiffies, DWORD *num_steps)
{
return NtUserGetCursorFrameInfo(hCursor, istep, rate_jiffies, num_steps);
}