2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Cursor and icon functions
5 * FILE: subsystems/win32/win32k/ntuser/cursoricon.c
6 * PROGRAMER: ReactOS Team
9 * We handle two types of cursors/icons:
11 * Loaded without LR_SHARED flag
12 * Private to a process
13 * Can be deleted by calling NtDestroyCursorIcon()
14 * CurIcon->hModule, CurIcon->hRsrc and CurIcon->hGroupRsrc set to NULL
16 * Loaded with LR_SHARED flag
17 * Possibly shared by multiple processes
18 * Immune to NtDestroyCursorIcon()
19 * CurIcon->hModule, CurIcon->hRsrc and CurIcon->hGroupRsrc are valid
23 DBG_DEFAULT_CHANNEL(UserIcon
);
25 SYSTEM_CURSORINFO gSysCursorInfo
;
30 gSysCursorInfo
.Enabled
= FALSE
;
31 gSysCursorInfo
.ButtonsDown
= 0;
32 gSysCursorInfo
.bClipped
= FALSE
;
33 gSysCursorInfo
.LastBtnDown
= 0;
34 gSysCursorInfo
.CurrentCursorObject
= NULL
;
35 gSysCursorInfo
.ShowingCursor
= -1;
36 gSysCursorInfo
.ClickLockActive
= FALSE
;
37 gSysCursorInfo
.ClickLockTime
= 0;
45 return &gSysCursorInfo
;
50 is_icon(PCURICON_OBJECT object
)
52 return MAKEINTRESOURCE(object
->rt
) == RT_ICON
;
55 /* This function creates a reference for the object! */
56 PCURICON_OBJECT FASTCALL
UserGetCurIconObject(HCURSOR hCurIcon
)
58 PCURICON_OBJECT CurIcon
;
62 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
66 if(UserObjectInDestroy(hCurIcon
))
68 ERR("Requesting destroyed cursor.\n");
69 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
73 CurIcon
= (PCURICON_OBJECT
)UserReferenceObjectByHandle(hCurIcon
, TYPE_CURSOR
);
76 /* We never set ERROR_INVALID_ICON_HANDLE. lets hope noone ever checks for it */
77 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
81 ASSERT(CurIcon
->head
.cLockObj
>= 1);
85 BOOL
UserSetCursorPos( INT x
, INT y
, DWORD flags
, ULONG_PTR dwExtraInfo
, BOOL Hook
)
88 PSYSTEM_CURSORINFO CurInfo
;
93 if(!(DesktopWindow
= UserGetDesktopWindow()))
98 CurInfo
= IntGetSysCursorInfo();
100 /* Clip cursor position */
101 if (!CurInfo
->bClipped
)
102 rcClip
= DesktopWindow
->rcClient
;
104 rcClip
= CurInfo
->rcClip
;
106 if(x
>= rcClip
.right
) x
= rcClip
.right
- 1;
107 if(x
< rcClip
.left
) x
= rcClip
.left
;
108 if(y
>= rcClip
.bottom
) y
= rcClip
.bottom
- 1;
109 if(y
< rcClip
.top
) y
= rcClip
.top
;
114 /* 1. Generate a mouse move message, this sets the htEx and Track Window too. */
115 Msg
.message
= WM_MOUSEMOVE
;
116 Msg
.wParam
= UserGetMouseButtonsState();
117 Msg
.lParam
= MAKELPARAM(x
, y
);
119 co_MsqInsertMouseMessage(&Msg
, flags
, dwExtraInfo
, Hook
);
121 /* 2. Store the new cursor position */
128 IntCreateCurIconHandle(BOOLEAN Animated
)
130 PCURICON_OBJECT CurIcon
;
133 CurIcon
= UserCreateObject(
139 Animated
? sizeof(ACON
) : sizeof(CURICON_OBJECT
));
143 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
147 UserDereferenceObject(CurIcon
);
153 IntDestroyCurIconObject(PCURICON_OBJECT CurIcon
, BOOLEAN bForce
)
155 if(CurIcon
->CURSORF_flags
& CURSORF_CURRENT
)
157 /* Mark the object as destroyed, and fail, as per tests */
158 TRACE("Cursor is current, marking as destroyed.\n");
159 UserDeleteObject(CurIcon
->head
.h
, TYPE_CURSOR
);
163 if(CurIcon
->head
.ppi
!= PsGetCurrentProcessWin32Process())
165 /* This object doesn't belong to the current process */
166 WARN("Trying to delete foreign cursor!\n");
167 UserDereferenceObject(CurIcon
);
168 EngSetLastError(ERROR_DESTROY_OBJECT_OF_OTHER_THREAD
);
172 /* Do not destroy it if it is shared. (And we're not forced to) */
173 if((CurIcon
->CURSORF_flags
& CURSORF_LRSHARED
) && !bForce
)
175 /* Tests show this is a valid call */
176 WARN("Trying to destroy shared cursor!\n");
177 UserDereferenceObject(CurIcon
);
181 if(!(CurIcon
->CURSORF_flags
& CURSORF_ACON
))
183 HBITMAP bmpMask
= CurIcon
->hbmMask
;
184 HBITMAP bmpColor
= CurIcon
->hbmColor
;
185 HBITMAP bmpAlpha
= CurIcon
->hbmAlpha
;
190 GreSetObjectOwner(bmpMask
, GDI_OBJ_HMGR_POWNED
);
191 GreDeleteObject(bmpMask
);
192 CurIcon
->hbmMask
= NULL
;
196 GreSetObjectOwner(bmpColor
, GDI_OBJ_HMGR_POWNED
);
197 GreDeleteObject(bmpColor
);
198 CurIcon
->hbmColor
= NULL
;
202 GreSetObjectOwner(bmpAlpha
, GDI_OBJ_HMGR_POWNED
);
203 GreDeleteObject(bmpAlpha
);
204 CurIcon
->hbmAlpha
= NULL
;
209 PACON AniCurIcon
= (PACON
)CurIcon
;
212 for(i
= 0; i
< AniCurIcon
->cpcur
; i
++)
213 IntDestroyCurIconObject(AniCurIcon
->aspcur
[i
], TRUE
);
214 ExFreePoolWithTag(AniCurIcon
->aspcur
, USERTAG_CURSOR
);
217 if (CurIcon
->CURSORF_flags
& CURSORF_LRSHARED
)
219 if (!IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
220 ExFreePoolWithTag(CurIcon
->strName
.Buffer
, TAG_STRING
);
221 if (CurIcon
->atomModName
)
222 RtlDeleteAtomFromAtomTable(gAtomTable
, CurIcon
->atomModName
);
223 CurIcon
->strName
.Buffer
= NULL
;
224 CurIcon
->atomModName
= 0;
227 /* We were given a pointer, no need to keep the reference any longer! */
228 UserDereferenceObject(CurIcon
);
229 return UserDeleteObject(CurIcon
->head
.h
, TYPE_CURSOR
);
233 IntCleanupCurIcons(struct _EPROCESS
*Process
, PPROCESSINFO Win32Process
)
235 PCURICON_OBJECT CurIcon
;
237 /* Run through the list of icon objects */
238 while(Win32Process
->pCursorCache
)
240 CurIcon
= Win32Process
->pCursorCache
;
241 Win32Process
->pCursorCache
= CurIcon
->pcurNext
;
242 /* One ref for the handle, one for the list,
243 * and potentially one from an other process via SetCursor */
244 ASSERT(CurIcon
->head
.cLockObj
<= 3);
245 IntDestroyCurIconObject(CurIcon
, TRUE
);
256 _In_ HANDLE hCurIcon
,
257 _Out_opt_ PICONINFO IconInfo
,
258 _Out_opt_ PUNICODE_STRING lpModule
, // Optional
259 _Out_opt_ PUNICODE_STRING lpResName
, // Optional
260 _Out_opt_ LPDWORD pbpp
, // Optional
264 PCURICON_OBJECT CurIcon
;
265 NTSTATUS Status
= STATUS_SUCCESS
;
269 TRACE("Enter NtUserGetIconInfo\n");
271 /* Check if something was actually asked */
272 if (!IconInfo
&& !lpModule
&& !lpResName
)
274 WARN("Nothing to fill.\n");
275 EngSetLastError(ERROR_INVALID_PARAMETER
);
279 UserEnterExclusive();
281 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
283 WARN("UserGetIconObject(0x%08x) Failed.\n", hCurIcon
);
288 /* Give back the icon information */
291 PCURICON_OBJECT FrameCurIcon
= CurIcon
;
292 if(CurIcon
->CURSORF_flags
& CURSORF_ACON
)
294 /* Get information from first frame. */
295 FrameCurIcon
= ((PACON
)CurIcon
)->aspcur
[0];
299 ii
.fIcon
= is_icon(FrameCurIcon
);
300 ii
.xHotspot
= FrameCurIcon
->xHotspot
;
301 ii
.yHotspot
= FrameCurIcon
->yHotspot
;
304 ii
.hbmMask
= BITMAP_CopyBitmap(FrameCurIcon
->hbmMask
);
305 GreSetObjectOwner(ii
.hbmMask
, GDI_OBJ_HMGR_POWNED
);
306 ii
.hbmColor
= BITMAP_CopyBitmap(FrameCurIcon
->hbmColor
);
307 GreSetObjectOwner(ii
.hbmColor
, GDI_OBJ_HMGR_POWNED
);
308 colorBpp
= FrameCurIcon
->bpp
;
313 ProbeForWrite(IconInfo
, sizeof(ICONINFO
), 1);
314 RtlCopyMemory(IconInfo
, &ii
, sizeof(ICONINFO
));
318 ProbeForWrite(pbpp
, sizeof(DWORD
), 1);
322 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
324 Status
= _SEH2_GetExceptionCode();
328 if (!NT_SUCCESS(Status
))
330 WARN("Status: 0x%08x.\n", Status
);
331 SetLastNtError(Status
);
336 /* Give back the module name */
340 if (!CurIcon
->atomModName
)
343 RtlQueryAtomInAtomTable(gAtomTable
, CurIcon
->atomModName
, NULL
, NULL
, NULL
, &BufLen
);
344 /* Get the module name from the atom table */
347 if (BufLen
> (lpModule
->MaximumLength
* sizeof(WCHAR
)))
349 lpModule
->Length
= 0;
353 ProbeForWrite(lpModule
->Buffer
, lpModule
->MaximumLength
, 1);
354 BufLen
= lpModule
->MaximumLength
* sizeof(WCHAR
);
355 RtlQueryAtomInAtomTable(gAtomTable
, CurIcon
->atomModName
, NULL
, NULL
, lpModule
->Buffer
, &BufLen
);
356 lpModule
->Length
= BufLen
/sizeof(WCHAR
);
359 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
361 Status
= _SEH2_GetExceptionCode();
365 if (!NT_SUCCESS(Status
))
367 SetLastNtError(Status
);
374 if (!CurIcon
->strName
.Buffer
)
380 ProbeForWrite(lpResName
, sizeof(UNICODE_STRING
), 1);
381 if (IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
383 lpResName
->Buffer
= CurIcon
->strName
.Buffer
;
384 lpResName
->Length
= 0;
386 else if (lpResName
->MaximumLength
< CurIcon
->strName
.Length
)
388 lpResName
->Length
= 0;
392 ProbeForWrite(lpResName
->Buffer
, lpResName
->MaximumLength
* sizeof(WCHAR
), 1);
393 RtlCopyMemory(lpResName
->Buffer
, CurIcon
->strName
.Buffer
, lpResName
->Length
);
396 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
398 Status
= _SEH2_GetExceptionCode();
403 if (!NT_SUCCESS(Status
))
405 SetLastNtError(Status
);
412 UserDereferenceObject(CurIcon
);
414 TRACE("Leave NtUserGetIconInfo, ret=%i\n", Ret
);
429 PLONG plcx
, // &size.cx
430 PLONG plcy
) // &size.cy
432 PCURICON_OBJECT CurIcon
;
433 NTSTATUS Status
= STATUS_SUCCESS
;
436 TRACE("Enter NtUserGetIconSize\n");
437 UserEnterExclusive();
439 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
444 if(CurIcon
->CURSORF_flags
& CURSORF_ACON
)
446 /* Use first frame for animated cursors */
447 PACON AniCurIcon
= (PACON
)CurIcon
;
448 CurIcon
= AniCurIcon
->aspcur
[0];
449 UserDereferenceObject(AniCurIcon
);
450 UserReferenceObject(CurIcon
);
455 ProbeForWrite(plcx
, sizeof(LONG
), 1);
457 ProbeForWrite(plcy
, sizeof(LONG
), 1);
460 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
462 Status
= _SEH2_GetExceptionCode();
466 if (NT_SUCCESS(Status
))
469 SetLastNtError(Status
); // Maybe not, test this
471 UserDereferenceObject(CurIcon
);
474 TRACE("Leave NtUserGetIconSize, ret=%i\n", bRet
);
489 PSYSTEM_CURSORINFO CurInfo
;
490 NTSTATUS Status
= STATUS_SUCCESS
;
491 PCURICON_OBJECT CurIcon
;
493 DECLARE_RETURN(BOOL
);
495 TRACE("Enter NtUserGetCursorInfo\n");
496 UserEnterExclusive();
498 CurInfo
= IntGetSysCursorInfo();
499 CurIcon
= (PCURICON_OBJECT
)CurInfo
->CurrentCursorObject
;
501 SafeCi
.cbSize
= sizeof(CURSORINFO
);
502 SafeCi
.flags
= ((CurIcon
&& CurInfo
->ShowingCursor
>= 0) ? CURSOR_SHOWING
: 0);
503 SafeCi
.hCursor
= (CurIcon
? CurIcon
->head
.h
: NULL
);
505 SafeCi
.ptScreenPos
= gpsi
->ptCursor
;
509 if (pci
->cbSize
== sizeof(CURSORINFO
))
511 ProbeForWrite(pci
, sizeof(CURSORINFO
), 1);
512 RtlCopyMemory(pci
, &SafeCi
, sizeof(CURSORINFO
));
517 EngSetLastError(ERROR_INVALID_PARAMETER
);
520 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
522 Status
= _SEH2_GetExceptionCode();
525 if (!NT_SUCCESS(Status
))
527 SetLastNtError(Status
);
533 TRACE("Leave NtUserGetCursorInfo, ret=%i\n",_ret_
);
543 /* FIXME: Check if process has WINSTA_WRITEATTRIBUTES */
544 PSYSTEM_CURSORINFO CurInfo
;
545 PWND DesktopWindow
= NULL
;
547 CurInfo
= IntGetSysCursorInfo();
549 DesktopWindow
= UserGetDesktopWindow();
551 if (prcl
!= NULL
&& DesktopWindow
!= NULL
)
553 if (prcl
->right
< prcl
->left
|| prcl
->bottom
< prcl
->top
)
555 EngSetLastError(ERROR_INVALID_PARAMETER
);
559 CurInfo
->bClipped
= TRUE
;
561 /* Set nw cliping region. Note: we can't use RECTL_bIntersectRect because
562 it sets rect to 0 0 0 0 when it's empty. For more info see monitor winetest */
563 CurInfo
->rcClip
.left
= max(prcl
->left
, DesktopWindow
->rcWindow
.left
);
564 CurInfo
->rcClip
.right
= min(prcl
->right
, DesktopWindow
->rcWindow
.right
);
565 if (CurInfo
->rcClip
.right
< CurInfo
->rcClip
.left
)
566 CurInfo
->rcClip
.right
= CurInfo
->rcClip
.left
;
568 CurInfo
->rcClip
.top
= max(prcl
->top
, DesktopWindow
->rcWindow
.top
);
569 CurInfo
->rcClip
.bottom
= min(prcl
->bottom
, DesktopWindow
->rcWindow
.bottom
);
570 if (CurInfo
->rcClip
.bottom
< CurInfo
->rcClip
.top
)
571 CurInfo
->rcClip
.bottom
= CurInfo
->rcClip
.top
;
573 /* Make sure cursor is in clipping region */
574 UserSetCursorPos(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
, 0, 0, FALSE
);
578 CurInfo
->bClipped
= FALSE
;
599 /* Probe and copy rect */
600 ProbeForRead(prcl
, sizeof(RECTL
), 1);
603 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
605 EngSetLastError(ERROR_INVALID_PARAMETER
);
606 _SEH2_YIELD(return FALSE
;)
613 UserEnterExclusive();
615 /* Call the internal function */
616 bResult
= UserClipCursor(prcl
);
630 _In_ HANDLE hCurIcon
,
633 PCURICON_OBJECT CurIcon
;
635 DECLARE_RETURN(BOOL
);
637 TRACE("Enter NtUserDestroyCursorIcon\n");
638 UserEnterExclusive();
640 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
645 ret
= IntDestroyCurIconObject(CurIcon
, bForce
);
646 /* Note: IntDestroyCurIconObject will remove our reference for us! */
651 TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_
);
662 NtUserFindExistingCursorIcon(
663 _In_ PUNICODE_STRING pustrModule
,
664 _In_ PUNICODE_STRING pustrRsrc
,
665 _In_ FINDEXISTINGCURICONPARAM
* param
)
667 PCURICON_OBJECT CurIcon
;
669 UNICODE_STRING ustrModuleSafe
, ustrRsrcSafe
;
670 FINDEXISTINGCURICONPARAM paramSafe
;
672 PPROCESSINFO pProcInfo
= PsGetCurrentProcessWin32Process();
673 RTL_ATOM atomModName
;
675 TRACE("Enter NtUserFindExistingCursorIcon\n");
680 ProbeForRead(param
, sizeof(*param
), 1);
681 RtlCopyMemory(¶mSafe
, param
, sizeof(paramSafe
));
683 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
685 Status
= _SEH2_GetExceptionCode();
689 /* Capture resource name (it can be an INTRESOURCE == ATOM) */
690 Status
= ProbeAndCaptureUnicodeStringOrAtom(&ustrRsrcSafe
, pustrRsrc
);
691 if(!NT_SUCCESS(Status
))
693 Status
= ProbeAndCaptureUnicodeString(&ustrModuleSafe
, UserMode
, pustrModule
);
694 if(!NT_SUCCESS(Status
))
696 Status
= RtlLookupAtomInAtomTable(gAtomTable
, ustrModuleSafe
.Buffer
, &atomModName
);
697 ReleaseCapturedUnicodeString(&ustrModuleSafe
, UserMode
);
698 if(!NT_SUCCESS(Status
))
700 /* The module is not in the atom table. No chance to find the cursor */
704 UserEnterExclusive();
705 CurIcon
= pProcInfo
->pCursorCache
;
709 if (paramSafe
.bIcon
!= is_icon(CurIcon
))
711 CurIcon
= CurIcon
->pcurNext
;
714 /* See if module names match */
715 if (atomModName
== CurIcon
->atomModName
)
717 /* They do. Now see if this is the same resource */
718 if (IS_INTRESOURCE(CurIcon
->strName
.Buffer
) != IS_INTRESOURCE(ustrRsrcSafe
.Buffer
))
720 /* One is an INT resource and the other is not -> no match */
721 CurIcon
= CurIcon
->pcurNext
;
725 if (IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
727 if (CurIcon
->strName
.Buffer
== ustrRsrcSafe
.Buffer
)
729 /* INT resources match */
733 else if (RtlCompareUnicodeString(&ustrRsrcSafe
, &CurIcon
->strName
, TRUE
) == 0)
735 /* Resource name strings match */
739 CurIcon
= CurIcon
->pcurNext
;
742 Ret
= CurIcon
->head
.h
;
746 if(!IS_INTRESOURCE(ustrRsrcSafe
.Buffer
))
747 ExFreePoolWithTag(ustrRsrcSafe
.Buffer
, TAG_STRING
);
761 /* FIXME: Check if process has WINSTA_READATTRIBUTES */
762 PSYSTEM_CURSORINFO CurInfo
;
765 DECLARE_RETURN(BOOL
);
767 TRACE("Enter NtUserGetClipCursor\n");
768 UserEnterExclusive();
773 CurInfo
= IntGetSysCursorInfo();
774 if (CurInfo
->bClipped
)
776 Rect
= CurInfo
->rcClip
;
782 Rect
.right
= UserGetSystemMetrics(SM_CXSCREEN
);
783 Rect
.bottom
= UserGetSystemMetrics(SM_CYSCREEN
);
786 Status
= MmCopyToCaller(lpRect
, &Rect
, sizeof(RECT
));
787 if (!NT_SUCCESS(Status
))
789 SetLastNtError(Status
);
796 TRACE("Leave NtUserGetClipCursor, ret=%i\n",_ret_
);
810 PCURICON_OBJECT pcurOld
, pcurNew
;
811 HCURSOR hOldCursor
= NULL
;
813 TRACE("Enter NtUserSetCursor\n");
814 UserEnterExclusive();
818 pcurNew
= UserGetCurIconObject(hCursor
);
821 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
824 pcurNew
->CURSORF_flags
|= CURSORF_CURRENT
;
831 pcurOld
= UserSetCursor(pcurNew
, FALSE
);
834 hOldCursor
= pcurOld
->head
.h
;
835 pcurOld
->CURSORF_flags
&= ~CURSORF_CURRENT
;
836 if(UserObjectInDestroy(hOldCursor
))
838 /* Destroy it once and for all */
839 IntDestroyCurIconObject(pcurOld
, TRUE
);
844 UserDereferenceObject(pcurOld
);
859 NtUserSetCursorContents(
861 PICONINFO UnsafeIconInfo
)
863 FIXME(" is UNIMPLEMENTED.\n");
873 NtUserSetCursorIconData(
875 _In_opt_ PUNICODE_STRING pustrModule
,
876 _In_opt_ PUNICODE_STRING pustrRsrc
,
877 _In_
const CURSORDATA
* pCursorData
)
879 PCURICON_OBJECT CurIcon
;
880 NTSTATUS Status
= STATUS_SUCCESS
;
882 BOOLEAN IsShared
= FALSE
, IsAnim
= FALSE
;
886 TRACE("Enter NtUserSetCursorIconData\n");
888 UserEnterExclusive();
890 if (!(CurIcon
= UserGetCurIconObject(Handle
)))
893 EngSetLastError(ERROR_INVALID_HANDLE
);
899 ProbeForRead(pCursorData
, sizeof(*pCursorData
), 1);
900 if(pCursorData
->CURSORF_flags
& CURSORF_ACON
)
902 /* This is an animated cursor */
903 PACON AniCurIcon
= (PACON
)CurIcon
;
906 numFrames
= AniCurIcon
->cpcur
= pCursorData
->cpcur
;
907 numSteps
= AniCurIcon
->cicur
= pCursorData
->cicur
;
908 AniCurIcon
->iicur
= pCursorData
->iicur
;
909 AniCurIcon
->rt
= pCursorData
->rt
;
911 /* Calculate size: one cursor object for each frame, and a frame index and jiffies for each "step" */
912 AniCurIcon
->aspcur
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
, /* Let SEH catch allocation failures */
913 numFrames
* sizeof(CURICON_OBJECT
*) + numSteps
* (sizeof(DWORD
) + sizeof(INT
)),
915 AniCurIcon
->aicur
= (DWORD
*)(AniCurIcon
->aspcur
+ numFrames
);
916 AniCurIcon
->ajifRate
= (INT
*)(AniCurIcon
->aicur
+ numSteps
);
918 RtlZeroMemory(AniCurIcon
->aspcur
, numFrames
* sizeof(CURICON_OBJECT
*));
920 ProbeForRead(pCursorData
->aicur
, numSteps
* sizeof(DWORD
), 1);
921 RtlCopyMemory(AniCurIcon
->aicur
, pCursorData
->aicur
, numSteps
* sizeof(DWORD
));
922 ProbeForRead(pCursorData
->ajifRate
, numSteps
* sizeof(INT
), 1);
923 RtlCopyMemory(AniCurIcon
->ajifRate
, pCursorData
->ajifRate
, numSteps
* sizeof(INT
));
925 AniCurIcon
->CURSORF_flags
= pCursorData
->CURSORF_flags
;
926 pCursorData
= pCursorData
->aspcur
;
932 CurIcon
->xHotspot
= pCursorData
->xHotspot
;
933 CurIcon
->yHotspot
= pCursorData
->yHotspot
;
934 CurIcon
->cx
= pCursorData
->cx
;
935 CurIcon
->cy
= pCursorData
->cy
;
936 CurIcon
->rt
= pCursorData
->rt
;
937 CurIcon
->bpp
= pCursorData
->bpp
;
938 CurIcon
->hbmMask
= pCursorData
->hbmMask
;
939 CurIcon
->hbmColor
= pCursorData
->hbmColor
;
940 CurIcon
->hbmAlpha
= pCursorData
->hbmAlpha
;
941 CurIcon
->CURSORF_flags
= pCursorData
->CURSORF_flags
;
944 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
946 Status
= _SEH2_GetExceptionCode();
950 if (!NT_SUCCESS(Status
))
952 SetLastNtError(Status
);
958 PACON AniCurIcon
= (PACON
)CurIcon
;
959 /* This is an animated cursor. Create a cursor object for each frame and set up the data */
960 for(i
= 0; i
< numFrames
; i
++)
962 HANDLE hCurFrame
= IntCreateCurIconHandle(FALSE
);
963 if(!NtUserSetCursorIconData(hCurFrame
, NULL
, NULL
, pCursorData
))
965 AniCurIcon
->aspcur
[i
] = UserGetCurIconObject(hCurFrame
);
966 if(!AniCurIcon
->aspcur
[i
])
972 if(CurIcon
->CURSORF_flags
& CURSORF_LRSHARED
)
975 if(pustrRsrc
&& pustrModule
)
977 UNICODE_STRING ustrModuleSafe
;
978 /* We use this convenient function, because INTRESOURCEs and ATOMs are the same */
979 Status
= ProbeAndCaptureUnicodeStringOrAtom(&CurIcon
->strName
, pustrRsrc
);
980 if(!NT_SUCCESS(Status
))
982 Status
= ProbeAndCaptureUnicodeString(&ustrModuleSafe
, UserMode
, pustrModule
);
983 if(!NT_SUCCESS(Status
))
985 Status
= RtlAddAtomToAtomTable(gAtomTable
, ustrModuleSafe
.Buffer
, &CurIcon
->atomModName
);
986 ReleaseCapturedUnicodeString(&ustrModuleSafe
, UserMode
);
987 if(!NT_SUCCESS(Status
))
992 if(!CurIcon
->hbmMask
)
994 ERR("NtUserSetCursorIconData was got no hbmMask.\n");
995 EngSetLastError(ERROR_INVALID_PARAMETER
);
999 GreSetObjectOwner(CurIcon
->hbmMask
, GDI_OBJ_HMGR_PUBLIC
);
1001 if(CurIcon
->hbmColor
)
1002 GreSetObjectOwner(CurIcon
->hbmColor
, GDI_OBJ_HMGR_PUBLIC
);
1004 if(CurIcon
->hbmAlpha
)
1005 GreSetObjectOwner(CurIcon
->hbmAlpha
, GDI_OBJ_HMGR_PUBLIC
);
1009 /* Update process cache in case of shared cursor */
1010 PPROCESSINFO ppi
= CurIcon
->head
.ppi
;
1011 UserReferenceObject(CurIcon
);
1012 CurIcon
->pcurNext
= ppi
->pCursorCache
;
1013 ppi
->pCursorCache
= CurIcon
;
1019 if(!Ret
&& IsShared
)
1021 if(!IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
1022 ExFreePoolWithTag(CurIcon
->strName
.Buffer
, TAG_STRING
);
1027 PACON AniCurIcon
= (PACON
)CurIcon
;
1028 for(i
= 0; i
< numFrames
; i
++)
1030 if(AniCurIcon
->aspcur
[i
])
1031 IntDestroyCurIconObject(AniCurIcon
->aspcur
[i
], TRUE
);
1033 AniCurIcon
->cicur
= 0;
1034 AniCurIcon
->cpcur
= 0;
1035 ExFreePoolWithTag(AniCurIcon
->aspcur
, USERTAG_CURSOR
);
1036 AniCurIcon
->aspcur
= NULL
;
1037 AniCurIcon
->aicur
= NULL
;
1038 AniCurIcon
->ajifRate
= NULL
;
1041 UserDereferenceObject(CurIcon
);
1042 TRACE("Leave NtUserSetCursorIconData, ret=%i\n",Ret
);
1048 /* Mostly inspired from wine code.
1049 * We use low level functions because:
1050 * - at this point, the icon bitmap could have a different bit depth than the DC,
1051 * making it thus impossible to use NtCreateCompatibleDC and selecting the bitmap.
1052 * This happens after a mode setting change.
1053 * - it avoids massive GDI objects locking when only the destination surface needs it.
1054 * - It makes (small) performance gains.
1061 PCURICON_OBJECT pIcon
,
1065 HBRUSH hbrFlickerFreeDraw
,
1068 PSURFACE psurfDest
, psurfMask
, psurfColor
; //, psurfOffScreen = NULL;
1071 HBITMAP hbmMask
, hbmColor
, hbmAlpha
;
1073 RECTL rcDest
, rcSrc
;
1074 CLIPOBJ
* pdcClipObj
= NULL
;
1078 if((diFlags
& DI_NORMAL
) == 0)
1080 ERR("DrawIconEx called without mask or color bitmap to draw.\n");
1084 if (pIcon
->CURSORF_flags
& CURSORF_ACON
)
1086 ACON
* pAcon
= (ACON
*)pIcon
;
1087 if(istepIfAniCur
>= pAcon
->cicur
)
1089 ERR("NtUserDrawIconEx: istepIfAniCur too big!\n");
1092 pIcon
= pAcon
->aspcur
[pAcon
->aicur
[istepIfAniCur
]];
1095 hbmMask
= pIcon
->hbmMask
;
1096 hbmColor
= pIcon
->hbmColor
;
1097 hbmAlpha
= pIcon
->hbmAlpha
;
1101 * Shared locks are enough, we are only reading those bitmaps
1103 psurfMask
= SURFACE_ShareLockSurface(hbmMask
);
1104 if(psurfMask
== NULL
)
1106 ERR("Unable to lock the mask surface.\n");
1110 /* Color bitmap is not mandatory */
1111 if(hbmColor
== NULL
)
1113 /* But then the mask bitmap must have the information in it's bottom half */
1114 ASSERT(psurfMask
->SurfObj
.sizlBitmap
.cy
== 2*pIcon
->cy
);
1117 else if ((psurfColor
= SURFACE_ShareLockSurface(hbmColor
)) == NULL
)
1119 ERR("Unable to lock the color bitmap.\n");
1120 SURFACE_ShareUnlockSurface(psurfMask
);
1124 pdc
= DC_LockDc(hDc
);
1127 ERR("Could not lock the destination DC.\n");
1128 SURFACE_ShareUnlockSurface(psurfMask
);
1129 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1132 /* Calculate destination rectangle */
1133 RECTL_vSetRect(&rcDest
, xLeft
, yTop
, xLeft
+ cxWidth
, yTop
+ cyHeight
);
1134 IntLPtoDP(pdc
, (LPPOINT
)&rcDest
, 2);
1135 RECTL_vOffsetRect(&rcDest
, pdc
->ptlDCOrig
.x
, pdc
->ptlDCOrig
.y
);
1137 /* Prepare the underlying surface */
1138 DC_vPrepareDCsForBlit(pdc
, rcDest
, NULL
, rcDest
);
1140 /* We now have our destination surface and rectangle */
1141 psurfDest
= pdc
->dclevel
.pSurface
;
1143 if(psurfDest
== NULL
)
1146 DC_vFinishBlit(pdc
, NULL
);
1148 SURFACE_ShareUnlockSurface(psurfMask
);
1149 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1153 /* Set source rect */
1154 RECTL_vSetRect(&rcSrc
, 0, 0, pIcon
->cx
, pIcon
->cy
);
1156 /* Fix width parameter, if needed */
1159 if(diFlags
& DI_DEFAULTSIZE
)
1160 cxWidth
= is_icon(pIcon
) ?
1161 UserGetSystemMetrics(SM_CXICON
) : UserGetSystemMetrics(SM_CXCURSOR
);
1163 cxWidth
= pIcon
->cx
;
1166 /* Fix height parameter, if needed */
1169 if(diFlags
& DI_DEFAULTSIZE
)
1170 cyHeight
= is_icon(pIcon
) ?
1171 UserGetSystemMetrics(SM_CYICON
) : UserGetSystemMetrics(SM_CYCURSOR
);
1173 cyHeight
= pIcon
->cy
;
1176 /* Should we render off-screen? */
1177 bOffScreen
= hbrFlickerFreeDraw
&&
1178 (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw
) == GDI_OBJECT_TYPE_BRUSH
);
1182 /* Yes: Allocate and paint the offscreen surface */
1184 PBRUSH pbrush
= BRUSH_ShareLockBrush(hbrFlickerFreeDraw
);
1186 TRACE("Performing off-screen rendering.\n");
1190 ERR("Failed to get brush object.\n");
1194 #if 0 //We lock the hdc surface during the whole function it makes no sense to use an offscreen surface for "flicker free" drawing
1195 psurfOffScreen
= SURFACE_AllocSurface(STYPE_BITMAP
,
1196 cxWidth
, cyHeight
, psurfDest
->SurfObj
.iBitmapFormat
,
1200 ERR("Failed to allocate the off-screen surface.\n");
1201 BRUSH_ShareUnlockBrush(pbrush
);
1205 /* Paint the brush */
1206 EBRUSHOBJ_vInit(&eboFill
, pbrush
, psurfOffScreen
, 0x00FFFFFF, 0, NULL
);
1207 RECTL_vSetRect(&rcDest
, 0, 0, cxWidth
, cyHeight
);
1209 Ret
= IntEngBitBlt(&psurfOffScreen
->SurfObj
,
1217 &eboFill
.BrushObject
,
1221 /* Clean up everything */
1222 EBRUSHOBJ_vCleanup(&eboFill
);
1223 BRUSH_ShareUnlockBrush(pbrush
);
1227 ERR("Failed to paint the off-screen surface.\n");
1231 /* We now have our destination surface */
1232 psurfDest
= psurfOffScreen
;
1234 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1235 /* Paint the brush */
1236 EBRUSHOBJ_vInit(&eboFill
, pbrush
, psurfDest
, 0x00FFFFFF, 0, NULL
);
1238 Ret
= IntEngBitBlt(&psurfDest
->SurfObj
,
1246 &eboFill
.BrushObject
,
1250 /* Clean up everything */
1251 EBRUSHOBJ_vCleanup(&eboFill
);
1252 BRUSH_ShareUnlockBrush(pbrush
);
1256 ERR("Failed to paint the off-screen surface.\n");
1263 /* We directly draw to the DC */
1264 TRACE("Performing on screen rendering.\n");
1265 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1266 // psurfOffScreen = NULL;
1269 /* Now do the rendering */
1270 if(hbmAlpha
&& ((diFlags
& DI_NORMAL
) == DI_NORMAL
))
1272 BLENDOBJ blendobj
= { {AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
} };
1273 PSURFACE psurf
= NULL
;
1275 psurf
= SURFACE_ShareLockSurface(hbmAlpha
);
1278 ERR("SURFACE_LockSurface failed!\n");
1282 /* Initialize color translation object */
1283 EXLATEOBJ_vInitialize(&exlo
, psurf
->ppal
, psurfDest
->ppal
, 0xFFFFFFFF, 0xFFFFFFFF, 0);
1286 Ret
= IntEngAlphaBlend(&psurfDest
->SurfObj
,
1294 EXLATEOBJ_vCleanup(&exlo
);
1295 SURFACE_ShareUnlockSurface(psurf
);
1297 ERR("NtGdiAlphaBlend failed!\n");
1300 if (diFlags
& DI_MASK
)
1302 DWORD rop4
= (diFlags
& DI_IMAGE
) ? ROP4_SRCAND
: ROP4_SRCCOPY
;
1304 EXLATEOBJ_vInitSrcMonoXlate(&exlo
, psurfDest
->ppal
, 0x00FFFFFF, 0);
1306 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1307 &psurfMask
->SurfObj
,
1319 EXLATEOBJ_vCleanup(&exlo
);
1323 ERR("Failed to mask the bitmap data.\n");
1328 if(diFlags
& DI_IMAGE
)
1332 DWORD rop4
= (diFlags
& DI_MASK
) ? ROP4_SRCINVERT
: ROP4_SRCCOPY
;
1334 EXLATEOBJ_vInitialize(&exlo
, psurfColor
->ppal
, psurfDest
->ppal
, 0x00FFFFFF, 0x00FFFFFF, 0);
1336 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1337 &psurfColor
->SurfObj
,
1349 EXLATEOBJ_vCleanup(&exlo
);
1353 ERR("Failed to render the icon bitmap.\n");
1359 /* Mask bitmap holds the information in its bottom half */
1360 DWORD rop4
= (diFlags
& DI_MASK
) ? ROP4_SRCINVERT
: ROP4_SRCCOPY
;
1361 RECTL_vOffsetRect(&rcSrc
, 0, pIcon
->cy
);
1363 EXLATEOBJ_vInitSrcMonoXlate(&exlo
, psurfDest
->ppal
, 0x00FFFFFF, 0);
1365 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1366 &psurfMask
->SurfObj
,
1378 EXLATEOBJ_vCleanup(&exlo
);
1382 ERR("Failed to render the icon bitmap.\n");
1390 /* We're done. Was it a double buffered draw ? */
1393 /* Yes. Draw it back to our DC */
1394 POINTL ptSrc
= {0, 0};
1396 /* Calculate destination rectangle */
1397 RECTL_vSetRect(&rcDest
, xLeft
, yTop
, xLeft
+ cxWidth
, yTop
+ cyHeight
);
1398 IntLPtoDP(pdc
, (LPPOINT
)&rcDest
, 2);
1399 RECTL_vOffsetRect(&rcDest
, pdc
->ptlDCOrig
.x
, pdc
->ptlDCOrig
.y
);
1401 /* Get the clip object */
1402 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1404 /* We now have our destination surface and rectangle */
1405 psurfDest
= pdc
->dclevel
.pSurface
;
1407 /* Color translation */
1408 EXLATEOBJ_vInitialize(&exlo
, psurfOffScreen
->ppal
, psurfDest
->ppal
, 0x00FFFFFF, 0x00FFFFFF, 0);
1411 Ret
= IntEngBitBlt(&psurfDest
->SurfObj
,
1412 &psurfOffScreen
->SurfObj
,
1423 EXLATEOBJ_vCleanup(&exlo
);
1429 DC_vFinishBlit(pdc
, NULL
);
1434 /* Delete off screen rendering surface */
1436 GDIOBJ_vDeleteObject(&psurfOffScreen
->BaseObject
);
1439 /* Unlock other surfaces */
1440 SURFACE_ShareUnlockSurface(psurfMask
);
1441 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1459 HBRUSH hbrFlickerFreeDraw
,
1461 BOOL bMetaHDC
, // When TRUE, GDI functions need to be handled in User32!
1464 PCURICON_OBJECT pIcon
;
1467 TRACE("Enter NtUserDrawIconEx\n");
1468 UserEnterExclusive();
1470 if (!(pIcon
= UserGetCurIconObject(hIcon
)))
1472 ERR("UserGetCurIconObject(0x%08x) failed!\n", hIcon
);
1477 Ret
= UserDrawIconEx(hdc
,
1487 UserDereferenceObject(pIcon
);
1498 NtUserGetCursorFrameInfo(
1504 PCURICON_OBJECT CurIcon
;
1508 NTSTATUS Status
= STATUS_SUCCESS
;
1510 TRACE("Enter NtUserGetCursorFrameInfo\n");
1511 UserEnterExclusive();
1513 if (!(CurIcon
= UserGetCurIconObject(hCursor
)))
1519 ret
= CurIcon
->head
.h
;
1521 if(CurIcon
->CURSORF_flags
& CURSORF_ACON
)
1523 PACON AniCurIcon
= (PACON
)CurIcon
;
1524 if(istep
>= AniCurIcon
->cicur
)
1526 UserDereferenceObject(CurIcon
);
1530 jiffies
= AniCurIcon
->ajifRate
[istep
];
1531 steps
= AniCurIcon
->cicur
;
1532 ret
= AniCurIcon
->aspcur
[AniCurIcon
->aicur
[istep
]]->head
.h
;
1537 ProbeForWrite(rate_jiffies
, sizeof(INT
), 1);
1538 ProbeForWrite(num_steps
, sizeof(DWORD
), 1);
1539 *rate_jiffies
= jiffies
;
1542 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1544 Status
= _SEH2_GetExceptionCode();
1548 if (!NT_SUCCESS(Status
))
1550 WARN("Status: 0x%08x.\n", Status
);
1551 SetLastNtError(Status
);
1555 UserDereferenceObject(CurIcon
);
1558 TRACE("Leaving NtUserGetCursorFrameInfo, ret = 0x%08x\n", ret
);