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
20 * There's a M:N relationship between processes and (shared) cursor/icons.
21 * A process can have multiple cursor/icons and a cursor/icon can be used
22 * by multiple processes. To keep track of this we keep a list of all
23 * cursor/icons (CurIconList) and per cursor/icon we keep a list of
24 * CURICON_PROCESS structs starting at CurIcon->ProcessList.
28 DBG_DEFAULT_CHANNEL(UserIcon
);
30 static LIST_ENTRY gCurIconList
;
31 static PAGED_LOOKASIDE_LIST
*pgProcessLookasideList
;
33 SYSTEM_CURSORINFO gSysCursorInfo
;
38 pgProcessLookasideList
= ExAllocatePool(NonPagedPool
, sizeof(PAGED_LOOKASIDE_LIST
));
39 if(!pgProcessLookasideList
)
42 ExInitializePagedLookasideList(pgProcessLookasideList
,
46 sizeof(CURICON_PROCESS
),
49 InitializeListHead(&gCurIconList
);
51 gSysCursorInfo
.Enabled
= FALSE
;
52 gSysCursorInfo
.ButtonsDown
= 0;
53 gSysCursorInfo
.bClipped
= FALSE
;
54 gSysCursorInfo
.LastBtnDown
= 0;
55 gSysCursorInfo
.CurrentCursorObject
= NULL
;
56 gSysCursorInfo
.ShowingCursor
= -1;
57 gSysCursorInfo
.ClickLockActive
= FALSE
;
58 gSysCursorInfo
.ClickLockTime
= 0;
66 return &gSysCursorInfo
;
71 is_icon(PCURICON_OBJECT object
)
73 return MAKEINTRESOURCE(object
->rt
) == RT_ICON
;
76 /* This function creates a reference for the object! */
77 PCURICON_OBJECT FASTCALL
UserGetCurIconObject(HCURSOR hCurIcon
)
79 PCURICON_OBJECT CurIcon
;
83 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
87 CurIcon
= (PCURICON_OBJECT
)UserReferenceObjectByHandle(hCurIcon
, otCursorIcon
);
90 /* We never set ERROR_INVALID_ICON_HANDLE. lets hope noone ever checks for it */
91 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
95 ASSERT(CurIcon
->head
.cLockObj
>= 1);
99 BOOL
UserSetCursorPos( INT x
, INT y
, DWORD flags
, ULONG_PTR dwExtraInfo
, BOOL Hook
)
102 PSYSTEM_CURSORINFO CurInfo
;
107 if(!(DesktopWindow
= UserGetDesktopWindow()))
112 CurInfo
= IntGetSysCursorInfo();
114 /* Clip cursor position */
115 if (!CurInfo
->bClipped
)
116 rcClip
= DesktopWindow
->rcClient
;
118 rcClip
= CurInfo
->rcClip
;
120 if(x
>= rcClip
.right
) x
= rcClip
.right
- 1;
121 if(x
< rcClip
.left
) x
= rcClip
.left
;
122 if(y
>= rcClip
.bottom
) y
= rcClip
.bottom
- 1;
123 if(y
< rcClip
.top
) y
= rcClip
.top
;
128 /* 1. Generate a mouse move message, this sets the htEx and Track Window too. */
129 Msg
.message
= WM_MOUSEMOVE
;
130 Msg
.wParam
= UserGetMouseButtonsState();
131 Msg
.lParam
= MAKELPARAM(x
, y
);
133 co_MsqInsertMouseMessage(&Msg
, flags
, dwExtraInfo
, Hook
);
135 /* 2. Store the new cursor position */
142 * We have to register that this object is in use by the current
143 * process. The only way to do that seems to be to walk the list
144 * of cursor/icon objects starting at W32Process->CursorIconListHead.
145 * If the object is already present in the list, we don't have to do
146 * anything, if it's not present we add it and inc the ProcessCount
147 * in the object. Having to walk the list kind of sucks, but that's
150 static BOOLEAN FASTCALL
151 ReferenceCurIconByProcess(PCURICON_OBJECT CurIcon
)
153 PPROCESSINFO Win32Process
;
154 PCURICON_PROCESS Current
;
156 Win32Process
= PsGetCurrentProcessWin32Process();
158 LIST_FOR_EACH(Current
, &CurIcon
->ProcessList
, CURICON_PROCESS
, ListEntry
)
160 if (Current
->Process
== Win32Process
)
162 /* Already registered for this process */
167 /* Not registered yet */
168 Current
= ExAllocateFromPagedLookasideList(pgProcessLookasideList
);
173 InsertHeadList(&CurIcon
->ProcessList
, &Current
->ListEntry
);
174 Current
->Process
= Win32Process
;
182 IntFindExistingCurIconObject(
183 PUNICODE_STRING pustrModule
,
184 PUNICODE_STRING pustrRsrc
,
185 FINDEXISTINGCURICONPARAM
* param
)
187 PCURICON_OBJECT CurIcon
;
189 LIST_FOR_EACH(CurIcon
, &gCurIconList
, CURICON_OBJECT
, ListEntry
)
191 /* See if we are looking for an icon or a cursor */
192 if(MAKEINTRESOURCE(CurIcon
->rt
) != (param
->bIcon
? RT_ICON
: RT_CURSOR
))
194 /* See if module names match */
195 if(RtlCompareUnicodeString(pustrModule
, &CurIcon
->ustrModule
, TRUE
) == 0)
197 /* They do. Now see if this is the same resource */
198 if(IS_INTRESOURCE(CurIcon
->strName
.Buffer
) && IS_INTRESOURCE(pustrRsrc
->Buffer
))
200 if(CurIcon
->strName
.Buffer
!= pustrRsrc
->Buffer
)
203 else if(IS_INTRESOURCE(CurIcon
->strName
.Buffer
) || IS_INTRESOURCE(pustrRsrc
->Buffer
))
205 else if(RtlCompareUnicodeString(pustrRsrc
, &CurIcon
->strName
, TRUE
) != 0)
208 if ((param
->cx
== CurIcon
->cx
) && (param
->cy
== CurIcon
->cy
))
210 if (! ReferenceCurIconByProcess(CurIcon
))
224 IntCreateCurIconHandle(DWORD dwNumber
)
226 PCURICON_OBJECT CurIcon
;
232 CurIcon
= UserCreateObject(gHandleTable
, NULL
, NULL
, &hCurIcon
, otCursorIcon
, sizeof(CURICON_OBJECT
));
236 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
240 CurIcon
->Self
= hCurIcon
;
241 InitializeListHead(&CurIcon
->ProcessList
);
243 if (! ReferenceCurIconByProcess(CurIcon
))
245 ERR("Failed to add process\n");
246 UserDereferenceObject(CurIcon
);
247 UserDeleteObject(hCurIcon
, otCursorIcon
);
251 InsertHeadList(&gCurIconList
, &CurIcon
->ListEntry
);
257 IntDestroyCurIconObject(PCURICON_OBJECT CurIcon
, PPROCESSINFO ppi
, BOOLEAN bForce
)
259 HBITMAP bmpMask
, bmpColor
, bmpAlpha
;
260 BOOLEAN Ret
, bListEmpty
, bFound
= FALSE
;
261 PCURICON_PROCESS Current
= NULL
;
263 /* Check if this is the current cursor */
264 if(CurIcon
->CURSORF_flags
& CURSORF_CURRENT
)
266 UserDereferenceObject(CurIcon
);
270 /* For handles created without any data (error handling) */
271 if(IsListEmpty(&CurIcon
->ProcessList
))
274 /* Now find this process in the list of processes referencing this object and
275 remove it from that list */
276 LIST_FOR_EACH(Current
, &CurIcon
->ProcessList
, CURICON_PROCESS
, ListEntry
)
278 if (Current
->Process
== ppi
)
281 bListEmpty
= RemoveEntryList(&Current
->ListEntry
);
288 /* This object doesn't belong to this process */
289 EngSetLastError(ERROR_INVALID_HANDLE
);
290 /* Caller expects us to dereference! */
291 UserDereferenceObject(CurIcon
);
295 /* We found our process, but we're told to not destroy it in case it is shared */
296 if((CurIcon
->ustrModule
.Buffer
!= NULL
) && !bForce
)
298 /* Tests show this is a valid call */
299 UserDereferenceObject(CurIcon
);
303 ExFreeToPagedLookasideList(pgProcessLookasideList
, Current
);
305 /* If there are still processes referencing this object we can't destroy it yet */
308 if(CurIcon
->head
.ppi
== ppi
)
310 /* Set the first process of the list as owner */
311 Current
= CONTAINING_RECORD(CurIcon
->ProcessList
.Flink
, CURICON_PROCESS
, ListEntry
);
312 UserSetObjectOwner(CurIcon
, otCursorIcon
, Current
->Process
);
314 UserDereferenceObject(CurIcon
);
319 /* Remove it from the list */
320 RemoveEntryList(&CurIcon
->ListEntry
);
322 bmpMask
= CurIcon
->hbmMask
;
323 bmpColor
= CurIcon
->hbmColor
;
324 bmpAlpha
= CurIcon
->hbmAlpha
;
329 GreSetObjectOwner(bmpMask
, GDI_OBJ_HMGR_POWNED
);
330 GreDeleteObject(bmpMask
);
331 CurIcon
->hbmMask
= NULL
;
335 GreSetObjectOwner(bmpColor
, GDI_OBJ_HMGR_POWNED
);
336 GreDeleteObject(bmpColor
);
337 CurIcon
->hbmColor
= NULL
;
341 GreSetObjectOwner(bmpAlpha
, GDI_OBJ_HMGR_POWNED
);
342 GreDeleteObject(bmpAlpha
);
343 CurIcon
->hbmAlpha
= NULL
;
346 if(!IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
347 ExFreePoolWithTag(CurIcon
->strName
.Buffer
, TAG_STRING
);
348 if(CurIcon
->ustrModule
.Buffer
)
349 ReleaseCapturedUnicodeString(&CurIcon
->ustrModule
, UserMode
);
351 /* We were given a pointer, no need to keep the reference anylonger! */
352 UserDereferenceObject(CurIcon
);
353 Ret
= UserDeleteObject(CurIcon
->Self
, otCursorIcon
);
359 IntCleanupCurIcons(struct _EPROCESS
*Process
, PPROCESSINFO Win32Process
)
361 PCURICON_OBJECT CurIcon
, tmp
;
363 /* Run through the list of icon objects */
364 LIST_FOR_EACH_SAFE(CurIcon
, tmp
, &gCurIconList
, CURICON_OBJECT
, ListEntry
)
366 UserReferenceObject(CurIcon
);
367 IntDestroyCurIconObject(CurIcon
, Win32Process
, TRUE
);
378 _In_ HANDLE hCurIcon
,
379 _Out_opt_ PICONINFO IconInfo
,
380 _Out_opt_ PUNICODE_STRING lpModule
, // Optional
381 _Out_opt_ PUNICODE_STRING lpResName
, // Optional
382 _Out_opt_ LPDWORD pbpp
, // Optional
386 PCURICON_OBJECT CurIcon
;
387 NTSTATUS Status
= STATUS_SUCCESS
;
391 TRACE("Enter NtUserGetIconInfo\n");
393 /* Check if something was actually asked */
394 if (!IconInfo
&& !lpModule
&& !lpResName
)
396 WARN("Nothing to fill.\n");
397 EngSetLastError(ERROR_INVALID_PARAMETER
);
401 UserEnterExclusive();
403 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
405 WARN("UserGetIconObject(0x%08x) Failed.\n", hCurIcon
);
410 /* Give back the icon information */
414 ii
.fIcon
= is_icon(CurIcon
);
415 ii
.xHotspot
= CurIcon
->xHotspot
;
416 ii
.yHotspot
= CurIcon
->yHotspot
;
419 ii
.hbmMask
= BITMAP_CopyBitmap(CurIcon
->hbmMask
);
420 GreSetObjectOwner(ii
.hbmMask
, GDI_OBJ_HMGR_POWNED
);
421 ii
.hbmColor
= BITMAP_CopyBitmap(CurIcon
->hbmColor
);
422 GreSetObjectOwner(ii
.hbmColor
, GDI_OBJ_HMGR_POWNED
);
423 colorBpp
= CurIcon
->bpp
;
428 ProbeForWrite(IconInfo
, sizeof(ICONINFO
), 1);
429 RtlCopyMemory(IconInfo
, &ii
, sizeof(ICONINFO
));
433 ProbeForWrite(pbpp
, sizeof(DWORD
), 1);
437 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
439 Status
= _SEH2_GetExceptionCode();
444 if (!NT_SUCCESS(Status
))
446 WARN("Status: 0x%08x.\n", Status
);
447 SetLastNtError(Status
);
451 /* Give back the module name */
454 if(!CurIcon
->ustrModule
.Buffer
)
456 EngSetLastError(ERROR_INVALID_HANDLE
);
459 /* Copy what we can */
462 ProbeForWrite(lpModule
, sizeof(UNICODE_STRING
), 1);
463 ProbeForWrite(lpModule
->Buffer
, lpModule
->MaximumLength
, 1);
464 lpModule
->Length
= min(lpModule
->MaximumLength
, CurIcon
->ustrModule
.Length
);
465 RtlCopyMemory(lpModule
->Buffer
, CurIcon
->ustrModule
.Buffer
, lpModule
->Length
);
467 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
469 Status
= _SEH2_GetExceptionCode();
474 if (!NT_SUCCESS(Status
))
476 SetLastNtError(Status
);
482 if(!CurIcon
->strName
.Buffer
)
484 EngSetLastError(ERROR_INVALID_HANDLE
);
490 ProbeForWrite(lpResName
, sizeof(UNICODE_STRING
), 1);
491 if(IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
493 lpResName
->Buffer
= CurIcon
->strName
.Buffer
;
494 lpResName
->Length
= 0;
498 lpResName
->Length
= min(lpResName
->MaximumLength
, CurIcon
->strName
.Length
);
499 RtlCopyMemory(lpResName
->Buffer
, CurIcon
->strName
.Buffer
, lpResName
->Length
);
502 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
504 Status
= _SEH2_GetExceptionCode();
509 if (!NT_SUCCESS(Status
))
511 SetLastNtError(Status
);
518 UserDereferenceObject(CurIcon
);
520 TRACE("Leave NtUserGetIconInfo, ret=%i\n", Ret
);
535 PLONG plcx
, // &size.cx
536 PLONG plcy
) // &size.cy
538 PCURICON_OBJECT CurIcon
;
539 NTSTATUS Status
= STATUS_SUCCESS
;
542 TRACE("Enter NtUserGetIconSize\n");
543 UserEnterExclusive();
545 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
552 ProbeForWrite(plcx
, sizeof(LONG
), 1);
554 ProbeForWrite(plcy
, sizeof(LONG
), 1);
557 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
559 Status
= _SEH2_GetExceptionCode();
563 if (NT_SUCCESS(Status
))
566 SetLastNtError(Status
); // Maybe not, test this
568 UserDereferenceObject(CurIcon
);
571 TRACE("Leave NtUserGetIconSize, ret=%i\n", bRet
);
586 PSYSTEM_CURSORINFO CurInfo
;
587 NTSTATUS Status
= STATUS_SUCCESS
;
588 PCURICON_OBJECT CurIcon
;
590 DECLARE_RETURN(BOOL
);
592 TRACE("Enter NtUserGetCursorInfo\n");
593 UserEnterExclusive();
595 CurInfo
= IntGetSysCursorInfo();
596 CurIcon
= (PCURICON_OBJECT
)CurInfo
->CurrentCursorObject
;
598 SafeCi
.cbSize
= sizeof(CURSORINFO
);
599 SafeCi
.flags
= ((CurIcon
&& CurInfo
->ShowingCursor
>= 0) ? CURSOR_SHOWING
: 0);
600 SafeCi
.hCursor
= (CurIcon
? (HCURSOR
)CurIcon
->Self
: (HCURSOR
)0);
602 SafeCi
.ptScreenPos
= gpsi
->ptCursor
;
606 if (pci
->cbSize
== sizeof(CURSORINFO
))
608 ProbeForWrite(pci
, sizeof(CURSORINFO
), 1);
609 RtlCopyMemory(pci
, &SafeCi
, sizeof(CURSORINFO
));
614 EngSetLastError(ERROR_INVALID_PARAMETER
);
617 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
619 Status
= _SEH2_GetExceptionCode();
622 if (!NT_SUCCESS(Status
))
624 SetLastNtError(Status
);
630 TRACE("Leave NtUserGetCursorInfo, ret=%i\n",_ret_
);
640 /* FIXME: Check if process has WINSTA_WRITEATTRIBUTES */
641 PSYSTEM_CURSORINFO CurInfo
;
642 PWND DesktopWindow
= NULL
;
644 CurInfo
= IntGetSysCursorInfo();
646 DesktopWindow
= UserGetDesktopWindow();
648 if (prcl
!= NULL
&& DesktopWindow
!= NULL
)
650 if (prcl
->right
< prcl
->left
|| prcl
->bottom
< prcl
->top
)
652 EngSetLastError(ERROR_INVALID_PARAMETER
);
656 CurInfo
->bClipped
= TRUE
;
658 /* Set nw cliping region. Note: we can't use RECTL_bIntersectRect because
659 it sets rect to 0 0 0 0 when it's empty. For more info see monitor winetest */
660 CurInfo
->rcClip
.left
= max(prcl
->left
, DesktopWindow
->rcWindow
.left
);
661 CurInfo
->rcClip
.right
= min(prcl
->right
, DesktopWindow
->rcWindow
.right
);
662 if (CurInfo
->rcClip
.right
< CurInfo
->rcClip
.left
)
663 CurInfo
->rcClip
.right
= CurInfo
->rcClip
.left
;
665 CurInfo
->rcClip
.top
= max(prcl
->top
, DesktopWindow
->rcWindow
.top
);
666 CurInfo
->rcClip
.bottom
= min(prcl
->bottom
, DesktopWindow
->rcWindow
.bottom
);
667 if (CurInfo
->rcClip
.bottom
< CurInfo
->rcClip
.top
)
668 CurInfo
->rcClip
.bottom
= CurInfo
->rcClip
.top
;
670 /* Make sure cursor is in clipping region */
671 UserSetCursorPos(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
, 0, 0, FALSE
);
675 CurInfo
->bClipped
= FALSE
;
696 /* Probe and copy rect */
697 ProbeForRead(prcl
, sizeof(RECTL
), 1);
700 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
702 EngSetLastError(ERROR_INVALID_PARAMETER
);
703 _SEH2_YIELD(return FALSE
;)
710 UserEnterExclusive();
712 /* Call the internal function */
713 bResult
= UserClipCursor(prcl
);
727 _In_ HANDLE hCurIcon
,
730 PCURICON_OBJECT CurIcon
;
732 DECLARE_RETURN(BOOL
);
734 TRACE("Enter NtUserDestroyCursorIcon\n");
735 UserEnterExclusive();
737 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
742 ret
= IntDestroyCurIconObject(CurIcon
, PsGetCurrentProcessWin32Process(), bForce
);
743 /* Note: IntDestroyCurIconObject will remove our reference for us! */
748 TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_
);
759 NtUserFindExistingCursorIcon(
760 _In_ PUNICODE_STRING pustrModule
,
761 _In_ PUNICODE_STRING pustrRsrc
,
762 _In_ FINDEXISTINGCURICONPARAM
* param
)
764 PCURICON_OBJECT CurIcon
;
766 UNICODE_STRING ustrModuleSafe
, ustrRsrcSafe
;
767 FINDEXISTINGCURICONPARAM paramSafe
;
770 TRACE("Enter NtUserFindExistingCursorIcon\n");
772 /* Capture resource name (it can be an INTRESOURCE == ATOM) */
773 Status
= ProbeAndCaptureUnicodeStringOrAtom(&ustrRsrcSafe
, pustrRsrc
);
774 if(!NT_SUCCESS(Status
))
776 Status
= ProbeAndCaptureUnicodeString(&ustrModuleSafe
, UserMode
, pustrModule
);
777 if(!NT_SUCCESS(Status
))
782 ProbeForRead(param
, sizeof(*param
), 1);
783 RtlCopyMemory(¶mSafe
, param
, sizeof(paramSafe
));
785 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
787 Status
= _SEH2_GetExceptionCode();
791 UserEnterExclusive();
792 CurIcon
= IntFindExistingCurIconObject(&ustrModuleSafe
, &ustrRsrcSafe
, ¶mSafe
);
798 if(!IS_INTRESOURCE(ustrRsrcSafe
.Buffer
))
799 ExFreePoolWithTag(ustrRsrcSafe
.Buffer
, TAG_STRING
);
800 ReleaseCapturedUnicodeString(&ustrModuleSafe
, UserMode
);
814 /* FIXME: Check if process has WINSTA_READATTRIBUTES */
815 PSYSTEM_CURSORINFO CurInfo
;
818 DECLARE_RETURN(BOOL
);
820 TRACE("Enter NtUserGetClipCursor\n");
821 UserEnterExclusive();
826 CurInfo
= IntGetSysCursorInfo();
827 if (CurInfo
->bClipped
)
829 Rect
= CurInfo
->rcClip
;
835 Rect
.right
= UserGetSystemMetrics(SM_CXSCREEN
);
836 Rect
.bottom
= UserGetSystemMetrics(SM_CYSCREEN
);
839 Status
= MmCopyToCaller(lpRect
, &Rect
, sizeof(RECT
));
840 if (!NT_SUCCESS(Status
))
842 SetLastNtError(Status
);
849 TRACE("Leave NtUserGetClipCursor, ret=%i\n",_ret_
);
863 PCURICON_OBJECT pcurOld
, pcurNew
;
864 HCURSOR hOldCursor
= NULL
;
866 TRACE("Enter NtUserSetCursor\n");
867 UserEnterExclusive();
871 pcurNew
= UserGetCurIconObject(hCursor
);
874 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
877 pcurNew
->CURSORF_flags
|= CURSORF_CURRENT
;
884 pcurOld
= UserSetCursor(pcurNew
, FALSE
);
887 hOldCursor
= (HCURSOR
)pcurOld
->Self
;
888 pcurOld
->CURSORF_flags
&= ~CURSORF_CURRENT
;
889 UserDereferenceObject(pcurOld
);
903 NtUserSetCursorContents(
905 PICONINFO UnsafeIconInfo
)
907 FIXME(" is UNIMPLEMENTED.\n");
917 NtUserSetCursorIconData(
919 _In_opt_ PUNICODE_STRING pustrModule
,
920 _In_opt_ PUNICODE_STRING pustrRsrc
,
921 _In_ PCURSORDATA pCursorData
)
923 PCURICON_OBJECT CurIcon
;
924 NTSTATUS Status
= STATUS_SUCCESS
;
927 TRACE("Enter NtUserSetCursorIconData\n");
929 /* If a module name is provided, we need a resource name, and vice versa */
930 if((pustrModule
&& !pustrRsrc
) || (!pustrModule
&& pustrRsrc
))
933 UserEnterExclusive();
935 if (!(CurIcon
= UserGetCurIconObject(Handle
)))
938 EngSetLastError(ERROR_INVALID_HANDLE
);
944 ProbeForRead(pCursorData
, sizeof(*pCursorData
), 1);
945 CurIcon
->xHotspot
= pCursorData
->xHotspot
;
946 CurIcon
->yHotspot
= pCursorData
->yHotspot
;
947 CurIcon
->cx
= pCursorData
->cx
;
948 CurIcon
->cy
= pCursorData
->cy
;
949 CurIcon
->rt
= pCursorData
->rt
;
950 CurIcon
->bpp
= pCursorData
->bpp
;
951 CurIcon
->hbmMask
= pCursorData
->hbmMask
;
952 CurIcon
->hbmColor
= pCursorData
->hbmColor
;
953 CurIcon
->hbmAlpha
= pCursorData
->hbmAlpha
;
955 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
957 Status
= _SEH2_GetExceptionCode();
961 if (!NT_SUCCESS(Status
))
963 SetLastNtError(Status
);
969 /* We use this convenient function, because INTRESOURCEs and ATOMs are the same */
970 Status
= ProbeAndCaptureUnicodeStringOrAtom(&CurIcon
->strName
, pustrRsrc
);
971 if(!NT_SUCCESS(Status
))
973 Status
= ProbeAndCaptureUnicodeString(&CurIcon
->ustrModule
, UserMode
, pustrModule
);
974 if(!NT_SUCCESS(Status
))
979 if(!CurIcon
->hbmMask
)
981 ERR("NtUserSetCursorIconData was got no hbmMask.\n");
982 EngSetLastError(ERROR_INVALID_PARAMETER
);
986 GreSetObjectOwner(CurIcon
->hbmMask
, GDI_OBJ_HMGR_PUBLIC
);
988 if(CurIcon
->hbmColor
)
989 GreSetObjectOwner(CurIcon
->hbmColor
, GDI_OBJ_HMGR_PUBLIC
);
991 if(CurIcon
->hbmAlpha
)
992 GreSetObjectOwner(CurIcon
->hbmAlpha
, GDI_OBJ_HMGR_PUBLIC
);
999 if(!IS_INTRESOURCE(CurIcon
->strName
.Buffer
))
1000 ExFreePoolWithTag(CurIcon
->strName
.Buffer
, TAG_STRING
);
1001 if(CurIcon
->ustrModule
.Buffer
)
1002 ReleaseCapturedUnicodeString(&CurIcon
->ustrModule
, UserMode
);
1004 UserDereferenceObject(CurIcon
);
1005 TRACE("Leave NtUserSetCursorIconData, ret=%i\n",Ret
);
1011 /* Mostly inspired from wine code.
1012 * We use low level functions because:
1013 * - at this point, the icon bitmap could have a different bit depth than the DC,
1014 * making it thus impossible to use NtCreateCompatibleDC and selecting the bitmap.
1015 * This happens after a mode setting change.
1016 * - it avoids massive GDI objects locking when only the destination surface needs it.
1017 * - It makes (small) performance gains.
1024 PCURICON_OBJECT pIcon
,
1028 HBRUSH hbrFlickerFreeDraw
,
1031 PSURFACE psurfDest
, psurfMask
, psurfColor
; //, psurfOffScreen = NULL;
1034 HBITMAP hbmMask
, hbmColor
, hbmAlpha
;
1036 RECTL rcDest
, rcSrc
;
1037 CLIPOBJ
* pdcClipObj
= NULL
;
1041 if((diFlags
& DI_NORMAL
) == 0)
1043 ERR("DrawIconEx called without mask or color bitmap to draw.\n");
1047 hbmMask
= pIcon
->hbmMask
;
1048 hbmColor
= pIcon
->hbmColor
;
1049 hbmAlpha
= pIcon
->hbmAlpha
;
1052 ERR("NtUserDrawIconEx: istepIfAniCur is not supported!\n");
1056 * Shared locks are enough, we are only reading those bitmaps
1058 psurfMask
= SURFACE_ShareLockSurface(hbmMask
);
1059 if(psurfMask
== NULL
)
1061 ERR("Unable to lock the mask surface.\n");
1065 /* Color bitmap is not mandatory */
1066 if(hbmColor
== NULL
)
1068 /* But then the mask bitmap must have the information in it's bottom half */
1069 ASSERT(psurfMask
->SurfObj
.sizlBitmap
.cy
== 2*pIcon
->cy
);
1072 else if ((psurfColor
= SURFACE_ShareLockSurface(hbmColor
)) == NULL
)
1074 ERR("Unable to lock the color bitmap.\n");
1075 SURFACE_ShareUnlockSurface(psurfMask
);
1079 pdc
= DC_LockDc(hDc
);
1082 ERR("Could not lock the destination DC.\n");
1083 SURFACE_ShareUnlockSurface(psurfMask
);
1084 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1087 /* Calculate destination rectangle */
1088 RECTL_vSetRect(&rcDest
, xLeft
, yTop
, xLeft
+ cxWidth
, yTop
+ cyHeight
);
1089 IntLPtoDP(pdc
, (LPPOINT
)&rcDest
, 2);
1090 RECTL_vOffsetRect(&rcDest
, pdc
->ptlDCOrig
.x
, pdc
->ptlDCOrig
.y
);
1092 /* Prepare the underlying surface */
1093 DC_vPrepareDCsForBlit(pdc
, rcDest
, NULL
, rcDest
);
1095 /* We now have our destination surface and rectangle */
1096 psurfDest
= pdc
->dclevel
.pSurface
;
1098 if(psurfDest
== NULL
)
1101 DC_vFinishBlit(pdc
, NULL
);
1103 SURFACE_ShareUnlockSurface(psurfMask
);
1104 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1108 /* Set source rect */
1109 RECTL_vSetRect(&rcSrc
, 0, 0, pIcon
->cx
, pIcon
->cy
);
1111 /* Fix width parameter, if needed */
1114 if(diFlags
& DI_DEFAULTSIZE
)
1115 cxWidth
= is_icon(pIcon
) ?
1116 UserGetSystemMetrics(SM_CXICON
) : UserGetSystemMetrics(SM_CXCURSOR
);
1118 cxWidth
= pIcon
->cx
;
1121 /* Fix height parameter, if needed */
1124 if(diFlags
& DI_DEFAULTSIZE
)
1125 cyHeight
= is_icon(pIcon
) ?
1126 UserGetSystemMetrics(SM_CYICON
) : UserGetSystemMetrics(SM_CYCURSOR
);
1128 cyHeight
= pIcon
->cy
;
1131 /* Should we render off-screen? */
1132 bOffScreen
= hbrFlickerFreeDraw
&&
1133 (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw
) == GDI_OBJECT_TYPE_BRUSH
);
1137 /* Yes: Allocate and paint the offscreen surface */
1139 PBRUSH pbrush
= BRUSH_ShareLockBrush(hbrFlickerFreeDraw
);
1141 TRACE("Performing off-screen rendering.\n");
1145 ERR("Failed to get brush object.\n");
1149 #if 0 //We lock the hdc surface during the whole function it makes no sense to use an offscreen surface for "flicker free" drawing
1150 psurfOffScreen
= SURFACE_AllocSurface(STYPE_BITMAP
,
1151 cxWidth
, cyHeight
, psurfDest
->SurfObj
.iBitmapFormat
,
1155 ERR("Failed to allocate the off-screen surface.\n");
1156 BRUSH_ShareUnlockBrush(pbrush
);
1160 /* Paint the brush */
1161 EBRUSHOBJ_vInit(&eboFill
, pbrush
, psurfOffScreen
, 0x00FFFFFF, 0, NULL
);
1162 RECTL_vSetRect(&rcDest
, 0, 0, cxWidth
, cyHeight
);
1164 Ret
= IntEngBitBlt(&psurfOffScreen
->SurfObj
,
1172 &eboFill
.BrushObject
,
1176 /* Clean up everything */
1177 EBRUSHOBJ_vCleanup(&eboFill
);
1178 BRUSH_ShareUnlockBrush(pbrush
);
1182 ERR("Failed to paint the off-screen surface.\n");
1186 /* We now have our destination surface */
1187 psurfDest
= psurfOffScreen
;
1189 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1190 /* Paint the brush */
1191 EBRUSHOBJ_vInit(&eboFill
, pbrush
, psurfDest
, 0x00FFFFFF, 0, NULL
);
1193 Ret
= IntEngBitBlt(&psurfDest
->SurfObj
,
1201 &eboFill
.BrushObject
,
1205 /* Clean up everything */
1206 EBRUSHOBJ_vCleanup(&eboFill
);
1207 BRUSH_ShareUnlockBrush(pbrush
);
1211 ERR("Failed to paint the off-screen surface.\n");
1218 /* We directly draw to the DC */
1219 TRACE("Performing on screen rendering.\n");
1220 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1221 // psurfOffScreen = NULL;
1224 /* Now do the rendering */
1225 if(hbmAlpha
&& ((diFlags
& DI_NORMAL
) == DI_NORMAL
))
1227 BLENDOBJ blendobj
= { {AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
} };
1228 PSURFACE psurf
= NULL
;
1230 psurf
= SURFACE_ShareLockSurface(hbmAlpha
);
1233 ERR("SURFACE_LockSurface failed!\n");
1237 /* Initialize color translation object */
1238 EXLATEOBJ_vInitialize(&exlo
, psurf
->ppal
, psurfDest
->ppal
, 0xFFFFFFFF, 0xFFFFFFFF, 0);
1241 Ret
= IntEngAlphaBlend(&psurfDest
->SurfObj
,
1249 EXLATEOBJ_vCleanup(&exlo
);
1250 SURFACE_ShareUnlockSurface(psurf
);
1252 ERR("NtGdiAlphaBlend failed!\n");
1255 if (diFlags
& DI_MASK
)
1257 DWORD rop4
= (diFlags
& DI_IMAGE
) ? ROP4_SRCAND
: ROP4_SRCCOPY
;
1259 EXLATEOBJ_vInitSrcMonoXlate(&exlo
, psurfDest
->ppal
, 0x00FFFFFF, 0);
1261 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1262 &psurfMask
->SurfObj
,
1274 EXLATEOBJ_vCleanup(&exlo
);
1278 ERR("Failed to mask the bitmap data.\n");
1283 if(diFlags
& DI_IMAGE
)
1287 DWORD rop4
= (diFlags
& DI_MASK
) ? ROP4_SRCINVERT
: ROP4_SRCCOPY
;
1289 EXLATEOBJ_vInitialize(&exlo
, psurfColor
->ppal
, psurfDest
->ppal
, 0x00FFFFFF, 0x00FFFFFF, 0);
1291 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1292 &psurfColor
->SurfObj
,
1304 EXLATEOBJ_vCleanup(&exlo
);
1308 ERR("Failed to render the icon bitmap.\n");
1314 /* Mask bitmap holds the information in its bottom half */
1315 DWORD rop4
= (diFlags
& DI_MASK
) ? ROP4_SRCINVERT
: ROP4_SRCCOPY
;
1316 RECTL_vOffsetRect(&rcSrc
, 0, pIcon
->cy
);
1318 EXLATEOBJ_vInitSrcMonoXlate(&exlo
, psurfDest
->ppal
, 0x00FFFFFF, 0);
1320 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1321 &psurfMask
->SurfObj
,
1333 EXLATEOBJ_vCleanup(&exlo
);
1337 ERR("Failed to render the icon bitmap.\n");
1345 /* We're done. Was it a double buffered draw ? */
1348 /* Yes. Draw it back to our DC */
1349 POINTL ptSrc
= {0, 0};
1351 /* Calculate destination rectangle */
1352 RECTL_vSetRect(&rcDest
, xLeft
, yTop
, xLeft
+ cxWidth
, yTop
+ cyHeight
);
1353 IntLPtoDP(pdc
, (LPPOINT
)&rcDest
, 2);
1354 RECTL_vOffsetRect(&rcDest
, pdc
->ptlDCOrig
.x
, pdc
->ptlDCOrig
.y
);
1356 /* Get the clip object */
1357 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1359 /* We now have our destination surface and rectangle */
1360 psurfDest
= pdc
->dclevel
.pSurface
;
1362 /* Color translation */
1363 EXLATEOBJ_vInitialize(&exlo
, psurfOffScreen
->ppal
, psurfDest
->ppal
, 0x00FFFFFF, 0x00FFFFFF, 0);
1366 Ret
= IntEngBitBlt(&psurfDest
->SurfObj
,
1367 &psurfOffScreen
->SurfObj
,
1378 EXLATEOBJ_vCleanup(&exlo
);
1384 DC_vFinishBlit(pdc
, NULL
);
1389 /* Delete off screen rendering surface */
1391 GDIOBJ_vDeleteObject(&psurfOffScreen
->BaseObject
);
1394 /* Unlock other surfaces */
1395 SURFACE_ShareUnlockSurface(psurfMask
);
1396 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1414 HBRUSH hbrFlickerFreeDraw
,
1416 BOOL bMetaHDC
, // When TRUE, GDI functions need to be handled in User32!
1419 PCURICON_OBJECT pIcon
;
1422 TRACE("Enter NtUserDrawIconEx\n");
1423 UserEnterExclusive();
1425 if (!(pIcon
= UserGetCurIconObject(hIcon
)))
1427 ERR("UserGetCurIconObject(0x%08x) failed!\n", hIcon
);
1432 Ret
= UserDrawIconEx(hdc
,
1442 UserDereferenceObject(pIcon
);