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 PPAGED_LOOKASIDE_LIST pgProcessLookasideList
;
31 static LIST_ENTRY gCurIconList
;
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
;
69 /* This function creates a reference for the object! */
70 PCURICON_OBJECT FASTCALL
UserGetCurIconObject(HCURSOR hCurIcon
)
72 PCURICON_OBJECT CurIcon
;
76 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
80 CurIcon
= (PCURICON_OBJECT
)UserReferenceObjectByHandle(hCurIcon
, otCursorIcon
);
83 /* We never set ERROR_INVALID_ICON_HANDLE. lets hope noone ever checks for it */
84 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
88 ASSERT(CurIcon
->head
.cLockObj
>= 1);
92 BOOL
UserSetCursorPos( INT x
, INT y
, DWORD flags
, ULONG_PTR dwExtraInfo
, BOOL Hook
)
95 PSYSTEM_CURSORINFO CurInfo
;
100 if(!(DesktopWindow
= UserGetDesktopWindow()))
105 CurInfo
= IntGetSysCursorInfo();
107 /* Clip cursor position */
108 if (!CurInfo
->bClipped
)
109 rcClip
= DesktopWindow
->rcClient
;
111 rcClip
= CurInfo
->rcClip
;
113 if(x
>= rcClip
.right
) x
= rcClip
.right
- 1;
114 if(x
< rcClip
.left
) x
= rcClip
.left
;
115 if(y
>= rcClip
.bottom
) y
= rcClip
.bottom
- 1;
116 if(y
< rcClip
.top
) y
= rcClip
.top
;
121 /* 1. Generate a mouse move message, this sets the htEx and Track Window too. */
122 Msg
.message
= WM_MOUSEMOVE
;
123 Msg
.wParam
= UserGetMouseButtonsState();
124 Msg
.lParam
= MAKELPARAM(x
, y
);
126 co_MsqInsertMouseMessage(&Msg
, flags
, dwExtraInfo
, Hook
);
128 /* 2. Store the new cursor position */
135 * We have to register that this object is in use by the current
136 * process. The only way to do that seems to be to walk the list
137 * of cursor/icon objects starting at W32Process->CursorIconListHead.
138 * If the object is already present in the list, we don't have to do
139 * anything, if it's not present we add it and inc the ProcessCount
140 * in the object. Having to walk the list kind of sucks, but that's
143 static BOOLEAN FASTCALL
144 ReferenceCurIconByProcess(PCURICON_OBJECT CurIcon
)
146 PPROCESSINFO Win32Process
;
147 PCURICON_PROCESS Current
;
149 Win32Process
= PsGetCurrentProcessWin32Process();
151 LIST_FOR_EACH(Current
, &CurIcon
->ProcessList
, CURICON_PROCESS
, ListEntry
)
153 if (Current
->Process
== Win32Process
)
155 /* Already registered for this process */
160 /* Not registered yet */
161 Current
= ExAllocateFromPagedLookasideList(pgProcessLookasideList
);
166 InsertHeadList(&CurIcon
->ProcessList
, &Current
->ListEntry
);
167 Current
->Process
= Win32Process
;
175 IntFindExistingCurIconObject(
176 PUNICODE_STRING pustrModule
,
177 PUNICODE_STRING pustrRsrc
,
178 FINDEXISTINGCURICONPARAM
* param
)
180 PCURICON_OBJECT CurIcon
;
182 LIST_FOR_EACH(CurIcon
, &gCurIconList
, CURICON_OBJECT
, ListEntry
)
184 /* See if we are looking for an icon or a cursor */
185 if(CurIcon
->bIcon
!= param
->bIcon
)
187 /* See if module names match */
188 if(RtlCompareUnicodeString(pustrModule
, &CurIcon
->ustrModule
, TRUE
) == 0)
190 /* They do. Now see if this is the same resource */
191 if(IS_INTRESOURCE(CurIcon
->ustrRsrc
.Buffer
) && IS_INTRESOURCE(pustrRsrc
->Buffer
))
193 if(CurIcon
->ustrRsrc
.Buffer
!= pustrRsrc
->Buffer
)
196 else if(IS_INTRESOURCE(CurIcon
->ustrRsrc
.Buffer
) || IS_INTRESOURCE(pustrRsrc
->Buffer
))
198 else if(RtlCompareUnicodeString(pustrRsrc
, &CurIcon
->ustrRsrc
, TRUE
) != 0)
201 if ((param
->cx
== CurIcon
->Size
.cx
) &&(param
->cy
== CurIcon
->Size
.cy
))
203 if (! ReferenceCurIconByProcess(CurIcon
))
217 IntCreateCurIconHandle(DWORD dwNumber
)
219 PCURICON_OBJECT CurIcon
;
220 BOOLEAN bIcon
= dwNumber
== 0;
226 CurIcon
= UserCreateObject(gHandleTable
, NULL
, NULL
, &hCurIcon
, otCursorIcon
, FIELD_OFFSET(CURICON_OBJECT
, aFrame
[dwNumber
]));
230 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
234 CurIcon
->Self
= hCurIcon
;
235 CurIcon
->bIcon
= bIcon
;
236 InitializeListHead(&CurIcon
->ProcessList
);
238 if (! ReferenceCurIconByProcess(CurIcon
))
240 ERR("Failed to add process\n");
241 UserDeleteObject(hCurIcon
, otCursorIcon
);
242 UserDereferenceObject(CurIcon
);
246 InsertHeadList(&gCurIconList
, &CurIcon
->ListEntry
);
252 IntDestroyCurIconObject(PCURICON_OBJECT CurIcon
, PPROCESSINFO ppi
, BOOLEAN bForce
)
254 PSYSTEM_CURSORINFO CurInfo
;
255 HBITMAP bmpMask
, bmpColor
, bmpAlpha
;
256 BOOLEAN Ret
, bListEmpty
, bFound
= FALSE
;
257 PCURICON_PROCESS Current
= NULL
;
259 /* For handles created without any data (error handling) */
260 if(IsListEmpty(&CurIcon
->ProcessList
))
263 /* Now find this process in the list of processes referencing this object and
264 remove it from that list */
265 LIST_FOR_EACH(Current
, &CurIcon
->ProcessList
, CURICON_PROCESS
, ListEntry
)
267 if (Current
->Process
== ppi
)
270 bListEmpty
= RemoveEntryList(&Current
->ListEntry
);
277 /* This object doesn't belong to this process */
278 EngSetLastError(ERROR_INVALID_HANDLE
);
279 /* Caller expects us to dereference! */
280 UserDereferenceObject(CurIcon
);
284 /* We found our process, but we're told to not destroy it in case it is shared */
285 if((CurIcon
->ustrModule
.Buffer
!= NULL
) && !bForce
)
287 /* Tests show this is a valid call */
288 UserDereferenceObject(CurIcon
);
292 ExFreeToPagedLookasideList(pgProcessLookasideList
, Current
);
294 /* If there are still processes referencing this object we can't destroy it yet */
297 if(CurIcon
->head
.ppi
== ppi
)
299 /* Set the first process of the list as owner */
300 Current
= CONTAINING_RECORD(CurIcon
->ProcessList
.Flink
, CURICON_PROCESS
, ListEntry
);
301 UserSetObjectOwner(CurIcon
, otCursorIcon
, Current
->Process
);
303 UserDereferenceObject(CurIcon
);
308 /* Remove it from the list */
309 RemoveEntryList(&CurIcon
->ListEntry
);
311 CurInfo
= IntGetSysCursorInfo();
313 if (CurInfo
->CurrentCursorObject
== CurIcon
)
315 /* Hide the cursor if we're destroying the current cursor */
316 UserSetCursor(NULL
, TRUE
);
319 bmpMask
= CurIcon
->aFrame
[0].hbmMask
;
320 bmpColor
= CurIcon
->aFrame
[0].hbmColor
;
321 bmpAlpha
= CurIcon
->aFrame
[0].hbmAlpha
;
326 GreSetObjectOwner(bmpMask
, GDI_OBJ_HMGR_POWNED
);
327 GreDeleteObject(bmpMask
);
328 CurIcon
->aFrame
[0].hbmMask
= NULL
;
332 GreSetObjectOwner(bmpColor
, GDI_OBJ_HMGR_POWNED
);
333 GreDeleteObject(bmpColor
);
334 CurIcon
->aFrame
[0].hbmColor
= NULL
;
338 GreSetObjectOwner(bmpAlpha
, GDI_OBJ_HMGR_POWNED
);
339 GreDeleteObject(bmpAlpha
);
340 CurIcon
->aFrame
[0].hbmAlpha
= NULL
;
343 if(!IS_INTRESOURCE(CurIcon
->ustrRsrc
.Buffer
))
344 ExFreePoolWithTag(CurIcon
->ustrRsrc
.Buffer
, TAG_STRING
);
345 if(CurIcon
->ustrModule
.Buffer
)
346 ReleaseCapturedUnicodeString(&CurIcon
->ustrModule
, UserMode
);
348 /* We were given a pointer, no need to keep the reference anylonger! */
349 UserDereferenceObject(CurIcon
);
350 Ret
= UserDeleteObject(CurIcon
->Self
, otCursorIcon
);
356 IntCleanupCurIcons(struct _EPROCESS
*Process
, PPROCESSINFO Win32Process
)
358 PCURICON_OBJECT CurIcon
, tmp
;
360 /* Run through the list of icon objects */
361 LIST_FOR_EACH_SAFE(CurIcon
, tmp
, &gCurIconList
, CURICON_OBJECT
, ListEntry
)
363 UserReferenceObject(CurIcon
);
364 IntDestroyCurIconObject(CurIcon
, Win32Process
, TRUE
);
375 _In_ HANDLE hCurIcon
,
376 _Out_opt_ PICONINFO IconInfo
,
377 _Out_opt_ PUNICODE_STRING lpModule
, // Optional
378 _Out_opt_ PUNICODE_STRING lpResName
, // Optional
379 _Out_opt_ LPDWORD pbpp
, // Optional
383 PCURICON_OBJECT CurIcon
;
384 NTSTATUS Status
= STATUS_SUCCESS
;
388 TRACE("Enter NtUserGetIconInfo\n");
390 /* Check if something was actually asked */
391 if (!IconInfo
&& !lpModule
&& !lpResName
)
393 WARN("Nothing to fill.\n");
394 EngSetLastError(ERROR_INVALID_PARAMETER
);
398 UserEnterExclusive();
400 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
402 WARN("UserGetIconObject(0x%08x) Failed.\n", hCurIcon
);
407 /* Give back the icon information */
411 ii
.fIcon
= CurIcon
->bIcon
;
412 ii
.xHotspot
= CurIcon
->ptlHotspot
.x
;
413 ii
.yHotspot
= CurIcon
->ptlHotspot
.y
;
416 ii
.hbmMask
= BITMAP_CopyBitmap(CurIcon
->aFrame
[0].hbmMask
);
417 GreSetObjectOwner(ii
.hbmMask
, GDI_OBJ_HMGR_POWNED
);
418 ii
.hbmColor
= BITMAP_CopyBitmap(CurIcon
->aFrame
[0].hbmColor
);
419 GreSetObjectOwner(ii
.hbmColor
, GDI_OBJ_HMGR_POWNED
);
425 psurfBmp
= SURFACE_ShareLockSurface(CurIcon
->aFrame
[0].hbmColor
);
428 colorBpp
= BitsPerFormat(psurfBmp
->SurfObj
.iBitmapFormat
);
429 SURFACE_ShareUnlockSurface(psurfBmp
);
436 ProbeForWrite(IconInfo
, sizeof(ICONINFO
), 1);
437 RtlCopyMemory(IconInfo
, &ii
, sizeof(ICONINFO
));
441 ProbeForWrite(pbpp
, sizeof(DWORD
), 1);
445 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
447 Status
= _SEH2_GetExceptionCode();
452 if (!NT_SUCCESS(Status
))
454 WARN("Status: 0x%08x.\n", Status
);
455 SetLastNtError(Status
);
459 /* Give back the module name */
462 if(!CurIcon
->ustrModule
.Buffer
)
464 EngSetLastError(ERROR_INVALID_HANDLE
);
467 /* Copy what we can */
470 ProbeForWrite(lpModule
, sizeof(UNICODE_STRING
), 1);
471 ProbeForWrite(lpModule
->Buffer
, lpModule
->MaximumLength
, 1);
472 lpModule
->Length
= min(lpModule
->MaximumLength
, CurIcon
->ustrModule
.Length
);
473 RtlCopyMemory(lpModule
->Buffer
, CurIcon
->ustrModule
.Buffer
, lpModule
->Length
);
475 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
477 Status
= _SEH2_GetExceptionCode();
482 if (!NT_SUCCESS(Status
))
484 SetLastNtError(Status
);
490 if(!CurIcon
->ustrRsrc
.Buffer
)
492 EngSetLastError(ERROR_INVALID_HANDLE
);
498 ProbeForWrite(lpResName
, sizeof(UNICODE_STRING
), 1);
499 if(IS_INTRESOURCE(CurIcon
->ustrRsrc
.Buffer
))
501 lpResName
->Buffer
= CurIcon
->ustrRsrc
.Buffer
;
502 lpResName
->Length
= 0;
506 lpResName
->Length
= min(lpResName
->MaximumLength
, CurIcon
->ustrRsrc
.Length
);
507 RtlCopyMemory(lpResName
->Buffer
, CurIcon
->ustrRsrc
.Buffer
, lpResName
->Length
);
510 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
512 Status
= _SEH2_GetExceptionCode();
517 if (!NT_SUCCESS(Status
))
519 SetLastNtError(Status
);
526 UserDereferenceObject(CurIcon
);
528 TRACE("Leave NtUserGetIconInfo, ret=%i\n", Ret
);
543 PLONG plcx
, // &size.cx
544 PLONG plcy
) // &size.cy
546 PCURICON_OBJECT CurIcon
;
547 NTSTATUS Status
= STATUS_SUCCESS
;
550 TRACE("Enter NtUserGetIconSize\n");
551 UserEnterExclusive();
553 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
560 ProbeForWrite(plcx
, sizeof(LONG
), 1);
561 RtlCopyMemory(plcx
, &CurIcon
->Size
.cx
, sizeof(LONG
));
562 ProbeForWrite(plcy
, sizeof(LONG
), 1);
563 RtlCopyMemory(plcy
, &CurIcon
->Size
.cy
, sizeof(LONG
));
565 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
567 Status
= _SEH2_GetExceptionCode();
571 if (NT_SUCCESS(Status
))
574 SetLastNtError(Status
); // Maybe not, test this
576 UserDereferenceObject(CurIcon
);
579 TRACE("Leave NtUserGetIconSize, ret=%i\n", bRet
);
594 PSYSTEM_CURSORINFO CurInfo
;
595 NTSTATUS Status
= STATUS_SUCCESS
;
596 PCURICON_OBJECT CurIcon
;
598 DECLARE_RETURN(BOOL
);
600 TRACE("Enter NtUserGetCursorInfo\n");
601 UserEnterExclusive();
603 CurInfo
= IntGetSysCursorInfo();
604 CurIcon
= (PCURICON_OBJECT
)CurInfo
->CurrentCursorObject
;
606 SafeCi
.cbSize
= sizeof(CURSORINFO
);
607 SafeCi
.flags
= ((CurIcon
&& CurInfo
->ShowingCursor
>= 0) ? CURSOR_SHOWING
: 0);
608 SafeCi
.hCursor
= (CurIcon
? (HCURSOR
)CurIcon
->Self
: (HCURSOR
)0);
610 SafeCi
.ptScreenPos
= gpsi
->ptCursor
;
614 if (pci
->cbSize
== sizeof(CURSORINFO
))
616 ProbeForWrite(pci
, sizeof(CURSORINFO
), 1);
617 RtlCopyMemory(pci
, &SafeCi
, sizeof(CURSORINFO
));
622 EngSetLastError(ERROR_INVALID_PARAMETER
);
625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
627 Status
= _SEH2_GetExceptionCode();
630 if (!NT_SUCCESS(Status
))
632 SetLastNtError(Status
);
638 TRACE("Leave NtUserGetCursorInfo, ret=%i\n",_ret_
);
648 /* FIXME: Check if process has WINSTA_WRITEATTRIBUTES */
649 PSYSTEM_CURSORINFO CurInfo
;
650 PWND DesktopWindow
= NULL
;
652 CurInfo
= IntGetSysCursorInfo();
654 DesktopWindow
= UserGetDesktopWindow();
656 if (prcl
!= NULL
&& DesktopWindow
!= NULL
)
658 if (prcl
->right
< prcl
->left
|| prcl
->bottom
< prcl
->top
)
660 EngSetLastError(ERROR_INVALID_PARAMETER
);
664 CurInfo
->bClipped
= TRUE
;
666 /* Set nw cliping region. Note: we can't use RECTL_bIntersectRect because
667 it sets rect to 0 0 0 0 when it's empty. For more info see monitor winetest */
668 CurInfo
->rcClip
.left
= max(prcl
->left
, DesktopWindow
->rcWindow
.left
);
669 CurInfo
->rcClip
.right
= min(prcl
->right
, DesktopWindow
->rcWindow
.right
);
670 if (CurInfo
->rcClip
.right
< CurInfo
->rcClip
.left
)
671 CurInfo
->rcClip
.right
= CurInfo
->rcClip
.left
;
673 CurInfo
->rcClip
.top
= max(prcl
->top
, DesktopWindow
->rcWindow
.top
);
674 CurInfo
->rcClip
.bottom
= min(prcl
->bottom
, DesktopWindow
->rcWindow
.bottom
);
675 if (CurInfo
->rcClip
.bottom
< CurInfo
->rcClip
.top
)
676 CurInfo
->rcClip
.bottom
= CurInfo
->rcClip
.top
;
678 /* Make sure cursor is in clipping region */
679 UserSetCursorPos(gpsi
->ptCursor
.x
, gpsi
->ptCursor
.y
, 0, 0, FALSE
);
683 CurInfo
->bClipped
= FALSE
;
704 /* Probe and copy rect */
705 ProbeForRead(prcl
, sizeof(RECTL
), 1);
708 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
710 EngSetLastError(ERROR_INVALID_PARAMETER
);
711 _SEH2_YIELD(return FALSE
;)
718 UserEnterExclusive();
720 /* Call the internal function */
721 bResult
= UserClipCursor(prcl
);
735 _In_ HANDLE hCurIcon
,
738 PCURICON_OBJECT CurIcon
;
740 DECLARE_RETURN(BOOL
);
742 TRACE("Enter NtUserDestroyCursorIcon\n");
743 UserEnterExclusive();
745 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
750 ret
= IntDestroyCurIconObject(CurIcon
, PsGetCurrentProcessWin32Process(), bForce
);
751 /* Note: IntDestroyCurIconObject will remove our reference for us! */
756 TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_
);
767 NtUserFindExistingCursorIcon(
768 _In_ PUNICODE_STRING pustrModule
,
769 _In_ PUNICODE_STRING pustrRsrc
,
770 _In_ FINDEXISTINGCURICONPARAM
* param
)
772 PCURICON_OBJECT CurIcon
;
774 UNICODE_STRING ustrModuleSafe
, ustrRsrcSafe
;
775 FINDEXISTINGCURICONPARAM paramSafe
;
778 TRACE("Enter NtUserFindExistingCursorIcon\n");
780 /* Capture resource name (it can be an INTRESOURCE == ATOM) */
781 Status
= ProbeAndCaptureUnicodeStringOrAtom(&ustrRsrcSafe
, pustrRsrc
);
782 if(!NT_SUCCESS(Status
))
784 Status
= ProbeAndCaptureUnicodeString(&ustrModuleSafe
, UserMode
, pustrModule
);
785 if(!NT_SUCCESS(Status
))
790 ProbeForRead(param
, sizeof(*param
), 1);
793 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
795 Status
= _SEH2_GetExceptionCode();
799 UserEnterExclusive();
800 CurIcon
= IntFindExistingCurIconObject(&ustrModuleSafe
, &ustrRsrcSafe
, ¶mSafe
);
806 if(!IS_INTRESOURCE(ustrRsrcSafe
.Buffer
))
807 ExFreePoolWithTag(ustrRsrcSafe
.Buffer
, TAG_STRING
);
808 ReleaseCapturedUnicodeString(&ustrModuleSafe
, UserMode
);
822 /* FIXME: Check if process has WINSTA_READATTRIBUTES */
823 PSYSTEM_CURSORINFO CurInfo
;
826 DECLARE_RETURN(BOOL
);
828 TRACE("Enter NtUserGetClipCursor\n");
829 UserEnterExclusive();
834 CurInfo
= IntGetSysCursorInfo();
835 if (CurInfo
->bClipped
)
837 Rect
= CurInfo
->rcClip
;
843 Rect
.right
= UserGetSystemMetrics(SM_CXSCREEN
);
844 Rect
.bottom
= UserGetSystemMetrics(SM_CYSCREEN
);
847 Status
= MmCopyToCaller(lpRect
, &Rect
, sizeof(RECT
));
848 if (!NT_SUCCESS(Status
))
850 SetLastNtError(Status
);
857 TRACE("Leave NtUserGetClipCursor, ret=%i\n",_ret_
);
871 PCURICON_OBJECT pcurOld
, pcurNew
;
872 HCURSOR hOldCursor
= NULL
;
874 TRACE("Enter NtUserSetCursor\n");
875 UserEnterExclusive();
879 pcurNew
= UserGetCurIconObject(hCursor
);
882 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE
);
891 pcurOld
= UserSetCursor(pcurNew
, FALSE
);
894 hOldCursor
= (HCURSOR
)pcurOld
->Self
;
895 UserDereferenceObject(pcurOld
);
909 NtUserSetCursorContents(
911 PICONINFO UnsafeIconInfo
)
913 PCURICON_OBJECT CurIcon
;
918 DECLARE_RETURN(BOOL
);
920 TRACE("Enter NtUserSetCursorContents\n");
921 UserEnterExclusive();
923 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
929 Status
= MmCopyFromCaller(&IconInfo
, UnsafeIconInfo
, sizeof(ICONINFO
));
930 if (!NT_SUCCESS(Status
))
932 SetLastNtError(Status
);
937 /* Check if we get valid information */
938 if(IconInfo
.fIcon
!= CurInfo
->bIcon
)
940 EngSetLastError(ERROR_INVALID_PARAMETER
);
945 /* Delete old bitmaps */
946 if (CurIcon
->aFrame
[0].hbmColor
)
947 GreDeleteObject(CurIcon
->aFrame
[0].hbmColor
);
948 if (CurIcon
->aFrame
[0].hbmMask
)
949 GreDeleteObject(CurIcon
->aFrame
[0].hbmMask
);
950 if(CurIcon
->aFrame
[0].hbmAlpha
)
951 GreDeleteObject(CurIcon
->aFrame
[0].hbmAlpha
);
954 CurIcon
->bIcon
= IconInfo
.fIcon
;
955 CurIcon
->ptlHotspot
.x
= IconInfo
.xHotspot
;
956 CurIcon
->ptlHotspot
.y
= IconInfo
.yHotspot
;
957 CurIcon
->aFrame
[0].hbmMask
= IconInfo
.hbmMask
;
958 CurIcon
->aFrame
[0].hbmColor
= IconInfo
.hbmColor
;
959 CurIcon
->aFrame
[0].hbmAlpha
= NULL
;
961 if (IconInfo
.hbmColor
)
963 BOOLEAN bAlpha
= FALSE
;
964 psurfBmp
= SURFACE_ShareLockSurface(IconInfo
.hbmColor
);
967 CurIcon
->Size
.cx
= psurfBmp
->SurfObj
.sizlBitmap
.cx
;
968 CurIcon
->Size
.cy
= psurfBmp
->SurfObj
.sizlBitmap
.cy
;
970 /* 32bpp bitmap is likely to have an alpha channel */
971 if(psurfBmp
->SurfObj
.iBitmapFormat
== BMF_32BPP
)
973 PFN_DIB_GetPixel fn_GetPixel
= DibFunctionsForBitmapFormat
[BMF_32BPP
].DIB_GetPixel
;
976 fn_GetPixel
= DibFunctionsForBitmapFormat
[BMF_32BPP
].DIB_GetPixel
;
977 for (i
= 0; i
< psurfBmp
->SurfObj
.sizlBitmap
.cx
; i
++)
979 for (j
= 0; j
< psurfBmp
->SurfObj
.sizlBitmap
.cy
; j
++)
981 bAlpha
= ((BYTE
)(fn_GetPixel(&psurfBmp
->SurfObj
, i
, j
) >> 24)) != 0;
989 /* We're done with this one */
990 SURFACE_ShareUnlockSurface(psurfBmp
);
991 GreSetObjectOwner(IconInfo
.hbmColor
, GDI_OBJ_HMGR_PUBLIC
);
998 /* Copy the bitmap */
999 CurIcon
->aFrame
[0].hbmAlpha
= BITMAP_CopyBitmap(IconInfo
.hbmColor
);
1000 if(!CurIcon
->aFrame
[0].hbmAlpha
)
1002 ERR("BITMAP_CopyBitmap failed!");
1006 psurfBmp
= SURFACE_ShareLockSurface(CurIcon
->aFrame
[0].hbmAlpha
);
1009 ERR("SURFACE_LockSurface failed!\n");
1013 /* Premultiply with the alpha channel value */
1014 for (i
= 0; i
< psurfBmp
->SurfObj
.sizlBitmap
.cy
; i
++)
1016 ptr
= (PBYTE
)psurfBmp
->SurfObj
.pvScan0
+ i
*psurfBmp
->SurfObj
.lDelta
;
1017 for (j
= 0; j
< psurfBmp
->SurfObj
.sizlBitmap
.cx
; j
++)
1020 ptr
[0] = (ptr
[0] * Alpha
) / 0xff;
1021 ptr
[1] = (ptr
[1] * Alpha
) / 0xff;
1022 ptr
[2] = (ptr
[2] * Alpha
) / 0xff;
1026 SURFACE_ShareUnlockSurface(psurfBmp
);
1027 GreSetObjectOwner(CurIcon
->aFrame
[0].hbmAlpha
, GDI_OBJ_HMGR_PUBLIC
);
1032 psurfBmp
= SURFACE_ShareLockSurface(IconInfo
.hbmMask
);
1036 CurIcon
->Size
.cx
= psurfBmp
->SurfObj
.sizlBitmap
.cx
;
1037 CurIcon
->Size
.cy
= psurfBmp
->SurfObj
.sizlBitmap
.cy
/ 2;
1039 SURFACE_ShareUnlockSurface(psurfBmp
);
1041 GreSetObjectOwner(IconInfo
.hbmMask
, GDI_OBJ_HMGR_PUBLIC
);
1049 UserDereferenceObject(CurIcon
);
1054 TRACE("Leave NtUserSetCursorContents, ret=%i\n",_ret_
);
1065 NtUserSetCursorIconData(
1066 _In_ HCURSOR Handle
,
1067 _In_opt_ PUNICODE_STRING pustrModule
,
1068 _In_opt_ PUNICODE_STRING pustrRsrc
,
1069 _In_ PICONINFO pIconInfo
)
1071 PCURICON_OBJECT CurIcon
;
1073 NTSTATUS Status
= STATUS_SUCCESS
;
1077 TRACE("Enter NtUserSetCursorIconData\n");
1079 /* If a module name is provided, we need a resource name, and vice versa */
1080 if((pustrModule
&& !pustrRsrc
) || (!pustrModule
&& pustrRsrc
))
1083 UserEnterExclusive();
1085 if (!(CurIcon
= UserGetCurIconObject(Handle
)))
1088 EngSetLastError(ERROR_INVALID_HANDLE
);
1094 ProbeForRead(pIconInfo
, sizeof(ICONINFO
), 1);
1097 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1099 Status
= _SEH2_GetExceptionCode();
1103 if (!NT_SUCCESS(Status
))
1105 SetLastNtError(Status
);
1109 /* This is probably not what windows does, but consistency checks can't hurt */
1110 if(CurIcon
->bIcon
!= ii
.fIcon
)
1112 EngSetLastError(ERROR_INVALID_PARAMETER
);
1115 CurIcon
->ptlHotspot
.x
= ii
.xHotspot
;
1116 CurIcon
->ptlHotspot
.y
= ii
.yHotspot
;
1120 EngSetLastError(ERROR_INVALID_PARAMETER
);
1124 CurIcon
->aFrame
[0].hbmMask
= BITMAP_CopyBitmap(ii
.hbmMask
);
1125 if(!CurIcon
->aFrame
[0].hbmMask
)
1130 CurIcon
->aFrame
[0].hbmColor
= BITMAP_CopyBitmap(ii
.hbmColor
);
1131 if(!CurIcon
->aFrame
[0].hbmColor
)
1135 if (CurIcon
->aFrame
[0].hbmColor
)
1137 psurfBmp
= SURFACE_ShareLockSurface(CurIcon
->aFrame
[0].hbmColor
);
1141 CurIcon
->Size
.cx
= psurfBmp
->SurfObj
.sizlBitmap
.cx
;
1142 CurIcon
->Size
.cy
= psurfBmp
->SurfObj
.sizlBitmap
.cy
;
1143 SURFACE_ShareUnlockSurface(psurfBmp
);
1144 GreSetObjectOwner(CurIcon
->aFrame
[0].hbmColor
, GDI_OBJ_HMGR_PUBLIC
);
1148 psurfBmp
= SURFACE_ShareLockSurface(CurIcon
->aFrame
[0].hbmMask
);
1152 CurIcon
->Size
.cx
= psurfBmp
->SurfObj
.sizlBitmap
.cx
;
1153 CurIcon
->Size
.cy
= psurfBmp
->SurfObj
.sizlBitmap
.cy
/2;
1154 SURFACE_ShareUnlockSurface(psurfBmp
);
1156 GreSetObjectOwner(CurIcon
->aFrame
[0].hbmMask
, GDI_OBJ_HMGR_PUBLIC
);
1160 /* We use this convenient function, because INTRESOURCEs and ATOMs are the same */
1161 Status
= ProbeAndCaptureUnicodeStringOrAtom(&CurIcon
->ustrRsrc
, pustrRsrc
);
1162 if(!NT_SUCCESS(Status
))
1164 Status
= ProbeAndCaptureUnicodeString(&CurIcon
->ustrModule
, UserMode
, pustrModule
);
1165 if(!NT_SUCCESS(Status
))
1172 UserDereferenceObject(CurIcon
);
1175 if (CurIcon
->aFrame
[0].hbmMask
)
1177 GreSetObjectOwner(CurIcon
->aFrame
[0].hbmMask
, GDI_OBJ_HMGR_POWNED
);
1178 GreDeleteObject(CurIcon
->aFrame
[0].hbmMask
);
1179 CurIcon
->aFrame
[0].hbmMask
= NULL
;
1181 if (CurIcon
->aFrame
[0].hbmColor
)
1183 GreSetObjectOwner(CurIcon
->aFrame
[0].hbmColor
, GDI_OBJ_HMGR_POWNED
);
1184 GreDeleteObject(CurIcon
->aFrame
[0].hbmColor
);
1185 CurIcon
->aFrame
[0].hbmColor
= NULL
;
1187 if(!IS_INTRESOURCE(CurIcon
->ustrRsrc
.Buffer
))
1188 ExFreePoolWithTag(CurIcon
->ustrRsrc
.Buffer
, TAG_STRING
);
1189 if(CurIcon
->ustrModule
.Buffer
)
1190 ReleaseCapturedUnicodeString(&CurIcon
->ustrModule
, UserMode
);
1193 TRACE("Leave NtUserSetCursorIconData, ret=%i\n",Ret
);
1199 /* Mostly inspired from wine code.
1200 * We use low level functions because:
1201 * - at this point, the icon bitmap could have a different bit depth than the DC,
1202 * making it thus impossible to use NtCreateCompatibleDC and selecting the bitmap.
1203 * This happens after a mode setting change.
1204 * - it avoids massive GDI objects locking when only the destination surface needs it.
1205 * - It makes (small) performance gains.
1212 PCURICON_OBJECT pIcon
,
1216 HBRUSH hbrFlickerFreeDraw
,
1219 PSURFACE psurfDest
, psurfMask
, psurfColor
; //, psurfOffScreen = NULL;
1222 HBITMAP hbmMask
, hbmColor
, hbmAlpha
;
1224 RECTL rcDest
, rcSrc
;
1225 CLIPOBJ
* pdcClipObj
= NULL
;
1229 if((diFlags
& DI_NORMAL
) == 0)
1231 ERR("DrawIconEx called without mask or color bitmap to draw.\n");
1235 hbmMask
= pIcon
->aFrame
[0].hbmMask
;
1236 hbmColor
= pIcon
->aFrame
[0].hbmColor
;
1237 hbmAlpha
= pIcon
->aFrame
[0].hbmAlpha
;
1240 ERR("NtUserDrawIconEx: istepIfAniCur is not supported!\n");
1244 * Shared locks are enough, we are only reading those bitmaps
1246 psurfMask
= SURFACE_ShareLockSurface(hbmMask
);
1247 if(psurfMask
== NULL
)
1249 ERR("Unable to lock the mask surface.\n");
1253 /* Color bitmap is not mandatory */
1254 if(hbmColor
== NULL
)
1256 /* But then the mask bitmap must have the information in it's bottom half */
1257 ASSERT(psurfMask
->SurfObj
.sizlBitmap
.cy
== 2*pIcon
->Size
.cy
);
1260 else if ((psurfColor
= SURFACE_ShareLockSurface(hbmColor
)) == NULL
)
1262 ERR("Unable to lock the color bitmap.\n");
1263 SURFACE_ShareUnlockSurface(psurfMask
);
1267 pdc
= DC_LockDc(hDc
);
1270 ERR("Could not lock the destination DC.\n");
1271 SURFACE_ShareUnlockSurface(psurfMask
);
1272 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1275 /* Calculate destination rectangle */
1276 RECTL_vSetRect(&rcDest
, xLeft
, yTop
, xLeft
+ cxWidth
, yTop
+ cyHeight
);
1277 IntLPtoDP(pdc
, (LPPOINT
)&rcDest
, 2);
1278 RECTL_vOffsetRect(&rcDest
, pdc
->ptlDCOrig
.x
, pdc
->ptlDCOrig
.y
);
1280 /* Prepare the underlying surface */
1281 DC_vPrepareDCsForBlit(pdc
, rcDest
, NULL
, rcDest
);
1283 /* We now have our destination surface and rectangle */
1284 psurfDest
= pdc
->dclevel
.pSurface
;
1286 if(psurfDest
== NULL
)
1289 DC_vFinishBlit(pdc
, NULL
);
1291 SURFACE_ShareUnlockSurface(psurfMask
);
1292 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1296 /* Set source rect */
1297 RECTL_vSetRect(&rcSrc
, 0, 0, pIcon
->Size
.cx
, pIcon
->Size
.cy
);
1299 /* Fix width parameter, if needed */
1302 if(diFlags
& DI_DEFAULTSIZE
)
1303 cxWidth
= pIcon
->bIcon
?
1304 UserGetSystemMetrics(SM_CXICON
) : UserGetSystemMetrics(SM_CXCURSOR
);
1306 cxWidth
= pIcon
->Size
.cx
;
1309 /* Fix height parameter, if needed */
1312 if(diFlags
& DI_DEFAULTSIZE
)
1313 cyHeight
= pIcon
->bIcon
?
1314 UserGetSystemMetrics(SM_CYICON
) : UserGetSystemMetrics(SM_CYCURSOR
);
1316 cyHeight
= pIcon
->Size
.cy
;
1319 /* Should we render off-screen? */
1320 bOffScreen
= hbrFlickerFreeDraw
&&
1321 (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw
) == GDI_OBJECT_TYPE_BRUSH
);
1325 /* Yes: Allocate and paint the offscreen surface */
1327 PBRUSH pbrush
= BRUSH_ShareLockBrush(hbrFlickerFreeDraw
);
1329 TRACE("Performing off-screen rendering.\n");
1333 ERR("Failed to get brush object.\n");
1337 #if 0 //We lock the hdc surface during the whole function it makes no sense to use an offscreen surface for "flicker free" drawing
1338 psurfOffScreen
= SURFACE_AllocSurface(STYPE_BITMAP
,
1339 cxWidth
, cyHeight
, psurfDest
->SurfObj
.iBitmapFormat
,
1343 ERR("Failed to allocate the off-screen surface.\n");
1344 BRUSH_ShareUnlockBrush(pbrush
);
1348 /* Paint the brush */
1349 EBRUSHOBJ_vInit(&eboFill
, pbrush
, psurfOffScreen
, 0x00FFFFFF, 0, NULL
);
1350 RECTL_vSetRect(&rcDest
, 0, 0, cxWidth
, cyHeight
);
1352 Ret
= IntEngBitBlt(&psurfOffScreen
->SurfObj
,
1360 &eboFill
.BrushObject
,
1364 /* Clean up everything */
1365 EBRUSHOBJ_vCleanup(&eboFill
);
1366 BRUSH_ShareUnlockBrush(pbrush
);
1370 ERR("Failed to paint the off-screen surface.\n");
1374 /* We now have our destination surface */
1375 psurfDest
= psurfOffScreen
;
1377 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1378 /* Paint the brush */
1379 EBRUSHOBJ_vInit(&eboFill
, pbrush
, psurfDest
, 0x00FFFFFF, 0, NULL
);
1381 Ret
= IntEngBitBlt(&psurfDest
->SurfObj
,
1389 &eboFill
.BrushObject
,
1393 /* Clean up everything */
1394 EBRUSHOBJ_vCleanup(&eboFill
);
1395 BRUSH_ShareUnlockBrush(pbrush
);
1399 ERR("Failed to paint the off-screen surface.\n");
1406 /* We directly draw to the DC */
1407 TRACE("Performing on screen rendering.\n");
1408 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1409 // psurfOffScreen = NULL;
1412 /* Now do the rendering */
1413 if(hbmAlpha
&& (diFlags
& DI_IMAGE
))
1415 BLENDOBJ blendobj
= { {AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
} };
1416 PSURFACE psurf
= NULL
;
1418 psurf
= SURFACE_ShareLockSurface(hbmAlpha
);
1421 ERR("SURFACE_LockSurface failed!\n");
1425 /* Initialize color translation object */
1426 EXLATEOBJ_vInitialize(&exlo
, psurf
->ppal
, psurfDest
->ppal
, 0xFFFFFFFF, 0xFFFFFFFF, 0);
1429 Ret
= IntEngAlphaBlend(&psurfDest
->SurfObj
,
1437 EXLATEOBJ_vCleanup(&exlo
);
1438 SURFACE_ShareUnlockSurface(psurf
);
1440 ERR("NtGdiAlphaBlend failed!\n");
1443 if (diFlags
& DI_MASK
)
1445 DWORD rop4
= (diFlags
& DI_IMAGE
) ? ROP4_SRCAND
: ROP4_SRCCOPY
;
1447 EXLATEOBJ_vInitSrcMonoXlate(&exlo
, psurfDest
->ppal
, 0x00FFFFFF, 0);
1449 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1450 &psurfMask
->SurfObj
,
1462 EXLATEOBJ_vCleanup(&exlo
);
1466 ERR("Failed to mask the bitmap data.\n");
1471 if(diFlags
& DI_IMAGE
)
1475 DWORD rop4
= (diFlags
& DI_MASK
) ? ROP4_SRCINVERT
: ROP4_SRCCOPY
;
1477 EXLATEOBJ_vInitialize(&exlo
, psurfColor
->ppal
, psurfDest
->ppal
, 0x00FFFFFF, 0x00FFFFFF, 0);
1479 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1480 &psurfColor
->SurfObj
,
1492 EXLATEOBJ_vCleanup(&exlo
);
1496 ERR("Failed to render the icon bitmap.\n");
1502 /* Mask bitmap holds the information in its bottom half */
1503 DWORD rop4
= (diFlags
& DI_MASK
) ? ROP4_SRCINVERT
: ROP4_SRCCOPY
;
1504 RECTL_vOffsetRect(&rcSrc
, 0, pIcon
->Size
.cy
);
1506 EXLATEOBJ_vInitSrcMonoXlate(&exlo
, psurfDest
->ppal
, 0x00FFFFFF, 0);
1508 Ret
= IntEngStretchBlt(&psurfDest
->SurfObj
,
1509 &psurfMask
->SurfObj
,
1521 EXLATEOBJ_vCleanup(&exlo
);
1525 ERR("Failed to render the icon bitmap.\n");
1533 /* We're done. Was it a double buffered draw ? */
1536 /* Yes. Draw it back to our DC */
1537 POINTL ptSrc
= {0, 0};
1539 /* Calculate destination rectangle */
1540 RECTL_vSetRect(&rcDest
, xLeft
, yTop
, xLeft
+ cxWidth
, yTop
+ cyHeight
);
1541 IntLPtoDP(pdc
, (LPPOINT
)&rcDest
, 2);
1542 RECTL_vOffsetRect(&rcDest
, pdc
->ptlDCOrig
.x
, pdc
->ptlDCOrig
.y
);
1544 /* Get the clip object */
1545 pdcClipObj
= pdc
->rosdc
.CombinedClip
;
1547 /* We now have our destination surface and rectangle */
1548 psurfDest
= pdc
->dclevel
.pSurface
;
1550 /* Color translation */
1551 EXLATEOBJ_vInitialize(&exlo
, psurfOffScreen
->ppal
, psurfDest
->ppal
, 0x00FFFFFF, 0x00FFFFFF, 0);
1554 Ret
= IntEngBitBlt(&psurfDest
->SurfObj
,
1555 &psurfOffScreen
->SurfObj
,
1566 EXLATEOBJ_vCleanup(&exlo
);
1572 DC_vFinishBlit(pdc
, NULL
);
1577 /* Delete off screen rendering surface */
1579 GDIOBJ_vDeleteObject(&psurfOffScreen
->BaseObject
);
1582 /* Unlock other surfaces */
1583 SURFACE_ShareUnlockSurface(psurfMask
);
1584 if(psurfColor
) SURFACE_ShareUnlockSurface(psurfColor
);
1602 HBRUSH hbrFlickerFreeDraw
,
1604 BOOL bMetaHDC
, // When TRUE, GDI functions need to be handled in User32!
1607 PCURICON_OBJECT pIcon
;
1610 TRACE("Enter NtUserDrawIconEx\n");
1611 UserEnterExclusive();
1613 if (!(pIcon
= UserGetCurIconObject(hIcon
)))
1615 ERR("UserGetCurIconObject(0x%08x) failed!\n", hIcon
);
1620 Ret
= UserDrawIconEx(hdc
,
1630 UserDereferenceObject(pIcon
);