2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Cursor and icon functions
5 * FILE: win32ss/user/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(PVOID Object
)
155 PCURICON_OBJECT CurIcon
= Object
;
157 if(!(CurIcon
->CURSORF_flags
& CURSORF_ACON
))
159 HBITMAP bmpMask
= CurIcon
->hbmMask
;
160 HBITMAP bmpColor
= CurIcon
->hbmColor
;
161 HBITMAP bmpAlpha
= CurIcon
->hbmAlpha
;
166 GreSetObjectOwner(bmpMask
, GDI_OBJ_HMGR_POWNED
);
167 GreDeleteObject(bmpMask
);
168 CurIcon
->hbmMask
= NULL
;
172 GreSetObjectOwner(bmpColor
, GDI_OBJ_HMGR_POWNED
);
173 GreDeleteObject(bmpColor
);
174 CurIcon
->hbmColor
= NULL
;
178 GreSetObjectOwner(bmpAlpha
, GDI_OBJ_HMGR_POWNED
);
179 GreDeleteObject(bmpAlpha
);
180 CurIcon
->hbmAlpha
= NULL
;
185 PACON AniCurIcon
= (PACON
)CurIcon
;
188 for(i
= 0; i
< AniCurIcon
->cpcur
; i
++)
189 IntDestroyCurIconObject(AniCurIcon
->aspcur
[i
]);
190 ExFreePoolWithTag(AniCurIcon
->aspcur
, USERTAG_CURSOR
);
193 if (CurIcon
->CURSORF_flags
& CURSORF_LRSHARED
)
197 if (!IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
198 ExFreePoolWithTag(CurIcon
->strName
.Buffer
, TAG_STRING
);
199 if (CurIcon
->atomModName
)
200 RtlDeleteAtomFromAtomTable(gAtomTable
, CurIcon
->atomModName
);
201 CurIcon
->strName
.Buffer
= NULL
;
202 CurIcon
->atomModName
= 0;
204 /* Try finding it in its process cache */
205 ppi
= CurIcon
->head
.ppi
;
206 if (ppi
->pCursorCache
== CurIcon
)
207 ppi
->pCursorCache
= CurIcon
->pcurNext
;
210 PCURICON_OBJECT CacheCurIcon
= ppi
->pCursorCache
;
213 if (CacheCurIcon
->pcurNext
== CurIcon
)
215 CacheCurIcon
->pcurNext
= CurIcon
->pcurNext
;
218 CacheCurIcon
= CacheCurIcon
->pcurNext
;
227 IntCleanupCurIconCache(PPROCESSINFO Win32Process
)
229 PCURICON_OBJECT CurIcon
;
231 /* Run through the list of icon objects */
232 while (Win32Process
->pCursorCache
)
234 CurIcon
= Win32Process
->pCursorCache
;
235 Win32Process
->pCursorCache
= CurIcon
->pcurNext
;
236 UserDereferenceObject(CurIcon
);
246 _In_ HANDLE hCurIcon
,
247 _Out_opt_ PICONINFO IconInfo
,
248 _Out_opt_ PUNICODE_STRING lpModule
, // Optional
249 _Out_opt_ PUNICODE_STRING lpResName
, // Optional
250 _Out_opt_ LPDWORD pbpp
, // Optional
254 PCURICON_OBJECT CurIcon
;
255 NTSTATUS Status
= STATUS_SUCCESS
;
259 TRACE("Enter NtUserGetIconInfo\n");
261 /* Check if something was actually asked */
262 if (!IconInfo
&& !lpModule
&& !lpResName
)
264 WARN("Nothing to fill.\n");
265 EngSetLastError(ERROR_INVALID_PARAMETER
);
269 UserEnterExclusive();
271 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
273 WARN("UserGetIconObject(0x%08x) Failed.\n", hCurIcon
);
278 /* Give back the icon information */
281 PCURICON_OBJECT FrameCurIcon
= CurIcon
;
282 if(CurIcon
->CURSORF_flags
& CURSORF_ACON
)
284 /* Get information from first frame. */
285 FrameCurIcon
= ((PACON
)CurIcon
)->aspcur
[0];
289 ii
.fIcon
= is_icon(FrameCurIcon
);
290 ii
.xHotspot
= FrameCurIcon
->xHotspot
;
291 ii
.yHotspot
= FrameCurIcon
->yHotspot
;
294 ii
.hbmMask
= BITMAP_CopyBitmap(FrameCurIcon
->hbmMask
);
295 GreSetObjectOwner(ii
.hbmMask
, GDI_OBJ_HMGR_POWNED
);
296 ii
.hbmColor
= BITMAP_CopyBitmap(FrameCurIcon
->hbmColor
);
297 GreSetObjectOwner(ii
.hbmColor
, GDI_OBJ_HMGR_POWNED
);
298 colorBpp
= FrameCurIcon
->bpp
;
303 ProbeForWrite(IconInfo
, sizeof(ICONINFO
), 1);
304 RtlCopyMemory(IconInfo
, &ii
, sizeof(ICONINFO
));
308 ProbeForWrite(pbpp
, sizeof(DWORD
), 1);
312 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
314 Status
= _SEH2_GetExceptionCode();
318 if (!NT_SUCCESS(Status
))
320 WARN("Status: 0x%08x.\n", Status
);
321 SetLastNtError(Status
);
326 /* Give back the module name */
330 if (!CurIcon
->atomModName
)
333 RtlQueryAtomInAtomTable(gAtomTable
, CurIcon
->atomModName
, NULL
, NULL
, NULL
, &BufLen
);
334 /* Get the module name from the atom table */
337 if (BufLen
> (lpModule
->MaximumLength
* sizeof(WCHAR
)))
339 lpModule
->Length
= 0;
343 ProbeForWrite(lpModule
->Buffer
, lpModule
->MaximumLength
, 1);
344 BufLen
= lpModule
->MaximumLength
* sizeof(WCHAR
);
345 RtlQueryAtomInAtomTable(gAtomTable
, CurIcon
->atomModName
, NULL
, NULL
, lpModule
->Buffer
, &BufLen
);
346 lpModule
->Length
= BufLen
/sizeof(WCHAR
);
349 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
351 Status
= _SEH2_GetExceptionCode();
355 if (!NT_SUCCESS(Status
))
357 SetLastNtError(Status
);
364 if (!CurIcon
->strName
.Buffer
)
370 ProbeForWrite(lpResName
, sizeof(UNICODE_STRING
), 1);
371 if (IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
373 lpResName
->Buffer
= CurIcon
->strName
.Buffer
;
374 lpResName
->Length
= 0;
376 else if (lpResName
->MaximumLength
< CurIcon
->strName
.Length
)
378 lpResName
->Length
= 0;
382 ProbeForWrite(lpResName
->Buffer
, lpResName
->MaximumLength
* sizeof(WCHAR
), 1);
383 RtlCopyMemory(lpResName
->Buffer
, CurIcon
->strName
.Buffer
, lpResName
->Length
);
386 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
388 Status
= _SEH2_GetExceptionCode();
393 if (!NT_SUCCESS(Status
))
395 SetLastNtError(Status
);
402 UserDereferenceObject(CurIcon
);
404 TRACE("Leave NtUserGetIconInfo, ret=%i\n", Ret
);
419 PLONG plcx
, // &size.cx
420 PLONG plcy
) // &size.cy
422 PCURICON_OBJECT CurIcon
;
423 NTSTATUS Status
= STATUS_SUCCESS
;
426 TRACE("Enter NtUserGetIconSize\n");
427 UserEnterExclusive();
429 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
434 if(CurIcon
->CURSORF_flags
& CURSORF_ACON
)
436 /* Use first frame for animated cursors */
437 PACON AniCurIcon
= (PACON
)CurIcon
;
438 CurIcon
= AniCurIcon
->aspcur
[0];
439 UserDereferenceObject(AniCurIcon
);
440 UserReferenceObject(CurIcon
);
445 ProbeForWrite(plcx
, sizeof(LONG
), 1);
447 ProbeForWrite(plcy
, sizeof(LONG
), 1);
450 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
452 Status
= _SEH2_GetExceptionCode();
456 if (NT_SUCCESS(Status
))
459 SetLastNtError(Status
); // Maybe not, test this
461 UserDereferenceObject(CurIcon
);
464 TRACE("Leave NtUserGetIconSize, ret=%i\n", bRet
);
479 PSYSTEM_CURSORINFO CurInfo
;
480 NTSTATUS Status
= STATUS_SUCCESS
;
481 PCURICON_OBJECT CurIcon
;
483 DECLARE_RETURN(BOOL
);
485 TRACE("Enter NtUserGetCursorInfo\n");
486 UserEnterExclusive();
488 CurInfo
= IntGetSysCursorInfo();
489 CurIcon
= (PCURICON_OBJECT
)CurInfo
->CurrentCursorObject
;
491 SafeCi
.cbSize
= sizeof(CURSORINFO
);
492 SafeCi
.flags
= ((CurIcon
&& CurInfo
->ShowingCursor
>= 0) ? CURSOR_SHOWING
: 0);
493 SafeCi
.hCursor
= (CurIcon
? CurIcon
->head
.h
: NULL
);
495 SafeCi
.ptScreenPos
= gpsi
->ptCursor
;
499 if (pci
->cbSize
== sizeof(CURSORINFO
))
501 ProbeForWrite(pci
, sizeof(CURSORINFO
), 1);
502 RtlCopyMemory(pci
, &SafeCi
, sizeof(CURSORINFO
));
507 EngSetLastError(ERROR_INVALID_PARAMETER
);
510 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
512 Status
= _SEH2_GetExceptionCode();
515 if (!NT_SUCCESS(Status
))
517 SetLastNtError(Status
);
523 TRACE("Leave NtUserGetCursorInfo, ret=%i\n",_ret_
);
533 /* FIXME: Check if process has WINSTA_WRITEATTRIBUTES */
534 PSYSTEM_CURSORINFO CurInfo
;
535 PWND DesktopWindow
= NULL
;
537 CurInfo
= IntGetSysCursorInfo();
539 DesktopWindow
= UserGetDesktopWindow();
541 if (prcl
!= NULL
&& DesktopWindow
!= NULL
)
543 if (prcl
->right
< prcl
->left
|| prcl
->bottom
< prcl
->top
)
545 EngSetLastError(ERROR_INVALID_PARAMETER
);
549 CurInfo
->bClipped
= TRUE
;
551 /* Set nw cliping region. Note: we can't use RECTL_bIntersectRect because
552 it sets rect to 0 0 0 0 when it's empty. For more info see monitor winetest */
553 CurInfo
->rcClip
.left
= max(prcl
->left
, DesktopWindow
->rcWindow
.left
);
554 CurInfo
->rcClip
.right
= min(prcl
->right
, DesktopWindow
->rcWindow
.right
);
555 if (CurInfo
->rcClip
.right
< CurInfo
->rcClip
.left
)
556 CurInfo
->rcClip
.right
= CurInfo
->rcClip
.left
;
558 CurInfo
->rcClip
.top
= max(prcl
->top
, DesktopWindow
->rcWindow
.top
);
559 CurInfo
->rcClip
.bottom
= min(prcl
->bottom
, DesktopWindow
->rcWindow
.bottom
);
560 if (CurInfo
->rcClip
.bottom
< CurInfo
->rcClip
.top
)
561 CurInfo
->rcClip
.bottom
= CurInfo
->rcClip
.top
;
563 /* Make sure cursor is in clipping region */
564 UserSetCursorPos(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
, 0, 0, FALSE
);
568 CurInfo
->bClipped
= FALSE
;
589 /* Probe and copy rect */
590 ProbeForRead(prcl
, sizeof(RECTL
), 1);
593 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
595 EngSetLastError(ERROR_INVALID_PARAMETER
);
596 _SEH2_YIELD(return FALSE
;)
603 UserEnterExclusive();
605 /* Call the internal function */
606 bResult
= UserClipCursor(prcl
);
620 _In_ HANDLE hCurIcon
,
625 TRACE("Enter NtUserDestroyCursorIcon\n");
626 UserEnterExclusive();
630 /* Maybe we have good reasons not to destroy this object */
631 PCURICON_OBJECT CurIcon
= UserGetCurIconObject(hCurIcon
);
639 if (CurIcon
->head
.ppi
!= PsGetCurrentProcessWin32Process())
641 /* No way, you're not touching my cursor */
643 UserDereferenceObject(CurIcon
);
647 Flags
= CurIcon
->CURSORF_flags
;
648 UserDereferenceObject(CurIcon
);
650 if (Flags
& CURSORF_CURRENT
)
652 WARN("Trying to delete current cursor!\n");
657 if (Flags
& CURSORF_LRSHARED
)
659 WARN("Trying to delete shared cursor.\n");
660 /* This one is not an error */
666 /* Destroy the handle */
667 ret
= UserDeleteObject(hCurIcon
, TYPE_CURSOR
);
670 TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n", ret
);
681 NtUserFindExistingCursorIcon(
682 _In_ PUNICODE_STRING pustrModule
,
683 _In_ PUNICODE_STRING pustrRsrc
,
684 _In_ FINDEXISTINGCURICONPARAM
* param
)
686 PCURICON_OBJECT CurIcon
;
688 UNICODE_STRING ustrModuleSafe
, ustrRsrcSafe
;
689 FINDEXISTINGCURICONPARAM paramSafe
;
691 PPROCESSINFO pProcInfo
= PsGetCurrentProcessWin32Process();
692 RTL_ATOM atomModName
;
694 TRACE("Enter NtUserFindExistingCursorIcon\n");
699 ProbeForRead(param
, sizeof(*param
), 1);
700 RtlCopyMemory(¶mSafe
, param
, sizeof(paramSafe
));
702 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
704 Status
= _SEH2_GetExceptionCode();
708 /* Capture resource name (it can be an INTRESOURCE == ATOM) */
709 Status
= ProbeAndCaptureUnicodeStringOrAtom(&ustrRsrcSafe
, pustrRsrc
);
710 if(!NT_SUCCESS(Status
))
712 Status
= ProbeAndCaptureUnicodeString(&ustrModuleSafe
, UserMode
, pustrModule
);
713 if(!NT_SUCCESS(Status
))
715 Status
= RtlLookupAtomInAtomTable(gAtomTable
, ustrModuleSafe
.Buffer
, &atomModName
);
716 ReleaseCapturedUnicodeString(&ustrModuleSafe
, UserMode
);
717 if(!NT_SUCCESS(Status
))
719 /* The module is not in the atom table. No chance to find the cursor */
723 UserEnterExclusive();
724 CurIcon
= pProcInfo
->pCursorCache
;
728 if (paramSafe
.bIcon
!= is_icon(CurIcon
))
730 CurIcon
= CurIcon
->pcurNext
;
733 /* See if module names match */
734 if (atomModName
== CurIcon
->atomModName
)
736 /* They do. Now see if this is the same resource */
737 if (IS_INTRESOURCE(CurIcon
->strName
.Buffer
) != IS_INTRESOURCE(ustrRsrcSafe
.Buffer
))
739 /* One is an INT resource and the other is not -> no match */
740 CurIcon
= CurIcon
->pcurNext
;
744 if (IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
746 if (CurIcon
->strName
.Buffer
== ustrRsrcSafe
.Buffer
)
748 /* INT resources match */
752 else if (RtlCompareUnicodeString(&ustrRsrcSafe
, &CurIcon
->strName
, TRUE
) == 0)
754 /* Resource name strings match */
758 CurIcon
= CurIcon
->pcurNext
;
761 Ret
= CurIcon
->head
.h
;
765 if(!IS_INTRESOURCE(ustrRsrcSafe
.Buffer
))
766 ExFreePoolWithTag(ustrRsrcSafe
.Buffer
, TAG_STRING
);
780 /* FIXME: Check if process has WINSTA_READATTRIBUTES */
781 PSYSTEM_CURSORINFO CurInfo
;
784 DECLARE_RETURN(BOOL
);
786 TRACE("Enter NtUserGetClipCursor\n");
787 UserEnterExclusive();
792 CurInfo
= IntGetSysCursorInfo();
793 if (CurInfo
->bClipped
)
795 Rect
= CurInfo
->rcClip
;
801 Rect
.right
= UserGetSystemMetrics(SM_CXSCREEN
);
802 Rect
.bottom
= UserGetSystemMetrics(SM_CYSCREEN
);
805 Status
= MmCopyToCaller(lpRect
, &Rect
, sizeof(RECT
));
806 if (!NT_SUCCESS(Status
))
808 SetLastNtError(Status
);
815 TRACE("Leave NtUserGetClipCursor, ret=%i\n",_ret_
);
829 PCURICON_OBJECT pcurOld
, pcurNew
;
830 HCURSOR hOldCursor
= NULL
;
832 TRACE("Enter NtUserSetCursor\n");
833 UserEnterExclusive();
837 pcurNew
= UserGetCurIconObject(hCursor
);
840 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
843 pcurNew
->CURSORF_flags
|= CURSORF_CURRENT
;
850 pcurOld
= UserSetCursor(pcurNew
, FALSE
);
853 pcurOld
->CURSORF_flags
&= ~CURSORF_CURRENT
;
854 UserDereferenceObject(pcurOld
);
868 NtUserSetCursorContents(
870 PICONINFO UnsafeIconInfo
)
872 FIXME(" is UNIMPLEMENTED.\n");
882 NtUserSetCursorIconData(
884 _In_opt_ PUNICODE_STRING pustrModule
,
885 _In_opt_ PUNICODE_STRING pustrRsrc
,
886 _In_
const CURSORDATA
* pCursorData
)
888 PCURICON_OBJECT CurIcon
;
889 NTSTATUS Status
= STATUS_SUCCESS
;
891 BOOLEAN IsShared
= FALSE
, IsAnim
= FALSE
;
895 TRACE("Enter NtUserSetCursorIconData\n");
897 UserEnterExclusive();
899 if (!(CurIcon
= UserGetCurIconObject(Handle
)))
902 EngSetLastError(ERROR_INVALID_HANDLE
);
908 ProbeForRead(pCursorData
, sizeof(*pCursorData
), 1);
909 if(pCursorData
->CURSORF_flags
& CURSORF_ACON
)
911 /* This is an animated cursor */
912 PACON AniCurIcon
= (PACON
)CurIcon
;
915 numFrames
= AniCurIcon
->cpcur
= pCursorData
->cpcur
;
916 numSteps
= AniCurIcon
->cicur
= pCursorData
->cicur
;
917 AniCurIcon
->iicur
= pCursorData
->iicur
;
918 AniCurIcon
->rt
= pCursorData
->rt
;
920 /* Calculate size: one cursor object for each frame, and a frame index and jiffies for each "step" */
921 AniCurIcon
->aspcur
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
, /* Let SEH catch allocation failures */
922 numFrames
* sizeof(CURICON_OBJECT
*) + numSteps
* (sizeof(DWORD
) + sizeof(INT
)),
924 AniCurIcon
->aicur
= (DWORD
*)(AniCurIcon
->aspcur
+ numFrames
);
925 AniCurIcon
->ajifRate
= (INT
*)(AniCurIcon
->aicur
+ numSteps
);
927 RtlZeroMemory(AniCurIcon
->aspcur
, numFrames
* sizeof(CURICON_OBJECT
*));
929 ProbeForRead(pCursorData
->aicur
, numSteps
* sizeof(DWORD
), 1);
930 RtlCopyMemory(AniCurIcon
->aicur
, pCursorData
->aicur
, numSteps
* sizeof(DWORD
));
931 ProbeForRead(pCursorData
->ajifRate
, numSteps
* sizeof(INT
), 1);
932 RtlCopyMemory(AniCurIcon
->ajifRate
, pCursorData
->ajifRate
, numSteps
* sizeof(INT
));
934 AniCurIcon
->CURSORF_flags
= pCursorData
->CURSORF_flags
;
935 pCursorData
= pCursorData
->aspcur
;
941 CurIcon
->xHotspot
= pCursorData
->xHotspot
;
942 CurIcon
->yHotspot
= pCursorData
->yHotspot
;
943 CurIcon
->cx
= pCursorData
->cx
;
944 CurIcon
->cy
= pCursorData
->cy
;
945 CurIcon
->rt
= pCursorData
->rt
;
946 CurIcon
->bpp
= pCursorData
->bpp
;
947 CurIcon
->hbmMask
= pCursorData
->hbmMask
;
948 CurIcon
->hbmColor
= pCursorData
->hbmColor
;
949 CurIcon
->hbmAlpha
= pCursorData
->hbmAlpha
;
950 CurIcon
->CURSORF_flags
= pCursorData
->CURSORF_flags
;
953 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
955 Status
= _SEH2_GetExceptionCode();
959 if (!NT_SUCCESS(Status
))
961 SetLastNtError(Status
);
967 PACON AniCurIcon
= (PACON
)CurIcon
;
968 /* This is an animated cursor. Create a cursor object for each frame and set up the data */
969 for(i
= 0; i
< numFrames
; i
++)
971 HANDLE hCurFrame
= IntCreateCurIconHandle(FALSE
);
972 if(!NtUserSetCursorIconData(hCurFrame
, NULL
, NULL
, pCursorData
))
974 AniCurIcon
->aspcur
[i
] = UserGetCurIconObject(hCurFrame
);
975 if(!AniCurIcon
->aspcur
[i
])
981 if(CurIcon
->CURSORF_flags
& CURSORF_LRSHARED
)
984 if(pustrRsrc
&& pustrModule
)
986 UNICODE_STRING ustrModuleSafe
;
987 /* We use this convenient function, because INTRESOURCEs and ATOMs are the same */
988 Status
= ProbeAndCaptureUnicodeStringOrAtom(&CurIcon
->strName
, pustrRsrc
);
989 if(!NT_SUCCESS(Status
))
991 Status
= ProbeAndCaptureUnicodeString(&ustrModuleSafe
, UserMode
, pustrModule
);
992 if(!NT_SUCCESS(Status
))
994 Status
= RtlAddAtomToAtomTable(gAtomTable
, ustrModuleSafe
.Buffer
, &CurIcon
->atomModName
);
995 ReleaseCapturedUnicodeString(&ustrModuleSafe
, UserMode
);
996 if(!NT_SUCCESS(Status
))
1001 if(!CurIcon
->hbmMask
)
1003 ERR("NtUserSetCursorIconData was got no hbmMask.\n");
1004 EngSetLastError(ERROR_INVALID_PARAMETER
);
1008 GreSetObjectOwner(CurIcon
->hbmMask
, GDI_OBJ_HMGR_PUBLIC
);
1010 if(CurIcon
->hbmColor
)
1011 GreSetObjectOwner(CurIcon
->hbmColor
, GDI_OBJ_HMGR_PUBLIC
);
1013 if(CurIcon
->hbmAlpha
)
1014 GreSetObjectOwner(CurIcon
->hbmAlpha
, GDI_OBJ_HMGR_PUBLIC
);
1018 /* Update process cache in case of shared cursor */
1019 PPROCESSINFO ppi
= CurIcon
->head
.ppi
;
1020 UserReferenceObject(CurIcon
);
1021 CurIcon
->pcurNext
= ppi
->pCursorCache
;
1022 ppi
->pCursorCache
= CurIcon
;
1028 if(!Ret
&& IsShared
)
1030 if(!IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
1031 ExFreePoolWithTag(CurIcon
->strName
.Buffer
, TAG_STRING
);
1036 PACON AniCurIcon
= (PACON
)CurIcon
;
1037 for(i
= 0; i
< numFrames
; i
++)
1039 if(AniCurIcon
->aspcur
[i
])
1040 IntDestroyCurIconObject(AniCurIcon
->aspcur
[i
]);
1042 AniCurIcon
->cicur
= 0;
1043 AniCurIcon
->cpcur
= 0;
1044 ExFreePoolWithTag(AniCurIcon
->aspcur
, USERTAG_CURSOR
);
1045 AniCurIcon
->aspcur
= NULL
;
1046 AniCurIcon
->aicur
= NULL
;
1047 AniCurIcon
->ajifRate
= NULL
;
1050 UserDereferenceObject(CurIcon
);
1051 TRACE("Leave NtUserSetCursorIconData, ret=%i\n",Ret
);
1057 /* Mostly inspired from wine code.
1058 * We use low level functions because:
1059 * - at this point, the icon bitmap could have a different bit depth than the DC,
1060 * making it thus impossible to use NtCreateCompatibleDC and selecting the bitmap.
1061 * This happens after a mode setting change.
1062 * - it avoids massive GDI objects locking when only the destination surface needs it.
1063 * - It makes (small) performance gains.
1070 PCURICON_OBJECT pIcon
,
1074 HBRUSH hbrFlickerFreeDraw
,
1077 PSURFACE psurfDest
, psurfMask
, psurfColor
; //, psurfOffScreen = NULL;
1080 HBITMAP hbmMask
, hbmColor
, hbmAlpha
;
1082 RECTL rcDest
, rcSrc
;
1083 CLIPOBJ
* pdcClipObj
= NULL
;
1087 if((diFlags
& DI_NORMAL
) == 0)
1089 ERR("DrawIconEx called without mask or color bitmap to draw.\n");
1093 if (pIcon
->CURSORF_flags
& CURSORF_ACON
)
1095 ACON
* pAcon
= (ACON
*)pIcon
;
1096 if(istepIfAniCur
>= pAcon
->cicur
)
1098 ERR("NtUserDrawIconEx: istepIfAniCur too big!\n");
1101 pIcon
= pAcon
->aspcur
[pAcon
->aicur
[istepIfAniCur
]];
1104 hbmMask
= pIcon
->hbmMask
;
1105 hbmColor
= pIcon
->hbmColor
;
1106 hbmAlpha
= pIcon
->hbmAlpha
;
1110 * Shared locks are enough, we are only reading those bitmaps
1112 psurfMask
= SURFACE_ShareLockSurface(hbmMask
);
1113 if(psurfMask
== NULL
)
1115 ERR("Unable to lock the mask surface.\n");
1119 /* Color bitmap is not mandatory */
1120 if(hbmColor
== NULL
)
1122 /* But then the mask bitmap must have the information in it's bottom half */
1123 ASSERT(psurfMask
->SurfObj
.sizlBitmap
.cy
== 2*pIcon
->cy
);
1126 else if ((psurfColor
= SURFACE_ShareLockSurface(hbmColor
)) == NULL
)
1128 ERR("Unable to lock the color bitmap.\n");
1129 SURFACE_ShareUnlockSurface(psurfMask
);
1133 pdc
= DC_LockDc(hDc
);
1136 ERR("Could not lock the destination DC.\n");
1137 SURFACE_ShareUnlockSurface(psurfMask
);
1138 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1141 /* Calculate destination rectangle */
1142 RECTL_vSetRect(&rcDest
, xLeft
, yTop
, xLeft
+ cxWidth
, yTop
+ cyHeight
);
1143 IntLPtoDP(pdc
, (LPPOINT
)&rcDest
, 2);
1144 RECTL_vOffsetRect(&rcDest
, pdc
->ptlDCOrig
.x
, pdc
->ptlDCOrig
.y
);
1146 /* Prepare the underlying surface */
1147 DC_vPrepareDCsForBlit(pdc
, &rcDest
, NULL
, NULL
);
1149 /* We now have our destination surface and rectangle */
1150 psurfDest
= pdc
->dclevel
.pSurface
;
1152 if(psurfDest
== NULL
)
1155 DC_vFinishBlit(pdc
, NULL
);
1157 SURFACE_ShareUnlockSurface(psurfMask
);
1158 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1162 /* Set source rect */
1163 RECTL_vSetRect(&rcSrc
, 0, 0, pIcon
->cx
, pIcon
->cy
);
1165 /* Fix width parameter, if needed */
1168 if(diFlags
& DI_DEFAULTSIZE
)
1169 cxWidth
= is_icon(pIcon
) ?
1170 UserGetSystemMetrics(SM_CXICON
) : UserGetSystemMetrics(SM_CXCURSOR
);
1172 cxWidth
= pIcon
->cx
;
1175 /* Fix height parameter, if needed */
1178 if(diFlags
& DI_DEFAULTSIZE
)
1179 cyHeight
= is_icon(pIcon
) ?
1180 UserGetSystemMetrics(SM_CYICON
) : UserGetSystemMetrics(SM_CYCURSOR
);
1182 cyHeight
= pIcon
->cy
;
1185 /* Should we render off-screen? */
1186 bOffScreen
= hbrFlickerFreeDraw
&&
1187 (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw
) == GDI_OBJECT_TYPE_BRUSH
);
1191 /* Yes: Allocate and paint the offscreen surface */
1193 PBRUSH pbrush
= BRUSH_ShareLockBrush(hbrFlickerFreeDraw
);
1195 TRACE("Performing off-screen rendering.\n");
1199 ERR("Failed to get brush object.\n");
1203 #if 0 //We lock the hdc surface during the whole function it makes no sense to use an offscreen surface for "flicker free" drawing
1204 psurfOffScreen
= SURFACE_AllocSurface(STYPE_BITMAP
,
1205 cxWidth
, cyHeight
, psurfDest
->SurfObj
.iBitmapFormat
,
1209 ERR("Failed to allocate the off-screen surface.\n");
1210 BRUSH_ShareUnlockBrush(pbrush
);
1214 /* Paint the brush */
1215 EBRUSHOBJ_vInit(&eboFill
, pbrush
, psurfOffScreen
, 0x00FFFFFF, 0, NULL
);
1216 RECTL_vSetRect(&rcDest
, 0, 0, cxWidth
, cyHeight
);
1218 Ret
= IntEngBitBlt(&psurfOffScreen
->SurfObj
,
1226 &eboFill
.BrushObject
,
1230 /* Clean up everything */
1231 EBRUSHOBJ_vCleanup(&eboFill
);
1232 BRUSH_ShareUnlockBrush(pbrush
);
1236 ERR("Failed to paint the off-screen surface.\n");
1240 /* We now have our destination surface */
1241 psurfDest
= psurfOffScreen
;
1243 pdcClipObj
= &pdc
->co
.ClipObj
;
1244 /* Paint the brush */
1245 EBRUSHOBJ_vInit(&eboFill
, pbrush
, psurfDest
, 0x00FFFFFF, 0, NULL
);
1247 Ret
= IntEngBitBlt(&psurfDest
->SurfObj
,
1255 &eboFill
.BrushObject
,
1259 /* Clean up everything */
1260 EBRUSHOBJ_vCleanup(&eboFill
);
1261 BRUSH_ShareUnlockBrush(pbrush
);
1265 ERR("Failed to paint the off-screen surface.\n");
1272 /* We directly draw to the DC */
1273 TRACE("Performing on screen rendering.\n");
1274 pdcClipObj
= &pdc
->co
.ClipObj
;
1275 // psurfOffScreen = NULL;
1278 /* Now do the rendering */
1279 if(hbmAlpha
&& ((diFlags
& DI_NORMAL
) == DI_NORMAL
))
1281 BLENDOBJ blendobj
= { {AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
} };
1282 PSURFACE psurf
= NULL
;
1284 psurf
= SURFACE_ShareLockSurface(hbmAlpha
);
1287 ERR("SURFACE_LockSurface failed!\n");
1291 /* Initialize color translation object */
1292 EXLATEOBJ_vInitialize(&exlo
, psurf
->ppal
, psurfDest
->ppal
, 0xFFFFFFFF, 0xFFFFFFFF, 0);
1295 Ret
= IntEngAlphaBlend(&psurfDest
->SurfObj
,
1303 EXLATEOBJ_vCleanup(&exlo
);
1304 SURFACE_ShareUnlockSurface(psurf
);
1306 ERR("NtGdiAlphaBlend failed!\n");
1309 if (diFlags
& DI_MASK
)
1311 DWORD rop4
= (diFlags
& DI_IMAGE
) ? ROP4_SRCAND
: ROP4_SRCCOPY
;
1313 EXLATEOBJ_vInitSrcMonoXlate(&exlo
, psurfDest
->ppal
, 0x00FFFFFF, 0);
1315 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1316 &psurfMask
->SurfObj
,
1328 EXLATEOBJ_vCleanup(&exlo
);
1332 ERR("Failed to mask the bitmap data.\n");
1337 if(diFlags
& DI_IMAGE
)
1341 DWORD rop4
= (diFlags
& DI_MASK
) ? ROP4_SRCINVERT
: ROP4_SRCCOPY
;
1343 EXLATEOBJ_vInitialize(&exlo
, psurfColor
->ppal
, psurfDest
->ppal
, 0x00FFFFFF, 0x00FFFFFF, 0);
1345 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1346 &psurfColor
->SurfObj
,
1358 EXLATEOBJ_vCleanup(&exlo
);
1362 ERR("Failed to render the icon bitmap.\n");
1368 /* Mask bitmap holds the information in its bottom half */
1369 DWORD rop4
= (diFlags
& DI_MASK
) ? ROP4_SRCINVERT
: ROP4_SRCCOPY
;
1370 RECTL_vOffsetRect(&rcSrc
, 0, pIcon
->cy
);
1372 EXLATEOBJ_vInitSrcMonoXlate(&exlo
, psurfDest
->ppal
, 0x00FFFFFF, 0);
1374 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1375 &psurfMask
->SurfObj
,
1387 EXLATEOBJ_vCleanup(&exlo
);
1391 ERR("Failed to render the icon bitmap.\n");
1399 /* We're done. Was it a double buffered draw ? */
1402 /* Yes. Draw it back to our DC */
1403 POINTL ptSrc
= {0, 0};
1405 /* Calculate destination rectangle */
1406 RECTL_vSetRect(&rcDest
, xLeft
, yTop
, xLeft
+ cxWidth
, yTop
+ cyHeight
);
1407 IntLPtoDP(pdc
, (LPPOINT
)&rcDest
, 2);
1408 RECTL_vOffsetRect(&rcDest
, pdc
->ptlDCOrig
.x
, pdc
->ptlDCOrig
.y
);
1410 /* Get the clip object */
1411 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1413 /* We now have our destination surface and rectangle */
1414 psurfDest
= pdc
->dclevel
.pSurface
;
1416 /* Color translation */
1417 EXLATEOBJ_vInitialize(&exlo
, psurfOffScreen
->ppal
, psurfDest
->ppal
, 0x00FFFFFF, 0x00FFFFFF, 0);
1420 Ret
= IntEngBitBlt(&psurfDest
->SurfObj
,
1421 &psurfOffScreen
->SurfObj
,
1432 EXLATEOBJ_vCleanup(&exlo
);
1438 DC_vFinishBlit(pdc
, NULL
);
1443 /* Delete off screen rendering surface */
1445 GDIOBJ_vDeleteObject(&psurfOffScreen
->BaseObject
);
1448 /* Unlock other surfaces */
1449 SURFACE_ShareUnlockSurface(psurfMask
);
1450 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1468 HBRUSH hbrFlickerFreeDraw
,
1470 BOOL bMetaHDC
, // When TRUE, GDI functions need to be handled in User32!
1473 PCURICON_OBJECT pIcon
;
1476 TRACE("Enter NtUserDrawIconEx\n");
1477 UserEnterExclusive();
1479 if (!(pIcon
= UserGetCurIconObject(hIcon
)))
1481 ERR("UserGetCurIconObject(0x%08x) failed!\n", hIcon
);
1486 Ret
= UserDrawIconEx(hdc
,
1496 UserDereferenceObject(pIcon
);
1507 NtUserGetCursorFrameInfo(
1513 PCURICON_OBJECT CurIcon
;
1517 NTSTATUS Status
= STATUS_SUCCESS
;
1519 TRACE("Enter NtUserGetCursorFrameInfo\n");
1520 UserEnterExclusive();
1522 if (!(CurIcon
= UserGetCurIconObject(hCursor
)))
1528 ret
= CurIcon
->head
.h
;
1530 if(CurIcon
->CURSORF_flags
& CURSORF_ACON
)
1532 PACON AniCurIcon
= (PACON
)CurIcon
;
1533 if(istep
>= AniCurIcon
->cicur
)
1535 UserDereferenceObject(CurIcon
);
1539 jiffies
= AniCurIcon
->ajifRate
[istep
];
1540 steps
= AniCurIcon
->cicur
;
1541 ret
= AniCurIcon
->aspcur
[AniCurIcon
->aicur
[istep
]]->head
.h
;
1546 ProbeForWrite(rate_jiffies
, sizeof(INT
), 1);
1547 ProbeForWrite(num_steps
, sizeof(DWORD
), 1);
1548 *rate_jiffies
= jiffies
;
1551 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1553 Status
= _SEH2_GetExceptionCode();
1557 if (!NT_SUCCESS(Status
))
1559 WARN("Status: 0x%08x.\n", Status
);
1560 SetLastNtError(Status
);
1564 UserDereferenceObject(CurIcon
);
1567 TRACE("Leaving NtUserGetCursorFrameInfo, ret = 0x%08x\n", ret
);