* Sync up to trunk head (r64921).
[reactos.git] / win32ss / user / ntuser / cursoricon_new.c
index c481eb5..2fbfe99 100644 (file)
@@ -2,7 +2,7 @@
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS Win32k subsystem
  * PURPOSE:          Cursor and icon functions
- * FILE:             subsystems/win32/win32k/ntuser/cursoricon.c
+ * FILE:             win32ss/user/ntuser/cursoricon.c
  * PROGRAMER:        ReactOS Team
  */
 /*
  *   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 <win32k.h>
 DBG_DEFAULT_CHANNEL(UserIcon);
 
-static LIST_ENTRY gCurIconList;
-static PAGED_LOOKASIDE_LIST *pgProcessLookasideList;
-
 SYSTEM_CURSORINFO gSysCursorInfo;
 
 BOOL
-InitCursorImpl()
+InitCursorImpl(VOID)
 {
-    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))
+    {
+        WARN("Requesting invalid/destroyed cursor.\n");
+        EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
+        return NULL;
+    }
+
     CurIcon = (PCURICON_OBJECT)UserReferenceObjectByHandle(hCurIcon, TYPE_CURSOR);
     if (!CurIcon)
     {
@@ -138,237 +124,142 @@ 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)
+HANDLE
+IntCreateCurIconHandle(BOOLEAN Animated)
 {
-    PPROCESSINFO Win32Process;
-    PCURICON_PROCESS Current;
-
-    Win32Process = PsGetCurrentProcessWin32Process();
+    PCURICON_OBJECT CurIcon;
+    HANDLE hCurIcon;
 
-    LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
-    {
-        if (Current->Process == Win32Process)
-        {
-            /* Already registered for this process */
-            return TRUE;
-        }
-    }
+    CurIcon = UserCreateObject(
+        gHandleTable,
+        NULL,
+        GetW32ThreadInfo(),
+        &hCurIcon,
+        TYPE_CURSOR,
+        Animated ? sizeof(ACON) : sizeof(CURICON_OBJECT));
 
-    /* Not registered yet */
-    Current = ExAllocateFromPagedLookasideList(pgProcessLookasideList);
-    if (NULL == Current)
+    if (!CurIcon)
     {
+        EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
         return FALSE;
     }
-    InsertHeadList(&CurIcon->ProcessList, &Current->ListEntry);
-    Current->Process = Win32Process;
 
-    return TRUE;
+    UserDereferenceObject(CurIcon);
+
+    return hCurIcon;
 }
 
-static
-PCURICON_OBJECT
-FASTCALL
-IntFindExistingCurIconObject(
-    PUNICODE_STRING pustrModule,
-    PUNICODE_STRING pustrRsrc,
-    FINDEXISTINGCURICONPARAM* param)
+BOOLEAN
+IntDestroyCurIconObject(
+    _In_ PVOID Object)
 {
-    PCURICON_OBJECT CurIcon;
+    PCURICON_OBJECT CurIcon = Object;
 
-    LIST_FOR_EACH(CurIcon, &gCurIconList, CURICON_OBJECT, ListEntry)
+    /* Try finding it in its process cache */
+    if (CurIcon->CURSORF_flags & CURSORF_LRSHARED)
     {
-        /* 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)
+        PPROCESSINFO ppi;
+
+        ppi = CurIcon->head.ppi;
+        if (ppi->pCursorCache == CurIcon)
         {
-            /* 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))
+            ppi->pCursorCache = CurIcon->pcurNext;
+            UserDereferenceObject(CurIcon);
+        }
+        else
+        {
+            PCURICON_OBJECT CacheCurIcon = ppi->pCursorCache;
+            while (CacheCurIcon)
             {
-                if (! ReferenceCurIconByProcess(CurIcon))
+                if (CacheCurIcon->pcurNext == CurIcon)
                 {
-                    return NULL;
+                    CacheCurIcon->pcurNext = CurIcon->pcurNext;
+                    break;
                 }
-
-                return CurIcon;
+                CacheCurIcon = CacheCurIcon->pcurNext;
             }
-        }
-    }
-
-    return NULL;
-}
-
-PCURICON_OBJECT
-IntCreateCurIconHandle(DWORD dwNumber)
-{
-    PCURICON_OBJECT CurIcon;
-    HANDLE hCurIcon;
-    
-    if(dwNumber == 0)
-        dwNumber = 1;
-
-    CurIcon = UserCreateObject(gHandleTable, NULL, NULL, &hCurIcon, TYPE_CURSOR, sizeof(CURICON_OBJECT));
-
-    if (!CurIcon)
-    {
-        EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
-        return FALSE;
-    }
-
-    CurIcon->Self = hCurIcon;
-    InitializeListHead(&CurIcon->ProcessList);
 
-    if (! ReferenceCurIconByProcess(CurIcon))
-    {
-        ERR("Failed to add process\n");
-        UserDereferenceObject(CurIcon);
-        UserDeleteObject(hCurIcon, TYPE_CURSOR);
-        return NULL;
+            /* We must have found it! */
+            ASSERT(CacheCurIcon != NULL);
+            UserDereferenceObject(CurIcon);
+        }
     }
 
