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
);
253 PCURICON_OBJECT pcurOld
, pcurNew
;
254 HCURSOR hOldCursor
= NULL
;
258 pcurNew
= UserGetCurIconObject(hCursor
);
261 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
264 pcurNew
->CURSORF_flags
|= CURSORF_CURRENT
;
271 pcurOld
= UserSetCursor(pcurNew
, FALSE
);
274 hOldCursor
= pcurOld
->head
.h
;
275 pcurOld
->CURSORF_flags
&= ~CURSORF_CURRENT
;
276 if(UserObjectInDestroy(hOldCursor
))
278 /* Destroy it once and for all */
279 IntDestroyCurIconObject(pcurOld
, TRUE
);
284 UserDereferenceObject(pcurOld
);
296 PCURICON_OBJECT CurIcon
;
299 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
304 ret
= IntDestroyCurIconObject(CurIcon
, bForce
);
305 /* Note: IntDestroyCurIconObject will remove our reference for us! */
316 _In_ HANDLE hCurIcon
,
317 _Out_opt_ PICONINFO IconInfo
,
318 _Out_opt_ PUNICODE_STRING lpModule
, // Optional
319 _Out_opt_ PUNICODE_STRING lpResName
, // Optional
320 _Out_opt_ LPDWORD pbpp
, // Optional
324 PCURICON_OBJECT CurIcon
;
325 NTSTATUS Status
= STATUS_SUCCESS
;
329 TRACE("Enter NtUserGetIconInfo\n");
331 /* Check if something was actually asked */
332 if (!IconInfo
&& !lpModule
&& !lpResName
)
334 WARN("Nothing to fill.\n");
335 EngSetLastError(ERROR_INVALID_PARAMETER
);
339 UserEnterExclusive();
341 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
343 WARN("UserGetIconObject(0x%08x) Failed.\n", hCurIcon
);
348 /* Give back the icon information */
351 PCURICON_OBJECT FrameCurIcon
= CurIcon
;
352 if(CurIcon
->CURSORF_flags
& CURSORF_ACON
)
354 /* Get information from first frame. */
355 FrameCurIcon
= ((PACON
)CurIcon
)->aspcur
[0];
359 ii
.fIcon
= is_icon(FrameCurIcon
);
360 ii
.xHotspot
= FrameCurIcon
->xHotspot
;
361 ii
.yHotspot
= FrameCurIcon
->yHotspot
;
364 ii
.hbmMask
= BITMAP_CopyBitmap(FrameCurIcon
->hbmMask
);
365 GreSetObjectOwner(ii
.hbmMask
, GDI_OBJ_HMGR_POWNED
);
366 ii
.hbmColor
= BITMAP_CopyBitmap(FrameCurIcon
->hbmColor
);
367 GreSetObjectOwner(ii
.hbmColor
, GDI_OBJ_HMGR_POWNED
);
368 colorBpp
= FrameCurIcon
->bpp
;
373 ProbeForWrite(IconInfo
, sizeof(ICONINFO
), 1);
374 RtlCopyMemory(IconInfo
, &ii
, sizeof(ICONINFO
));
378 ProbeForWrite(pbpp
, sizeof(DWORD
), 1);
382 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
384 Status
= _SEH2_GetExceptionCode();
388 if (!NT_SUCCESS(Status
))
390 WARN("Status: 0x%08x.\n", Status
);
391 SetLastNtError(Status
);
396 /* Give back the module name */
400 if (!CurIcon
->atomModName
)
403 RtlQueryAtomInAtomTable(gAtomTable
, CurIcon
->atomModName
, NULL
, NULL
, NULL
, &BufLen
);
404 /* Get the module name from the atom table */
407 if (BufLen
> (lpModule
->MaximumLength
* sizeof(WCHAR
)))
409 lpModule
->Length
= 0;
413 ProbeForWrite(lpModule
->Buffer
, lpModule
->MaximumLength
, 1);
414 BufLen
= lpModule
->MaximumLength
* sizeof(WCHAR
);
415 RtlQueryAtomInAtomTable(gAtomTable
, CurIcon
->atomModName
, NULL
, NULL
, lpModule
->Buffer
, &BufLen
);
416 lpModule
->Length
= BufLen
/sizeof(WCHAR
);
419 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
421 Status
= _SEH2_GetExceptionCode();
425 if (!NT_SUCCESS(Status
))
427 SetLastNtError(Status
);
434 if (!CurIcon
->strName
.Buffer
)
440 ProbeForWrite(lpResName
, sizeof(UNICODE_STRING
), 1);
441 if (IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
443 lpResName
->Buffer
= CurIcon
->strName
.Buffer
;
444 lpResName
->Length
= 0;
446 else if (lpResName
->MaximumLength
< CurIcon
->strName
.Length
)
448 lpResName
->Length
= 0;
452 ProbeForWrite(lpResName
->Buffer
, lpResName
->MaximumLength
* sizeof(WCHAR
), 1);
453 RtlCopyMemory(lpResName
->Buffer
, CurIcon
->strName
.Buffer
, lpResName
->Length
);
456 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
458 Status
= _SEH2_GetExceptionCode();
463 if (!NT_SUCCESS(Status
))
465 SetLastNtError(Status
);
472 UserDereferenceObject(CurIcon
);
474 TRACE("Leave NtUserGetIconInfo, ret=%i\n", Ret
);
489 PLONG plcx
, // &size.cx
490 PLONG plcy
) // &size.cy
492 PCURICON_OBJECT CurIcon
;
493 NTSTATUS Status
= STATUS_SUCCESS
;
496 TRACE("Enter NtUserGetIconSize\n");
497 UserEnterExclusive();
499 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
504 if(CurIcon
->CURSORF_flags
& CURSORF_ACON
)
506 /* Use first frame for animated cursors */
507 PACON AniCurIcon
= (PACON
)CurIcon
;
508 CurIcon
= AniCurIcon
->aspcur
[0];
509 UserDereferenceObject(AniCurIcon
);
510 UserReferenceObject(CurIcon
);
515 ProbeForWrite(plcx
, sizeof(LONG
), 1);
517 ProbeForWrite(plcy
, sizeof(LONG
), 1);
520 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
522 Status
= _SEH2_GetExceptionCode();
526 if (NT_SUCCESS(Status
))
529 SetLastNtError(Status
); // Maybe not, test this
531 UserDereferenceObject(CurIcon
);
534 TRACE("Leave NtUserGetIconSize, ret=%i\n", bRet
);
549 PSYSTEM_CURSORINFO CurInfo
;
550 NTSTATUS Status
= STATUS_SUCCESS
;
551 PCURICON_OBJECT CurIcon
;
553 DECLARE_RETURN(BOOL
);
555 TRACE("Enter NtUserGetCursorInfo\n");
556 UserEnterExclusive();
558 CurInfo
= IntGetSysCursorInfo();
559 CurIcon
= (PCURICON_OBJECT
)CurInfo
->CurrentCursorObject
;
561 SafeCi
.cbSize
= sizeof(CURSORINFO
);
562 SafeCi
.flags
= ((CurIcon
&& CurInfo
->ShowingCursor
>= 0) ? CURSOR_SHOWING
: 0);
563 SafeCi
.hCursor
= (CurIcon
? CurIcon
->head
.h
: NULL
);
565 SafeCi
.ptScreenPos
= gpsi
->ptCursor
;
569 if (pci
->cbSize
== sizeof(CURSORINFO
))
571 ProbeForWrite(pci
, sizeof(CURSORINFO
), 1);
572 RtlCopyMemory(pci
, &SafeCi
, sizeof(CURSORINFO
));
577 EngSetLastError(ERROR_INVALID_PARAMETER
);
580 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
582 Status
= _SEH2_GetExceptionCode();
585 if (!NT_SUCCESS(Status
))
587 SetLastNtError(Status
);
593 TRACE("Leave NtUserGetCursorInfo, ret=%i\n",_ret_
);
603 /* FIXME: Check if process has WINSTA_WRITEATTRIBUTES */
604 PSYSTEM_CURSORINFO CurInfo
;
605 PWND DesktopWindow
= NULL
;
607 CurInfo
= IntGetSysCursorInfo();
609 DesktopWindow
= UserGetDesktopWindow();
611 if (prcl
!= NULL
&& DesktopWindow
!= NULL
)
613 if (prcl
->right
< prcl
->left
|| prcl
->bottom
< prcl
->top
)
615 EngSetLastError(ERROR_INVALID_PARAMETER
);
619 CurInfo
->bClipped
= TRUE
;
621 /* Set nw cliping region. Note: we can't use RECTL_bIntersectRect because
622 it sets rect to 0 0 0 0 when it's empty. For more info see monitor winetest */
623 CurInfo
->rcClip
.left
= max(prcl
->left
, DesktopWindow
->rcWindow
.left
);
624 CurInfo
->rcClip
.right
= min(prcl
->right
, DesktopWindow
->rcWindow
.right
);
625 if (CurInfo
->rcClip
.right
< CurInfo
->rcClip
.left
)
626 CurInfo
->rcClip
.right
= CurInfo
->rcClip
.left
;
628 CurInfo
->rcClip
.top
= max(prcl
->top
, DesktopWindow
->rcWindow
.top
);
629 CurInfo
->rcClip
.bottom
= min(prcl
->bottom
, DesktopWindow
->rcWindow
.bottom
);
630 if (CurInfo
->rcClip
.bottom
< CurInfo
->rcClip
.top
)
631 CurInfo
->rcClip
.bottom
= CurInfo
->rcClip
.top
;
633 /* Make sure cursor is in clipping region */
634 UserSetCursorPos(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
, 0, 0, FALSE
);
638 CurInfo
->bClipped
= FALSE
;
659 /* Probe and copy rect */
660 ProbeForRead(prcl
, sizeof(RECTL
), 1);
663 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
665 EngSetLastError(ERROR_INVALID_PARAMETER
);
666 _SEH2_YIELD(return FALSE
;)
673 UserEnterExclusive();
675 /* Call the internal function */
676 bResult
= UserClipCursor(prcl
);
690 _In_ HANDLE hCurIcon
,
693 PCURICON_OBJECT CurIcon
;
695 DECLARE_RETURN(BOOL
);
697 TRACE("Enter NtUserDestroyCursorIcon\n");
698 UserEnterExclusive();
700 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
705 ret
= IntDestroyCurIconObject(CurIcon
, bForce
);
706 /* Note: IntDestroyCurIconObject will remove our reference for us! */
711 TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_
);
722 NtUserFindExistingCursorIcon(
723 _In_ PUNICODE_STRING pustrModule
,
724 _In_ PUNICODE_STRING pustrRsrc
,
725 _In_ FINDEXISTINGCURICONPARAM
* param
)
727 PCURICON_OBJECT CurIcon
;
729 UNICODE_STRING ustrModuleSafe
, ustrRsrcSafe
;
730 FINDEXISTINGCURICONPARAM paramSafe
;
732 PPROCESSINFO pProcInfo
= PsGetCurrentProcessWin32Process();
733 RTL_ATOM atomModName
;
735 TRACE("Enter NtUserFindExistingCursorIcon\n");
740 ProbeForRead(param
, sizeof(*param
), 1);
741 RtlCopyMemory(¶mSafe
, param
, sizeof(paramSafe
));
743 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
745 Status
= _SEH2_GetExceptionCode();
749 /* Capture resource name (it can be an INTRESOURCE == ATOM) */
750 Status
= ProbeAndCaptureUnicodeStringOrAtom(&ustrRsrcSafe
, pustrRsrc
);
751 if(!NT_SUCCESS(Status
))
753 Status
= ProbeAndCaptureUnicodeString(&ustrModuleSafe
, UserMode
, pustrModule
);
754 if(!NT_SUCCESS(Status
))
756 Status
= RtlLookupAtomInAtomTable(gAtomTable
, ustrModuleSafe
.Buffer
, &atomModName
);
757 ReleaseCapturedUnicodeString(&ustrModuleSafe
, UserMode
);
758 if(!NT_SUCCESS(Status
))
760 /* The module is not in the atom table. No chance to find the cursor */
764 UserEnterExclusive();
765 CurIcon
= pProcInfo
->pCursorCache
;
769 if (paramSafe
.bIcon
!= is_icon(CurIcon
))
771 CurIcon
= CurIcon
->pcurNext
;
774 /* See if module names match */
775 if (atomModName
== CurIcon
->atomModName
)
777 /* They do. Now see if this is the same resource */
778 if (IS_INTRESOURCE(CurIcon
->strName
.Buffer
) != IS_INTRESOURCE(ustrRsrcSafe
.Buffer
))
780 /* One is an INT resource and the other is not -> no match */
781 CurIcon
= CurIcon
->pcurNext
;
785 if (IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
787 if (CurIcon
->strName
.Buffer
== ustrRsrcSafe
.Buffer
)
789 /* INT resources match */
793 else if (RtlCompareUnicodeString(&ustrRsrcSafe
, &CurIcon
->strName
, TRUE
) == 0)
795 /* Resource name strings match */
799 CurIcon
= CurIcon
->pcurNext
;
802 Ret
= CurIcon
->head
.h
;
806 if(!IS_INTRESOURCE(ustrRsrcSafe
.Buffer
))
807 ExFreePoolWithTag(ustrRsrcSafe
.Buffer
, TAG_STRING
);
821 /* FIXME: Check if process has WINSTA_READATTRIBUTES */
822 PSYSTEM_CURSORINFO CurInfo
;
825 DECLARE_RETURN(BOOL
);
827 TRACE("Enter NtUserGetClipCursor\n");
828 UserEnterExclusive();
833 CurInfo
= IntGetSysCursorInfo();
834 if (CurInfo
->bClipped
)
836 Rect
= CurInfo
->rcClip
;
842 Rect
.right
= UserGetSystemMetrics(SM_CXSCREEN
);
843 Rect
.bottom
= UserGetSystemMetrics(SM_CYSCREEN
);
846 Status
= MmCopyToCaller(lpRect
, &Rect
, sizeof(RECT
));
847 if (!NT_SUCCESS(Status
))
849 SetLastNtError(Status
);
856 TRACE("Leave NtUserGetClipCursor, ret=%i\n",_ret_
);
870 PCURICON_OBJECT pcurOld
, pcurNew
;
871 HCURSOR hOldCursor
= NULL
;
873 TRACE("Enter NtUserSetCursor\n");
874 UserEnterExclusive();
878 pcurNew
= UserGetCurIconObject(hCursor
);
881 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
884 pcurNew
->CURSORF_flags
|= CURSORF_CURRENT
;
891 pcurOld
= UserSetCursor(pcurNew
, FALSE
);
894 hOldCursor
= pcurOld
->head
.h
;
895 pcurOld
->CURSORF_flags
&= ~CURSORF_CURRENT
;
896 if(UserObjectInDestroy(hOldCursor
))
898 /* Destroy it once and for all */
899 IntDestroyCurIconObject(pcurOld
, TRUE
);
904 UserDereferenceObject(pcurOld
);
919 NtUserSetCursorContents(
921 PICONINFO UnsafeIconInfo
)
923 FIXME(" is UNIMPLEMENTED.\n");
933 NtUserSetCursorIconData(
935 _In_opt_ PUNICODE_STRING pustrModule
,
936 _In_opt_ PUNICODE_STRING pustrRsrc
,
937 _In_
const CURSORDATA
* pCursorData
)
939 PCURICON_OBJECT CurIcon
;
940 NTSTATUS Status
= STATUS_SUCCESS
;
942 BOOLEAN IsShared
= FALSE
, IsAnim
= FALSE
;
946 TRACE("Enter NtUserSetCursorIconData\n");
948 UserEnterExclusive();
950 if (!(CurIcon
= UserGetCurIconObject(Handle
)))
953 EngSetLastError(ERROR_INVALID_HANDLE
);
959 ProbeForRead(pCursorData
, sizeof(*pCursorData
), 1);
960 if(pCursorData
->CURSORF_flags
& CURSORF_ACON
)
962 /* This is an animated cursor */
963 PACON AniCurIcon
= (PACON
)CurIcon
;
966 numFrames
= AniCurIcon
->cpcur
= pCursorData
->cpcur
;
967 numSteps
= AniCurIcon
->cicur
= pCursorData
->cicur
;
968 AniCurIcon
->iicur
= pCursorData
->iicur
;
969 AniCurIcon
->rt
= pCursorData
->rt
;
971 /* Calculate size: one cursor object for each frame, and a frame index and jiffies for each "step" */
972 AniCurIcon
->aspcur
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
, /* Let SEH catch allocation failures */
973 numFrames
* sizeof(CURICON_OBJECT
*) + numSteps
* (sizeof(DWORD
) + sizeof(INT
)),
975 AniCurIcon
->aicur
= (DWORD
*)(AniCurIcon
->aspcur
+ numFrames
);
976 AniCurIcon
->ajifRate
= (INT
*)(AniCurIcon
->aicur
+ numSteps
);
978 RtlZeroMemory(AniCurIcon
->aspcur
, numFrames
* sizeof(CURICON_OBJECT
*));
980 ProbeForRead(pCursorData
->aicur
, numSteps
* sizeof(DWORD
), 1);
981 RtlCopyMemory(AniCurIcon
->aicur
, pCursorData
->aicur
, numSteps
* sizeof(DWORD
));
982 ProbeForRead(pCursorData
->ajifRate
, numSteps
* sizeof(INT
), 1);
983 RtlCopyMemory(AniCurIcon
->ajifRate
, pCursorData
->ajifRate
, numSteps
* sizeof(INT
));
985 AniCurIcon
->CURSORF_flags
= pCursorData
->CURSORF_flags
;
986 pCursorData
= pCursorData
->aspcur
;
992 CurIcon
->xHotspot
= pCursorData
->xHotspot
;
993 CurIcon
->yHotspot
= pCursorData
->yHotspot
;
994 CurIcon
->cx
= pCursorData
->cx
;
995 CurIcon
->cy
= pCursorData
->cy
;
996 CurIcon
->rt
= pCursorData
->rt
;
997 CurIcon
->bpp
= pCursorData
->bpp
;
998 CurIcon
->hbmMask
= pCursorData
->hbmMask
;
999 CurIcon
->hbmColor
= pCursorData
->hbmColor
;
1000 CurIcon
->hbmAlpha
= pCursorData
->hbmAlpha
;
1001 CurIcon
->CURSORF_flags
= pCursorData
->CURSORF_flags
;
1004 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1006 Status
= _SEH2_GetExceptionCode();
1010 if (!NT_SUCCESS(Status
))
1012 SetLastNtError(Status
);
1018 PACON AniCurIcon
= (PACON
)CurIcon
;
1019 /* This is an animated cursor. Create a cursor object for each frame and set up the data */
1020 for(i
= 0; i
< numFrames
; i
++)
1022 HANDLE hCurFrame
= IntCreateCurIconHandle(FALSE
);
1023 if(!NtUserSetCursorIconData(hCurFrame
, NULL
, NULL
, pCursorData
))
1025 AniCurIcon
->aspcur
[i
] = UserGetCurIconObject(hCurFrame
);
1026 if(!AniCurIcon
->aspcur
[i
])
1032 if(CurIcon
->CURSORF_flags
& CURSORF_LRSHARED
)
1035 if(pustrRsrc
&& pustrModule
)
1037 UNICODE_STRING ustrModuleSafe
;
1038 /* We use this convenient function, because INTRESOURCEs and ATOMs are the same */
1039 Status
= ProbeAndCaptureUnicodeStringOrAtom(&CurIcon
->strName
, pustrRsrc
);
1040 if(!NT_SUCCESS(Status
))
1042 Status
= ProbeAndCaptureUnicodeString(&ustrModuleSafe
, UserMode
, pustrModule
);
1043 if(!NT_SUCCESS(Status
))
1045 Status
= RtlAddAtomToAtomTable(gAtomTable
, ustrModuleSafe
.Buffer
, &CurIcon
->atomModName
);
1046 ReleaseCapturedUnicodeString(&ustrModuleSafe
, UserMode
);
1047 if(!NT_SUCCESS(Status
))
1052 if(!CurIcon
->hbmMask
)
1054 ERR("NtUserSetCursorIconData was got no hbmMask.\n");
1055 EngSetLastError(ERROR_INVALID_PARAMETER
);
1059 GreSetObjectOwner(CurIcon
->hbmMask
, GDI_OBJ_HMGR_PUBLIC
);
1061 if(CurIcon
->hbmColor
)
1062 GreSetObjectOwner(CurIcon
->hbmColor
, GDI_OBJ_HMGR_PUBLIC
);
1064 if(CurIcon
->hbmAlpha
)
1065 GreSetObjectOwner(CurIcon
->hbmAlpha
, GDI_OBJ_HMGR_PUBLIC
);
1069 /* Update process cache in case of shared cursor */
1070 PPROCESSINFO ppi
= CurIcon
->head
.ppi
;
1071 UserReferenceObject(CurIcon
);
1072 CurIcon
->pcurNext
= ppi
->pCursorCache
;
1073 ppi
->pCursorCache
= CurIcon
;
1079 if(!Ret
&& IsShared
)
1081 if(!IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
1082 ExFreePoolWithTag(CurIcon
->strName
.Buffer
, TAG_STRING
);
1087 PACON AniCurIcon
= (PACON
)CurIcon
;
1088 for(i
= 0; i
< numFrames
; i
++)
1090 if(AniCurIcon
->aspcur
[i
])
1091 IntDestroyCurIconObject(AniCurIcon
->aspcur
[i
], TRUE
);
1093 AniCurIcon
->cicur
= 0;
1094 AniCurIcon
->cpcur
= 0;
1095 ExFreePoolWithTag(AniCurIcon
->aspcur
, USERTAG_CURSOR
);
1096 AniCurIcon
->aspcur
= NULL
;
1097 AniCurIcon
->aicur
= NULL
;
1098 AniCurIcon
->ajifRate
= NULL
;
1101 UserDereferenceObject(CurIcon
);
1102 TRACE("Leave NtUserSetCursorIconData, ret=%i\n",Ret
);
1108 /* Mostly inspired from wine code.
1109 * We use low level functions because:
1110 * - at this point, the icon bitmap could have a different bit depth than the DC,
1111 * making it thus impossible to use NtCreateCompatibleDC and selecting the bitmap.
1112 * This happens after a mode setting change.
1113 * - it avoids massive GDI objects locking when only the destination surface needs it.
1114 * - It makes (small) performance gains.
1121 PCURICON_OBJECT pIcon
,
1125 HBRUSH hbrFlickerFreeDraw
,
1128 PSURFACE psurfDest
, psurfMask
, psurfColor
; //, psurfOffScreen = NULL;
1131 HBITMAP hbmMask
, hbmColor
, hbmAlpha
;
1133 RECTL rcDest
, rcSrc
;
1134 CLIPOBJ
* pdcClipObj
= NULL
;
1138 if((diFlags
& DI_NORMAL
) == 0)
1140 ERR("DrawIconEx called without mask or color bitmap to draw.\n");
1144 if (pIcon
->CURSORF_flags
& CURSORF_ACON
)
1146 ACON
* pAcon
= (ACON
*)pIcon
;
1147 if(istepIfAniCur
>= pAcon
->cicur
)
1149 ERR("NtUserDrawIconEx: istepIfAniCur too big!\n");
1152 pIcon
= pAcon
->aspcur
[pAcon
->aicur
[istepIfAniCur
]];
1155 hbmMask
= pIcon
->hbmMask
;
1156 hbmColor
= pIcon
->hbmColor
;
1157 hbmAlpha
= pIcon
->hbmAlpha
;
1161 * Shared locks are enough, we are only reading those bitmaps
1163 psurfMask
= SURFACE_ShareLockSurface(hbmMask
);
1164 if(psurfMask
== NULL
)
1166 ERR("Unable to lock the mask surface.\n");
1170 /* Color bitmap is not mandatory */
1171 if(hbmColor
== NULL
)
1173 /* But then the mask bitmap must have the information in it's bottom half */
1174 ASSERT(psurfMask
->SurfObj
.sizlBitmap
.cy
== 2*pIcon
->cy
);
1177 else if ((psurfColor
= SURFACE_ShareLockSurface(hbmColor
)) == NULL
)
1179 ERR("Unable to lock the color bitmap.\n");
1180 SURFACE_ShareUnlockSurface(psurfMask
);
1184 pdc
= DC_LockDc(hDc
);
1187 ERR("Could not lock the destination DC.\n");
1188 SURFACE_ShareUnlockSurface(psurfMask
);
1189 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1192 /* Calculate destination rectangle */
1193 RECTL_vSetRect(&rcDest
, xLeft
, yTop
, xLeft
+ cxWidth
, yTop
+ cyHeight
);
1194 IntLPtoDP(pdc
, (LPPOINT
)&rcDest
, 2);
1195 RECTL_vOffsetRect(&rcDest
, pdc
->ptlDCOrig
.x
, pdc
->ptlDCOrig
.y
);
1197 /* Prepare the underlying surface */
1198 DC_vPrepareDCsForBlit(pdc
, rcDest
, NULL
, rcDest
);
1200 /* We now have our destination surface and rectangle */
1201 psurfDest
= pdc
->dclevel
.pSurface
;
1203 if(psurfDest
== NULL
)
1206 DC_vFinishBlit(pdc
, NULL
);
1208 SURFACE_ShareUnlockSurface(psurfMask
);
1209 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1213 /* Set source rect */
1214 RECTL_vSetRect(&rcSrc
, 0, 0, pIcon
->cx
, pIcon
->cy
);
1216 /* Fix width parameter, if needed */
1219 if(diFlags
& DI_DEFAULTSIZE
)
1220 cxWidth
= is_icon(pIcon
) ?
1221 UserGetSystemMetrics(SM_CXICON
) : UserGetSystemMetrics(SM_CXCURSOR
);
1223 cxWidth
= pIcon
->cx
;
1226 /* Fix height parameter, if needed */
1229 if(diFlags
& DI_DEFAULTSIZE
)
1230 cyHeight
= is_icon(pIcon
) ?
1231 UserGetSystemMetrics(SM_CYICON
) : UserGetSystemMetrics(SM_CYCURSOR
);
1233 cyHeight
= pIcon
->cy
;
1236 /* Should we render off-screen? */
1237 bOffScreen
= hbrFlickerFreeDraw
&&
1238 (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw
) == GDI_OBJECT_TYPE_BRUSH
);
1242 /* Yes: Allocate and paint the offscreen surface */
1244 PBRUSH pbrush
= BRUSH_ShareLockBrush(hbrFlickerFreeDraw
);
1246 TRACE("Performing off-screen rendering.\n");
1250 ERR("Failed to get brush object.\n");
1254 #if 0 //We lock the hdc surface during the whole function it makes no sense to use an offscreen surface for "flicker free" drawing
1255 psurfOffScreen
= SURFACE_AllocSurface(STYPE_BITMAP
,
1256 cxWidth
, cyHeight
, psurfDest
->SurfObj
.iBitmapFormat
,
1260 ERR("Failed to allocate the off-screen surface.\n");
1261 BRUSH_ShareUnlockBrush(pbrush
);
1265 /* Paint the brush */
1266 EBRUSHOBJ_vInit(&eboFill
, pbrush
, psurfOffScreen
, 0x00FFFFFF, 0, NULL
);
1267 RECTL_vSetRect(&rcDest
, 0, 0, cxWidth
, cyHeight
);
1269 Ret
= IntEngBitBlt(&psurfOffScreen
->SurfObj
,
1277 &eboFill
.BrushObject
,
1281 /* Clean up everything */
1282 EBRUSHOBJ_vCleanup(&eboFill
);
1283 BRUSH_ShareUnlockBrush(pbrush
);
1287 ERR("Failed to paint the off-screen surface.\n");
1291 /* We now have our destination surface */
1292 psurfDest
= psurfOffScreen
;
1294 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1295 /* Paint the brush */
1296 EBRUSHOBJ_vInit(&eboFill
, pbrush
, psurfDest
, 0x00FFFFFF, 0, NULL
);
1298 Ret
= IntEngBitBlt(&psurfDest
->SurfObj
,
1306 &eboFill
.BrushObject
,
1310 /* Clean up everything */
1311 EBRUSHOBJ_vCleanup(&eboFill
);
1312 BRUSH_ShareUnlockBrush(pbrush
);
1316 ERR("Failed to paint the off-screen surface.\n");
1323 /* We directly draw to the DC */
1324 TRACE("Performing on screen rendering.\n");
1325 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1326 // psurfOffScreen = NULL;
1329 /* Now do the rendering */
1330 if(hbmAlpha
&& ((diFlags
& DI_NORMAL
) == DI_NORMAL
))
1332 BLENDOBJ blendobj
= { {AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
} };
1333 PSURFACE psurf
= NULL
;
1335 psurf
= SURFACE_ShareLockSurface(hbmAlpha
);
1338 ERR("SURFACE_LockSurface failed!\n");
1342 /* Initialize color translation object */
1343 EXLATEOBJ_vInitialize(&exlo
, psurf
->ppal
, psurfDest
->ppal
, 0xFFFFFFFF, 0xFFFFFFFF, 0);
1346 Ret
= IntEngAlphaBlend(&psurfDest
->SurfObj
,
1354 EXLATEOBJ_vCleanup(&exlo
);
1355 SURFACE_ShareUnlockSurface(psurf
);
1357 ERR("NtGdiAlphaBlend failed!\n");
1360 if (diFlags
& DI_MASK
)
1362 DWORD rop4
= (diFlags
& DI_IMAGE
) ? ROP4_SRCAND
: ROP4_SRCCOPY
;
1364 EXLATEOBJ_vInitSrcMonoXlate(&exlo
, psurfDest
->ppal
, 0x00FFFFFF, 0);
1366 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1367 &psurfMask
->SurfObj
,
1379 EXLATEOBJ_vCleanup(&exlo
);
1383 ERR("Failed to mask the bitmap data.\n");
1388 if(diFlags
& DI_IMAGE
)
1392 DWORD rop4
= (diFlags
& DI_MASK
) ? ROP4_SRCINVERT
: ROP4_SRCCOPY
;
1394 EXLATEOBJ_vInitialize(&exlo
, psurfColor
->ppal
, psurfDest
->ppal
, 0x00FFFFFF, 0x00FFFFFF, 0);
1396 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1397 &psurfColor
->SurfObj
,
1409 EXLATEOBJ_vCleanup(&exlo
);
1413 ERR("Failed to render the icon bitmap.\n");
1419 /* Mask bitmap holds the information in its bottom half */
1420 DWORD rop4
= (diFlags
& DI_MASK
) ? ROP4_SRCINVERT
: ROP4_SRCCOPY
;
1421 RECTL_vOffsetRect(&rcSrc
, 0, pIcon
->cy
);
1423 EXLATEOBJ_vInitSrcMonoXlate(&exlo
, psurfDest
->ppal
, 0x00FFFFFF, 0);
1425 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1426 &psurfMask
->SurfObj
,
1438 EXLATEOBJ_vCleanup(&exlo
);
1442 ERR("Failed to render the icon bitmap.\n");
1450 /* We're done. Was it a double buffered draw ? */
1453 /* Yes. Draw it back to our DC */
1454 POINTL ptSrc
= {0, 0};
1456 /* Calculate destination rectangle */
1457 RECTL_vSetRect(&rcDest
, xLeft
, yTop
, xLeft
+ cxWidth
, yTop
+ cyHeight
);
1458 IntLPtoDP(pdc
, (LPPOINT
)&rcDest
, 2);
1459 RECTL_vOffsetRect(&rcDest
, pdc
->ptlDCOrig
.x
, pdc
->ptlDCOrig
.y
);
1461 /* Get the clip object */
1462 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1464 /* We now have our destination surface and rectangle */
1465 psurfDest
= pdc
->dclevel
.pSurface
;
1467 /* Color translation */
1468 EXLATEOBJ_vInitialize(&exlo
, psurfOffScreen
->ppal
, psurfDest
->ppal
, 0x00FFFFFF, 0x00FFFFFF, 0);
1471 Ret
= IntEngBitBlt(&psurfDest
->SurfObj
,
1472 &psurfOffScreen
->SurfObj
,
1483 EXLATEOBJ_vCleanup(&exlo
);
1489 DC_vFinishBlit(pdc
, NULL
);
1494 /* Delete off screen rendering surface */
1496 GDIOBJ_vDeleteObject(&psurfOffScreen
->BaseObject
);
1499 /* Unlock other surfaces */
1500 SURFACE_ShareUnlockSurface(psurfMask
);
1501 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1519 HBRUSH hbrFlickerFreeDraw
,
1521 BOOL bMetaHDC
, // When TRUE, GDI functions need to be handled in User32!
1524 PCURICON_OBJECT pIcon
;
1527 TRACE("Enter NtUserDrawIconEx\n");
1528 UserEnterExclusive();
1530 if (!(pIcon
= UserGetCurIconObject(hIcon
)))
1532 ERR("UserGetCurIconObject(0x%08x) failed!\n", hIcon
);
1537 Ret
= UserDrawIconEx(hdc
,
1547 UserDereferenceObject(pIcon
);
1558 NtUserGetCursorFrameInfo(
1564 PCURICON_OBJECT CurIcon
;
1568 NTSTATUS Status
= STATUS_SUCCESS
;
1570 TRACE("Enter NtUserGetCursorFrameInfo\n");
1571 UserEnterExclusive();
1573 if (!(CurIcon
= UserGetCurIconObject(hCursor
)))
1579 ret
= CurIcon
->head
.h
;
1581 if(CurIcon
->CURSORF_flags
& CURSORF_ACON
)
1583 PACON AniCurIcon
= (PACON
)CurIcon
;
1584 if(istep
>= AniCurIcon
->cicur
)
1586 UserDereferenceObject(CurIcon
);
1590 jiffies
= AniCurIcon
->ajifRate
[istep
];
1591 steps
= AniCurIcon
->cicur
;
1592 ret
= AniCurIcon
->aspcur
[AniCurIcon
->aicur
[istep
]]->head
.h
;
1597 ProbeForWrite(rate_jiffies
, sizeof(INT
), 1);
1598 ProbeForWrite(num_steps
, sizeof(DWORD
), 1);
1599 *rate_jiffies
= jiffies
;
1602 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1604 Status
= _SEH2_GetExceptionCode();
1608 if (!NT_SUCCESS(Status
))
1610 WARN("Status: 0x%08x.\n", Status
);
1611 SetLastNtError(Status
);
1615 UserDereferenceObject(CurIcon
);
1618 TRACE("Leaving NtUserGetCursorFrameInfo, ret = 0x%08x\n", ret
);