From 26b89c0907246fcf125efc985739a1876e345021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gardou?= Date: Wed, 13 Nov 2013 20:06:01 +0000 Subject: [PATCH] [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 --- reactos/win32ss/include/ntuser.h | 6 +- reactos/win32ss/include/ntusrtyp.h | 2 +- reactos/win32ss/user/ntuser/class.c | 9 + reactos/win32ss/user/ntuser/cursoricon.c | 24 +- reactos/win32ss/user/ntuser/cursoricon.h | 71 +- reactos/win32ss/user/ntuser/cursoricon_new.c | 676 ++++++++++-------- reactos/win32ss/user/ntuser/ntstubs.c | 16 - reactos/win32ss/user/ntuser/simplecall.c | 6 +- reactos/win32ss/user/ntuser/win32.h | 1 + reactos/win32ss/user/user32/misc/stubs.c | 9 - .../win32ss/user/user32/windows/cursoricon.c | 7 + .../user/user32/windows/cursoricon_new.c | 619 ++++++++++++---- 12 files changed, 967 insertions(+), 479 deletions(-) diff --git a/reactos/win32ss/include/ntuser.h b/reactos/win32ss/include/ntuser.h index 862fae52cce..79a8c287b2f 100644 --- a/reactos/win32ss/include/ntuser.h +++ b/reactos/win32ss/include/ntuser.h @@ -2000,8 +2000,8 @@ NTAPI NtUserGetCursorFrameInfo( HCURSOR hCursor, DWORD istep, - PDWORD rate_jiffies, - INT *num_steps); + INT* rate_jiffies, + DWORD* num_steps); BOOL NTAPI @@ -2791,7 +2791,7 @@ NtUserSetCursorIconData( _In_ HCURSOR hCursor, _In_ PUNICODE_STRING pustrModule, _In_ PUNICODE_STRING puSrcName, - _In_ PCURSORDATA pCursorData); + _In_ const CURSORDATA* pCursorData); typedef struct _tagFINDEXISTINGCURICONPARAM { diff --git a/reactos/win32ss/include/ntusrtyp.h b/reactos/win32ss/include/ntusrtyp.h index 27a5aad4840..d686fe0755e 100644 --- a/reactos/win32ss/include/ntusrtyp.h +++ b/reactos/win32ss/include/ntusrtyp.h @@ -90,7 +90,7 @@ typedef struct { union { ICONRESDIR icon; - CURSORDIR cursor; + CURSORRESDIR cursor; } ResInfo; WORD wPlanes; WORD wBitCount; diff --git a/reactos/win32ss/user/ntuser/class.c b/reactos/win32ss/user/ntuser/class.c index 6b11866eb3f..58521045aba 100644 --- a/reactos/win32ss/user/ntuser/class.c +++ b/reactos/win32ss/user/ntuser/class.c @@ -1728,7 +1728,11 @@ IntSetClassMenuName(IN PCLS Class, } //// Do this for now in anticipation of new cursor icon code. +#ifndef NEW_CURSORICON BOOLEAN FASTCALL IntDestroyCurIconObject(PCURICON_OBJECT, PPROCESSINFO); +#else +BOOLEAN FASTCALL IntDestroyCurIconObject(PCURICON_OBJECT, BOOLEAN); +#endif BOOL FASTCALL IntClassDestroyIcon(HANDLE hCurIcon) @@ -1742,8 +1746,13 @@ IntClassDestroyIcon(HANDLE hCurIcon) ERR("hCurIcon was not found!\n"); return FALSE; } +#ifndef NEW_CURSORICON Ret = IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process()); /* Note: IntDestroyCurIconObject will remove our reference for us! */ +#else + /* Note: IntDestroyCurIconObject will remove our reference for us! */ + Ret = IntDestroyCurIconObject(CurIcon, TRUE); +#endif if (!Ret) { ERR("hCurIcon was not Destroyed!\n"); diff --git a/reactos/win32ss/user/ntuser/cursoricon.c b/reactos/win32ss/user/ntuser/cursoricon.c index a4dbce0f021..c3a95b26843 100644 --- a/reactos/win32ss/user/ntuser/cursoricon.c +++ b/reactos/win32ss/user/ntuser/cursoricon.c @@ -203,7 +203,7 @@ IntFindExistingCurIconObject(HMODULE hModule, return NULL; } -PCURICON_OBJECT +HANDLE IntCreateCurIconHandle(DWORD dwNumber) { PCURICON_OBJECT CurIcon; @@ -220,7 +220,7 @@ IntCreateCurIconHandle(DWORD dwNumber) CurIcon->Self = hCurIcon; InitializeListHead(&CurIcon->ProcessList); - if (! ReferenceCurIconByProcess(CurIcon)) + if (!ReferenceCurIconByProcess(CurIcon)) { ERR("Failed to add process\n"); UserDeleteObject(hCurIcon, TYPE_CURSOR); @@ -230,7 +230,9 @@ IntCreateCurIconHandle(DWORD dwNumber) InsertHeadList(&gCurIconList, &CurIcon->ListEntry); - return CurIcon; + UserDereferenceObject(CurIcon); + + return hCurIcon; } BOOLEAN FASTCALL @@ -1519,4 +1521,20 @@ NtUserDrawIconEx( return Ret; } +/* + * @unimplemented + */ +HCURSOR +NTAPI +NtUserGetCursorFrameInfo( + HCURSOR hCursor, + DWORD istep, + INT* rate_jiffies, + DWORD* num_steps) +{ + STUB + + return 0; +} + /* EOF */ diff --git a/reactos/win32ss/user/ntuser/cursoricon.h b/reactos/win32ss/user/ntuser/cursoricon.h index 2d001778a35..0e16e86419b 100644 --- a/reactos/win32ss/user/ntuser/cursoricon.h +++ b/reactos/win32ss/user/ntuser/cursoricon.h @@ -2,40 +2,53 @@ #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 { LIST_ENTRY ListEntry; PPROCESSINFO 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 { PROCMARKHEAD head; @@ -91,7 +104,7 @@ typedef struct _SYSTEM_CURSORINFO } SYSTEM_CURSORINFO, *PSYSTEM_CURSORINFO; BOOL InitCursorImpl(VOID); -PCURICON_OBJECT IntCreateCurIconHandle(DWORD dwNumber); +HANDLE IntCreateCurIconHandle(BOOLEAN Anim); VOID FASTCALL IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process); BOOL UserDrawIconEx(HDC hDc, INT xLeft, INT yTop, PCURICON_OBJECT pIcon, INT cxWidth, diff --git a/reactos/win32ss/user/ntuser/cursoricon_new.c b/reactos/win32ss/user/ntuser/cursoricon_new.c index c481eb56653..f4907a2658b 100644 --- a/reactos/win32ss/user/ntuser/cursoricon_new.c +++ b/reactos/win32ss/user/ntuser/cursoricon_new.c @@ -17,37 +17,16 @@ * Possibly shared by multiple processes * Immune to NtDestroyCursorIcon() * 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 DBG_DEFAULT_CHANNEL(UserIcon); -static LIST_ENTRY gCurIconList; -static PAGED_LOOKASIDE_LIST *pgProcessLookasideList; - SYSTEM_CURSORINFO gSysCursorInfo; BOOL 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.ButtonsDown = 0; gSysCursorInfo.bClipped = FALSE; @@ -84,6 +63,13 @@ PCURICON_OBJECT FASTCALL UserGetCurIconObject(HCURSOR hCurIcon) return NULL; } + if(UserObjectInDestroy(hCurIcon)) + { + ERR("Requesting destroyed cursor.\n"); + EngSetLastError(ERROR_INVALID_CURSOR_HANDLE); + return NULL; + } + CurIcon = (PCURICON_OBJECT)UserReferenceObjectByHandle(hCurIcon, TYPE_CURSOR); if (!CurIcon) { @@ -138,98 +124,19 @@ BOOL UserSetCursorPos( INT x, INT y, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Ho return TRUE; } -/* - * We have to register that this object is in use by the current - * 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) +HANDLE +IntCreateCurIconHandle(BOOLEAN Animated) { PCURICON_OBJECT CurIcon; 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) { @@ -237,134 +144,102 @@ IntCreateCurIconHandle(DWORD dwNumber) return FALSE; } - CurIcon->Self = hCurIcon; - InitializeListHead(&CurIcon->ProcessList); + UserDereferenceObject(CurIcon); - if (! ReferenceCurIconByProcess(CurIcon)) - { - ERR("Failed to add process\n"); - UserDereferenceObject(CurIcon); - UserDeleteObject(hCurIcon, TYPE_CURSOR); - return NULL; - } - - InsertHeadList(&gCurIconList, &CurIcon->ListEntry); - - return CurIcon; + return hCurIcon; } 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) { - UserDereferenceObject(CurIcon); + /* Mark the object as destroyed, and fail, as per wine tests */ + UserDeleteObject(CurIcon->head.h, TYPE_CURSOR); 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 - remove it from that list */ - LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry) + if(CurIcon->head.ppi != PsGetCurrentProcessWin32Process()) { - if (Current->Process == ppi) - { - 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! */ + /* This object doesn't belong to the current process */ + WARN("Trying to delete foreign cursor!\n"); UserDereferenceObject(CurIcon); + EngSetLastError(ERROR_DESTROY_OBJECT_OF_OTHER_THREAD); return FALSE; } - /* We found our process, but we're told to not destroy it in case it is shared */ - if((CurIcon->ustrModule.Buffer != NULL) && !bForce) + /* Do not destroy it if it is shared. (And we're not forced to) */ + if((CurIcon->CURSORF_flags & CURSORF_LRSHARED) && !bForce) { /* Tests show this is a valid call */ + WARN("Trying to destroy shared cursor!\n"); UserDereferenceObject(CurIcon); return TRUE; } - ExFreeToPagedLookasideList(pgProcessLookasideList, Current); - - /* If there are still processes referencing this object we can't destroy it yet */ - if (!bListEmpty) + if(!(CurIcon->CURSORF_flags & CURSORF_ACON)) { - 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 */ - Current = CONTAINING_RECORD(CurIcon->ProcessList.Flink, CURICON_PROCESS, ListEntry); - UserSetObjectOwner(CurIcon, TYPE_CURSOR, Current->Process); + GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED); + GreDeleteObject(bmpMask); + 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; } - -emptyList: - /* Remove it from the list */ - RemoveEntryList(&CurIcon->ListEntry); - - bmpMask = CurIcon->hbmMask; - bmpColor = CurIcon->hbmColor; - bmpAlpha = CurIcon->hbmAlpha; - - /* Delete bitmaps */ - if (bmpMask) + else { - GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED); - GreDeleteObject(bmpMask); - 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); + PACON AniCurIcon = (PACON)CurIcon; + UINT i; - /* 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); - Ret = UserDeleteObject(CurIcon->Self, TYPE_CURSOR); - - return Ret; + return UserDeleteObject(CurIcon->head.h, TYPE_CURSOR); } VOID FASTCALL IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process) { - PCURICON_OBJECT CurIcon, tmp; + PCURICON_OBJECT CurIcon; /* Run through the list of icon objects */ - LIST_FOR_EACH_SAFE(CurIcon, tmp, &gCurIconList, CURICON_OBJECT, ListEntry) + while(Win32Process->pCursorCache) { - UserReferenceObject(CurIcon); - IntDestroyCurIconObject(CurIcon, Win32Process, TRUE); + CurIcon = Win32Process->pCursorCache; + Win32Process->pCursorCache = CurIcon->pcurNext; + ASSERT(CurIcon->head.cLockObj == 2); + IntDestroyCurIconObject(CurIcon, TRUE); } } @@ -410,17 +285,24 @@ NtUserGetIconInfo( /* Give back the icon information */ if(IconInfo) { + PCURICON_OBJECT FrameCurIcon = CurIcon; + if(CurIcon->CURSORF_flags & CURSORF_ACON) + { + /* Get information from first frame. */ + FrameCurIcon = ((PACON)CurIcon)->aspcur[0]; + } + /* Fill data */ - ii.fIcon = is_icon(CurIcon); - ii.xHotspot = CurIcon->xHotspot; - ii.yHotspot = CurIcon->yHotspot; + ii.fIcon = is_icon(FrameCurIcon); + ii.xHotspot = FrameCurIcon->xHotspot; + ii.yHotspot = FrameCurIcon->yHotspot; /* Copy bitmaps */ - ii.hbmMask = BITMAP_CopyBitmap(CurIcon->hbmMask); + ii.hbmMask = BITMAP_CopyBitmap(FrameCurIcon->hbmMask); 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); - colorBpp = CurIcon->bpp; + colorBpp = FrameCurIcon->bpp; /* Copy fields */ _SEH2_TRY @@ -439,63 +321,72 @@ NtUserGetIconInfo( Status = _SEH2_GetExceptionCode(); } _SEH2_END - } - if (!NT_SUCCESS(Status)) - { - WARN("Status: 0x%08x.\n", Status); - SetLastNtError(Status); - goto leave; + if (!NT_SUCCESS(Status)) + { + WARN("Status: 0x%08x.\n", Status); + SetLastNtError(Status); + goto leave; + } } /* Give back the module name */ if(lpModule) { - if(!CurIcon->ustrModule.Buffer) - { - EngSetLastError(ERROR_INVALID_HANDLE); + ULONG BufLen = 0; + if (!CurIcon->atomModName) goto leave; - } - /* Copy what we can */ + + RtlQueryAtomInAtomTable(gAtomTable, CurIcon->atomModName, NULL, NULL, NULL, &BufLen); + /* Get the module name from the atom table */ _SEH2_TRY { - ProbeForWrite(lpModule, sizeof(UNICODE_STRING), 1); - ProbeForWrite(lpModule->Buffer, lpModule->MaximumLength, 1); - lpModule->Length = min(lpModule->MaximumLength, CurIcon->ustrModule.Length); - RtlCopyMemory(lpModule->Buffer, CurIcon->ustrModule.Buffer, lpModule->Length); + if (BufLen > (lpModule->MaximumLength * sizeof(WCHAR))) + { + lpModule->Length = 0; + } + 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) { Status = _SEH2_GetExceptionCode(); } _SEH2_END - } - if (!NT_SUCCESS(Status)) - { - SetLastNtError(Status); - goto leave; - } - - if(lpResName) - { - if(!CurIcon->strName.Buffer) + if (!NT_SUCCESS(Status)) { - EngSetLastError(ERROR_INVALID_HANDLE); + SetLastNtError(Status); goto leave; } + } + + if (lpResName) + { + if (!CurIcon->strName.Buffer) + goto leave; + /* Copy it */ _SEH2_TRY { ProbeForWrite(lpResName, sizeof(UNICODE_STRING), 1); - if(IS_INTRESOURCE(CurIcon->strName.Buffer)) + if (IS_INTRESOURCE(CurIcon->strName.Buffer)) { lpResName->Buffer = CurIcon->strName.Buffer; lpResName->Length = 0; } + else if (lpResName->MaximumLength < CurIcon->strName.Length) + { + lpResName->Length = 0; + } 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); } } @@ -547,6 +438,15 @@ NtUserGetIconSize( 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 { ProbeForWrite(plcx, sizeof(LONG), 1); @@ -597,7 +497,7 @@ NtUserGetCursorInfo( SafeCi.cbSize = sizeof(CURSORINFO); 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; @@ -739,7 +639,7 @@ NtUserDestroyCursor( RETURN(FALSE); } - ret = IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process(), bForce); + ret = IntDestroyCurIconObject(CurIcon, bForce); /* Note: IntDestroyCurIconObject will remove our reference for us! */ RETURN(ret); @@ -766,16 +666,11 @@ NtUserFindExistingCursorIcon( UNICODE_STRING ustrModuleSafe, ustrRsrcSafe; FINDEXISTINGCURICONPARAM paramSafe; NTSTATUS Status; + PPROCESSINFO pProcInfo = PsGetCurrentProcessWin32Process(); + RTL_ATOM atomModName; 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 { @@ -788,16 +683,65 @@ NtUserFindExistingCursorIcon( } _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(); - CurIcon = IntFindExistingCurIconObject(&ustrModuleSafe, &ustrRsrcSafe, ¶mSafe); - if (CurIcon) - Ret = CurIcon->Self; + CurIcon = pProcInfo->pCursorCache; + while(CurIcon) + { + /* 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(); done: if(!IS_INTRESOURCE(ustrRsrcSafe.Buffer)) ExFreePoolWithTag(ustrRsrcSafe.Buffer, TAG_STRING); - ReleaseCapturedUnicodeString(&ustrModuleSafe, UserMode); return Ret; } @@ -884,9 +828,18 @@ NtUserSetCursor( pcurOld = UserSetCursor(pcurNew, FALSE); if (pcurOld) { - hOldCursor = (HCURSOR)pcurOld->Self; + hOldCursor = pcurOld->head.h; 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: @@ -918,18 +871,17 @@ NtUserSetCursorIconData( _In_ HCURSOR Handle, _In_opt_ PUNICODE_STRING pustrModule, _In_opt_ PUNICODE_STRING pustrRsrc, - _In_ PCURSORDATA pCursorData) + _In_ const CURSORDATA* pCursorData) { PCURICON_OBJECT CurIcon; NTSTATUS Status = STATUS_SUCCESS; - BOOL Ret = FALSE; + BOOLEAN Ret = FALSE; + BOOLEAN IsShared = FALSE, IsAnim = FALSE; + DWORD numFrames; + UINT i = 0; 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(); if (!(CurIcon = UserGetCurIconObject(Handle))) @@ -942,15 +894,49 @@ NtUserSetCursorIconData( _SEH2_TRY { ProbeForRead(pCursorData, sizeof(*pCursorData), 1); - 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; + if(pCursorData->CURSORF_flags & CURSORF_ACON) + { + /* This is an animated cursor */ + PACON AniCurIcon = (PACON)CurIcon; + DWORD numSteps; + + numFrames = AniCurIcon->cpcur = pCursorData->cpcur; + numSteps = AniCurIcon->cicur = pCursorData->cicur; + 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) { @@ -963,18 +949,42 @@ NtUserSetCursorIconData( SetLastNtError(Status); 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) { @@ -990,17 +1000,41 @@ NtUserSetCursorIconData( if(CurIcon->hbmAlpha) 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; done: - if(!Ret) + if(!Ret && IsShared) { if(!IS_INTRESOURCE(CurIcon->strName.Buffer)) 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); TRACE("Leave NtUserSetCursorIconData, ret=%i\n",Ret); UserLeave(); @@ -1044,13 +1078,21 @@ UserDrawIconEx( 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; hbmColor = pIcon->hbmColor; hbmAlpha = pIcon->hbmAlpha; - if (istepIfAniCur) - ERR("NtUserDrawIconEx: istepIfAniCur is not supported!\n"); - /* * Get our objects. * Shared locks are enough, we are only reading those bitmaps @@ -1445,4 +1487,74 @@ NtUserDrawIconEx( 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 */ diff --git a/reactos/win32ss/user/ntuser/ntstubs.c b/reactos/win32ss/user/ntuser/ntstubs.c index ff1c07c7bbd..b87c5508913 100644 --- a/reactos/win32ss/user/ntuser/ntstubs.c +++ b/reactos/win32ss/user/ntuser/ntstubs.c @@ -1247,22 +1247,6 @@ BOOL APIENTRY NtUserGetUpdatedClipboardFormats( return FALSE; } -/* - * @unimplemented - */ -HCURSOR -NTAPI -NtUserGetCursorFrameInfo( - HCURSOR hCursor, - DWORD istep, - PDWORD rate_jiffies, - INT *num_steps) -{ - STUB - - return 0; -} - /* * @unimplemented */ diff --git a/reactos/win32ss/user/ntuser/simplecall.c b/reactos/win32ss/user/ntuser/simplecall.c index 2ad6cb19bac..c1b9791cceb 100644 --- a/reactos/win32ss/user/ntuser/simplecall.c +++ b/reactos/win32ss/user/ntuser/simplecall.c @@ -237,17 +237,13 @@ NtUserCallOneParam( case ONEPARAM_ROUTINE_CREATEEMPTYCUROBJECT: { - PCURICON_OBJECT CurIcon; DWORD_PTR Result ; - if (!(CurIcon = IntCreateCurIconHandle((DWORD)Param))) + if (!(Result = (DWORD_PTR)IntCreateCurIconHandle((DWORD)Param))) { EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); RETURN(0); } - - Result = (DWORD_PTR)CurIcon->Self; - UserDereferenceObject(CurIcon); RETURN(Result); } diff --git a/reactos/win32ss/user/ntuser/win32.h b/reactos/win32ss/user/ntuser/win32.h index bfba15062ba..fbbfaca8953 100644 --- a/reactos/win32ss/user/ntuser/win32.h +++ b/reactos/win32ss/user/ntuser/win32.h @@ -235,6 +235,7 @@ typedef struct _PROCESSINFO ACCESS_MASK amwinsta; DWORD dwHotkey; HMONITOR hMonitor; + struct _CURICON_OBJECT* pCursorCache; LUID luidSession; USERSTARTUPINFO usi; DWORD dwLayout; diff --git a/reactos/win32ss/user/user32/misc/stubs.c b/reactos/win32ss/user/user32/misc/stubs.c index 5014042a1cb..d349de50d70 100644 --- a/reactos/win32ss/user/user32/misc/stubs.c +++ b/reactos/win32ss/user/user32/misc/stubs.c @@ -540,15 +540,6 @@ DeviceEventWorker(DWORD dw1, DWORD dw2, DWORD dw3, DWORD dw4, DWORD dw5) 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 WINAPI GetReasonTitleFromReasonCode(DWORD dw1, DWORD dw2, DWORD dw3) diff --git a/reactos/win32ss/user/user32/windows/cursoricon.c b/reactos/win32ss/user/user32/windows/cursoricon.c index 6a6be37fbe8..23a2671725e 100644 --- a/reactos/win32ss/user/user32/windows/cursoricon.c +++ b/reactos/win32ss/user/user32/windows/cursoricon.c @@ -2207,3 +2207,10 @@ User32CallCopyImageFromKernel(PVOID Arguments, ULONG ArgumentLength) 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); +} + diff --git a/reactos/win32ss/user/user32/windows/cursoricon_new.c b/reactos/win32ss/user/user32/windows/cursoricon_new.c index d622d563e24..5f4aa30e574 100644 --- a/reactos/win32ss/user/user32/windows/cursoricon_new.c +++ b/reactos/win32ss/user/user32/windows/cursoricon_new.c @@ -14,6 +14,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(cursor); WINE_DECLARE_DEBUG_CHANNEL(icon); //WINE_DECLARE_DEBUG_CHANNEL(resource); +/* We only use Wide string functions */ +#undef MAKEINTRESOURCE +#define MAKEINTRESOURCE MAKEINTRESOURCEW + /************* USER32 INTERNAL FUNCTIONS **********/ /* This callback routine is called directly after switching to gui mode */ @@ -28,14 +32,14 @@ User32SetupDefaultCursors(PVOID Arguments, if(*DefaultCursor) { /* set default cursor */ - hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW); + hCursor = LoadCursorW(0, IDC_ARROW); SetCursor(hCursor); } else { /* FIXME load system cursor scheme */ SetCursor(0); - hCursor = LoadCursorW(0, (LPCWSTR)IDC_ARROW); + hCursor = LoadCursorW(0, IDC_ARROW); SetCursor(hCursor); } @@ -304,6 +308,113 @@ done: 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; iidCount; 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 ****************/ static BOOL CURSORICON_GetCursorDataFromBMI( @@ -509,6 +620,257 @@ static BOOL CURSORICON_GetCursorDataFromIconInfo( 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 +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 + +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 HBITMAP BITMAP_LoadImageW( @@ -561,7 +923,7 @@ BITMAP_LoadImageW( /* Caller wants an OEM bitmap */ if(!hinst) hinst = User32Instance; - hrsrc = FindResourceW(hinst, lpszName, (LPWSTR)RT_BITMAP); + hrsrc = FindResourceW(hinst, lpszName, RT_BITMAP); if(!hrsrc) return NULL; hgRsrc = LoadResource(hinst, hrsrc); @@ -774,28 +1136,6 @@ end: 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 HANDLE @@ -807,14 +1147,11 @@ CURSORICON_LoadFromFileW( _In_ BOOL bIcon ) { - CURSORICONDIR* fakeDir; - CURSORICONDIRENTRY* fakeEntry; - CURSORICONFILEDIRENTRY *entry; - CURSORICONFILEDIR *dir; + const CURSORICONFILEDIRENTRY *entry; + const CURSORICONFILEDIR *dir; DWORD filesize = 0; LPBYTE bits; HANDLE hCurIcon = NULL; - WORD i; CURSORDATA cursorData; TRACE("loading %s\n", debugstr_w( lpszName )); @@ -831,63 +1168,10 @@ CURSORICON_LoadFromFileW( } dir = (CURSORICONFILEDIR*) bits; - if ( filesize < sizeof(*dir) ) - goto end; - - if ( filesize < (sizeof(*dir) + sizeof(dir->idEntries[0])*(dir->idCount-1)) ) + entry = get_best_icon_file_entry(dir, filesize, cxDesired, cyDesired, bIcon, fuLoad); + if(!entry) 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; iidCount; 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 */ if(!cxDesired) cxDesired = entry->bWidth; if(!cyDesired) cyDesired = entry->bHeight; @@ -904,7 +1188,7 @@ CURSORICON_LoadFromFileW( if(!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)&bits[entry->dwDIBOffset])) goto end; - hCurIcon = NtUserxCreateEmptyCurObject(bIcon ? 0 : 1); + hCurIcon = NtUserxCreateEmptyCurObject(FALSE); if(!hCurIcon) goto end_error; @@ -914,12 +1198,12 @@ CURSORICON_LoadFromFileW( NtUserDestroyCursor(hCurIcon, TRUE); goto end_error; } - + end: UnmapViewOfFile(bits); return hCurIcon; - - /* Clean up */ + + /* Clean up */ end_error: DeleteObject(cursorData.hbmMask); if(cursorData.hbmColor) DeleteObject(cursorData.hbmColor); @@ -969,7 +1253,7 @@ CURSORICON_LoadImageW( { DWORD size = MAX_PATH; FINDEXISTINGCURICONPARAM param; - + TRACE("Checking for an LR_SHARED cursor/icon.\n"); /* Prepare the resource name string */ if(IS_INTRESOURCE(lpszName)) @@ -1020,8 +1304,8 @@ CURSORICON_LoadImageW( hrsrc = FindResourceW( hinst, lpszName, - (LPWSTR)(bIcon ? RT_GROUP_ICON : RT_GROUP_CURSOR)); - + bIcon ? RT_GROUP_ICON : RT_GROUP_CURSOR); + /* We let FindResource, LoadResource, etc. call SetLastError */ if(!hrsrc) goto done; @@ -1041,7 +1325,7 @@ CURSORICON_LoadImageW( hrsrc = FindResourceW( hinst, MAKEINTRESOURCEW(wResId), - (LPWSTR)(bIcon ? RT_ICON : RT_CURSOR)); + bIcon ? RT_ICON : RT_CURSOR); if(!hrsrc) goto done; @@ -1055,9 +1339,12 @@ CURSORICON_LoadImageW( FreeResource(handle); goto done; } - + ZeroMemory(&cursorData, sizeof(cursorData)); - + + /* This is from resource */ + cursorData.CURSORF_flags = CURSORF_FROMRESOURCE; + if(dir->idType == 2) { /* idType == 2 for cursor resources */ @@ -1080,11 +1367,8 @@ CURSORICON_LoadImageW( if(!bStatus) goto done; - /* This is from resource */ - cursorData.CURSORF_flags = CURSORF_FROMRESOURCE; - /* Create the handle */ - hCurIcon = NtUserxCreateEmptyCurObject(bIcon ? 0 : 1); + hCurIcon = NtUserxCreateEmptyCurObject(FALSE); if(!hCurIcon) { goto end_error; @@ -1352,21 +1636,21 @@ CURSORICON_CopyImage( 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, pvBuf); return NULL; } - if((ustrModule.Length < ustrModule.MaximumLength) && (ustrRsrc.Length < ustrRsrc.MaximumLength)) + if (ustrModule.Length && (ustrRsrc.Length || IS_INTRESOURCE(ustrRsrc.Buffer))) { /* Buffers were big enough */ break; } /* Find which buffer were too small */ - if(ustrModule.Length == ustrModule.MaximumLength) + if (!ustrModule.Length) { PWSTR newBuffer; ustrModule.MaximumLength *= 2; @@ -1379,7 +1663,7 @@ CURSORICON_CopyImage( ustrModule.Buffer = newBuffer; } - if(ustrRsrc.Length == ustrRsrc.MaximumLength) + if (!ustrRsrc.Length) { ustrRsrc.MaximumLength *= 2; pvBuf = HeapReAlloc(GetProcessHeap(), 0, ustrRsrc.Buffer, ustrRsrc.MaximumLength); @@ -1400,7 +1684,7 @@ CURSORICON_CopyImage( /* Get the module handle */ if(!GetModuleHandleExW(0, ustrModule.Buffer, &hModule)) { - /* This hould never happen */ + /* This should never happen */ ERR("Invalid handle?.\n"); SetLastError(ERROR_INVALID_PARAMETER); goto leave; @@ -1489,8 +1773,7 @@ HICON WINAPI CopyIcon( _In_ HICON hIcon ) { - UNIMPLEMENTED; - return NULL; + return CURSORICON_CopyImage(hIcon, FALSE, 0, 0, 0); } BOOL WINAPI DrawIcon( @@ -1687,6 +1970,8 @@ HANDLE WINAPI LoadImageW( _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 */ switch(uType) { @@ -1735,7 +2020,7 @@ int WINAPI LookupIconIdFromDirectoryEx( WARN("Invalid resource.\n"); return 0; } - + if(Flags & LR_MONOCHROME) bppDesired = 1; else @@ -1900,45 +2185,103 @@ HICON WINAPI CreateIconFromResourceEx( { CURSORDATA cursorData; HICON hIcon; + BOOL isAnimated; 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(!cxDesired) cxDesired = GetSystemMetrics(fIcon ? SM_CXICON : SM_CXCURSOR); 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)); cursorData.cx = cxDesired; cursorData.cy = cyDesired; 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; - cursorData.xHotspot = *pt++; - cursorData.yHotspot = *pt++; - pbIconBits = (PBYTE)pt; + if(!CURSORICON_GetCursorDataFromANI(&cursorData, pbIconBits, cbIconBits, uFlags)) + { + ERR("Could not get cursor data from .ani.\n"); + return NULL; + } + isAnimated = !!(cursorData.CURSORF_flags & CURSORF_ACON); } - - if(!CURSORICON_GetCursorDataFromBMI(&cursorData, (BITMAPINFO*)pbIconBits)) + else { - ERR("Couldn't fill the CURSORDATA structure.\n"); - return NULL; + /* It is possible to pass Icon Directories to this API */ + 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); - if(!hIcon) + hIcon = NtUserxCreateEmptyCurObject(isAnimated); + if (!hIcon) goto end_error; if(!NtUserSetCursorIconData(hIcon, NULL, NULL, &cursorData)) @@ -1947,11 +2290,16 @@ HICON WINAPI CreateIconFromResourceEx( NtUserDestroyCursor(hIcon, TRUE); goto end_error; } + + if(isAnimated) + HeapFree(GetProcessHeap(), 0, cursorData.aspcur); return hIcon; - + /* Clean up */ end_error: + if(isAnimated) + HeapFree(GetProcessHeap(), 0, cursorData.aspcur); DeleteObject(cursorData.hbmMask); if(cursorData.hbmColor) DeleteObject(cursorData.hbmColor); if(cursorData.hbmAlpha) DeleteObject(cursorData.hbmAlpha); @@ -1968,11 +2316,13 @@ HICON WINAPI CreateIconIndirect( CURSORDATA cursorData; TRACE("%p.\n", piconinfo); + + ZeroMemory(&cursorData, sizeof(cursorData)); if(!CURSORICON_GetCursorDataFromIconInfo(&cursorData, piconinfo)) return NULL; - hiconRet = NtUserxCreateEmptyCurObject(piconinfo->fIcon ? 0 : 1); + hiconRet = NtUserxCreateEmptyCurObject(FALSE); if(!hiconRet) goto end_error; @@ -2064,3 +2414,10 @@ BOOL WINAPI DestroyCursor( { 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); +}