-    InsertHeadList(&gCurIconList, &CurIcon->ListEntry);
-
-    return CurIcon;
+    /* We just mark the handle as being destroyed.
+     * Deleting all the stuff will be deferred to the actual struct free. */
+    return UserDeleteObject(CurIcon->head.h, TYPE_CURSOR);
 }
 
-BOOLEAN FASTCALL
-IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, PPROCESSINFO ppi, BOOLEAN bForce)
+void
+FreeCurIconObject(
+    _In_ PVOID Object)
 {
-    HBITMAP bmpMask, bmpColor, bmpAlpha;
-    BOOLEAN Ret, bListEmpty, bFound = FALSE;
-    PCURICON_PROCESS Current = NULL;
+    PCURICON_OBJECT CurIcon = Object;
     
-    /* Check if this is the current cursor */
-    if(CurIcon->CURSORF_flags & CURSORF_CURRENT)
+    if(!(CurIcon->CURSORF_flags & CURSORF_ACON))
     {
-        UserDereferenceObject(CurIcon);
-        return FALSE;
-    }
-    
-    /* For handles created without any data (error handling) */
-    if(IsListEmpty(&CurIcon->ProcessList))
-        goto emptyList;
+        HBITMAP bmpMask = CurIcon->hbmMask;
+        HBITMAP bmpColor = CurIcon->hbmColor;
+        HBITMAP bmpAlpha = CurIcon->hbmAlpha;
 
-    /* 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 (Current->Process == ppi)
+        /* Delete bitmaps */
+        if (bmpMask)
         {
-            bFound = TRUE;
-            bListEmpty = RemoveEntryList(&Current->ListEntry);
-            break;
+            GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED);
+            GreDeleteObject(bmpMask);
+            CurIcon->hbmMask = NULL;
         }
-    }
-    
-    if(!bFound)
-    {
-        /* This object doesn't belong to this process */
-        EngSetLastError(ERROR_INVALID_HANDLE);
-        /* Caller expects us to dereference! */
-        UserDereferenceObject(CurIcon);
-        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)
-    {
-        /* Tests show this is a valid call */
-        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->head.ppi == ppi)
+        if (bmpColor)
         {
-            /* 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(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)
-    {
-        GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED);
-        GreDeleteObject(bmpMask);
-        CurIcon->hbmMask = NULL;
     }
-    if (bmpColor)
+    else
     {
-        GreSetObjectOwner(bmpColor, GDI_OBJ_HMGR_POWNED);
-        GreDeleteObject(bmpColor);
-        CurIcon->hbmColor = NULL;
+        PACON AniCurIcon = (PACON)CurIcon;
+        UINT i;
+
+        for(i = 0; i < AniCurIcon->cpcur; i++)
+            IntDestroyCurIconObject(AniCurIcon->aspcur[i]);
+        ExFreePoolWithTag(AniCurIcon->aspcur, USERTAG_CURSOR);
     }
-    if (bmpAlpha)
+
+    if (CurIcon->CURSORF_flags & CURSORF_LRSHARED)
     {
-        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->atomModName)
+            RtlDeleteAtomFromAtomTable(gAtomTable, CurIcon->atomModName);
+        CurIcon->strName.Buffer = NULL;
+        CurIcon->atomModName = 0;
     }
-    
-    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! */
-    UserDereferenceObject(CurIcon);
-    Ret = UserDeleteObject(CurIcon->Self, TYPE_CURSOR);
-
-    return Ret;
+    /* Finally free the thing */
+    FreeProcMarkObject(CurIcon);
 }
 
 VOID FASTCALL
-IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process)
+IntCleanupCurIconCache(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;
+        UserDereferenceObject(CurIcon);
     }
 }
 
-
 /*
  * @implemented
  */
@@ -410,17 +301,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 +337,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 (!NT_SUCCESS(Status))
+        {
+            SetLastNtError(Status);
+            goto leave;
+        }
     }
     
-    if(lpResName)
+    if (lpResName)
     {
-        if(!CurIcon->strName.Buffer)
-        {
-            EngSetLastError(ERROR_INVALID_HANDLE);
+        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 +454,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 +513,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;
 
@@ -707,7 +623,7 @@ NtUserClipCursor(
         prcl = &rclLocal;
     }
 
-       UserEnterExclusive();
+    UserEnterExclusive();
 
     /* Call the internal function */
     bResult = UserClipCursor(prcl);
@@ -727,27 +643,54 @@ NtUserDestroyCursor(
   _In_   HANDLE hCurIcon,
   _In_   BOOL bForce)
 {
-    PCURICON_OBJECT CurIcon;
     BOOL ret;
-    DECLARE_RETURN(BOOL);
+    PCURICON_OBJECT CurIcon = NULL;
 
-    TRACE("Enter NtUserDestroyCursorIcon\n");
+    TRACE("Enter NtUserDestroyCursorIcon (%p, %u)\n", hCurIcon, bForce);
     UserEnterExclusive();
 
-    if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
+    CurIcon = UserGetCurIconObject(hCurIcon);
+    if (!CurIcon)
     {
-        RETURN(FALSE);
+        ret = FALSE;
+        goto leave;
     }
 
-    ret = IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process(), bForce);
-    /* Note: IntDestroyCurIconObject will remove our reference for us! */
+    if (!bForce)
+    {
+        /* Maybe we have good reasons not to destroy this object */
+        if (CurIcon->head.ppi != PsGetCurrentProcessWin32Process())
+        {
+            /* No way, you're not touching my cursor */
+            ret = FALSE;
+            goto leave;
+        }
+
+        if (CurIcon->CURSORF_flags & CURSORF_CURRENT)
+        {
+            WARN("Trying to delete current cursor!\n");
+            ret = FALSE;
+            goto leave;
+        }
+
+        if (CurIcon->CURSORF_flags & CURSORF_LRSHARED)
+        {
+            WARN("Trying to delete shared cursor.\n");
+            /* This one is not an error */
+            ret = TRUE;
+            goto leave;
+        }
+    }
 
-    RETURN(ret);
+    /* Destroy the handle */
+    ret = IntDestroyCurIconObject(CurIcon);
 
-CLEANUP:
-    TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_);
+leave:
+    if (CurIcon)
+        UserDereferenceObject(CurIcon);
+    TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n", ret);
     UserLeave();
-    END_CLEANUP;
+    return ret;
 }
 
 
@@ -766,16 +709,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 +726,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, &paramSafe);
-    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;
 }
@@ -863,7 +850,7 @@ NtUserSetCursor(
     PCURICON_OBJECT pcurOld, pcurNew;
     HCURSOR hOldCursor = NULL;
 
-    TRACE("Enter NtUserSetCursor\n");
+    TRACE("Enter NtUserSetCursor: %p\n", hCursor);
     UserEnterExclusive();
 
     if (hCursor)
@@ -884,7 +871,10 @@ NtUserSetCursor(
     pcurOld = UserSetCursor(pcurNew, FALSE);
     if (pcurOld)
     {
-        hOldCursor = (HCURSOR)pcurOld->Self;
+        hOldCursor = pcurOld->head.h;
+        /* See if it was destroyed in the meantime */
+        if (UserObjectInDestroy(hOldCursor))
+            hOldCursor = NULL;
         pcurOld->CURSORF_flags &= ~CURSORF_CURRENT;
         UserDereferenceObject(pcurOld);
     }
@@ -918,18 +908,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 +931,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,19 +986,43 @@ NtUserSetCursorIconData(
         SetLastNtError(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(pustrModule)
+    if(CurIcon->CURSORF_flags & CURSORF_LRSHARED)
     {
-        /* 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;
+        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)
     {
         ERR("NtUserSetCursorIconData was got no hbmMask.\n");
@@ -990,21 +1037,45 @@ NtUserSetCursorIconData(
     
     if(CurIcon->hbmAlpha)
         GreSetObjectOwner(CurIcon->hbmAlpha, GDI_OBJ_HMGR_PUBLIC);
+
+    if(IsShared)
+    {
+        /* Update process cache in case of shared cursor */
+        PPROCESSINFO ppi = CurIcon->head.ppi;
+        UserReferenceObject(CurIcon);
+        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]);
+        }
+        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();
-    
+
     return Ret;
 }
 
@@ -1044,13 +1115,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
@@ -1090,7 +1169,7 @@ UserDrawIconEx(
     RECTL_vOffsetRect(&rcDest, pdc->ptlDCOrig.x, pdc->ptlDCOrig.y);
     
     /* Prepare the underlying surface */
-    DC_vPrepareDCsForBlit(pdc, rcDest, NULL, rcDest);
+    DC_vPrepareDCsForBlit(pdc, &rcDest, NULL, NULL);
 
     /* We now have our destination surface and rectangle */
     psurfDest = pdc->dclevel.pSurface;
@@ -1186,7 +1265,7 @@ UserDrawIconEx(
         /* We now have our destination surface */
         psurfDest = psurfOffScreen;
 #else
-        pdcClipObj = pdc->rosdc.CombinedClip;
+        pdcClipObj = &pdc->co.ClipObj;
         /* Paint the brush */
         EBRUSHOBJ_vInit(&eboFill, pbrush, psurfDest, 0x00FFFFFF, 0, NULL);
         
@@ -1217,14 +1296,14 @@ UserDrawIconEx(
     {
         /* We directly draw to the DC */
         TRACE("Performing on screen rendering.\n");
-        pdcClipObj = pdc->rosdc.CombinedClip;
+        pdcClipObj = &pdc->co.ClipObj;
         // psurfOffScreen = NULL;
     }
 
     /* Now do the rendering */
-       if(hbmAlpha && ((diFlags & DI_NORMAL) == DI_NORMAL))
-       {
-           BLENDOBJ blendobj = { {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA } };
+    if(hbmAlpha && ((diFlags & DI_NORMAL) == DI_NORMAL))
+    {
+        BLENDOBJ blendobj = { {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA } };
         PSURFACE psurf = NULL;
 
         psurf = SURFACE_ShareLockSurface(hbmAlpha);
@@ -1249,7 +1328,7 @@ UserDrawIconEx(
         EXLATEOBJ_vCleanup(&exlo);
         SURFACE_ShareUnlockSurface(psurf);
         if(Ret) goto done;
-               ERR("NtGdiAlphaBlend failed!\n");
+        ERR("NtGdiAlphaBlend failed!\n");
     }
 NoAlpha:
     if (diFlags & DI_MASK)
@@ -1282,7 +1361,7 @@ NoAlpha:
 
     if(diFlags & DI_IMAGE)
     {
-               if (psurfColor)
+        if (psurfColor)
         {
             DWORD rop4 = (diFlags & DI_MASK) ? ROP4_SRCINVERT : ROP4_SRCCOPY ;
             
@@ -1445,4 +1524,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 */