Synchronize up to trunk's revision r57756.
[reactos.git] / win32ss / user / ntuser / cursoricon_new.c
1 /*
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
7 */
8 /*
9 * We handle two types of cursors/icons:
10 * - Private
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
15 * - Shared
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.
25 */
26
27 #include <win32k.h>
28 DBG_DEFAULT_CHANNEL(UserIcon);
29
30 static LIST_ENTRY gCurIconList;
31 static PAGED_LOOKASIDE_LIST *pgProcessLookasideList;
32
33 SYSTEM_CURSORINFO gSysCursorInfo;
34
35 BOOL
36 InitCursorImpl()
37 {
38 pgProcessLookasideList = ExAllocatePool(NonPagedPool, sizeof(PAGED_LOOKASIDE_LIST));
39 if(!pgProcessLookasideList)
40 return FALSE;
41
42 ExInitializePagedLookasideList(pgProcessLookasideList,
43 NULL,
44 NULL,
45 0,
46 sizeof(CURICON_PROCESS),
47 TAG_DIB,
48 128);
49 InitializeListHead(&gCurIconList);
50
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;
59
60 return TRUE;
61 }
62
63 PSYSTEM_CURSORINFO
64 IntGetSysCursorInfo()
65 {
66 return &gSysCursorInfo;
67 }
68
69 FORCEINLINE
70 BOOL
71 is_icon(PCURICON_OBJECT object)
72 {
73 return MAKEINTRESOURCE(object->rt) == RT_ICON;
74 }
75
76 /* This function creates a reference for the object! */
77 PCURICON_OBJECT FASTCALL UserGetCurIconObject(HCURSOR hCurIcon)
78 {
79 PCURICON_OBJECT CurIcon;
80
81 if (!hCurIcon)
82 {
83 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
84 return NULL;
85 }
86
87 CurIcon = (PCURICON_OBJECT)UserReferenceObjectByHandle(hCurIcon, otCursorIcon);
88 if (!CurIcon)
89 {
90 /* We never set ERROR_INVALID_ICON_HANDLE. lets hope noone ever checks for it */
91 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
92 return NULL;
93 }
94
95 ASSERT(CurIcon->head.cLockObj >= 1);
96 return CurIcon;
97 }
98
99 BOOL UserSetCursorPos( INT x, INT y, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook)
100 {
101 PWND DesktopWindow;
102 PSYSTEM_CURSORINFO CurInfo;
103 MSG Msg;
104 RECTL rcClip;
105 POINT pt;
106
107 if(!(DesktopWindow = UserGetDesktopWindow()))
108 {
109 return FALSE;
110 }
111
112 CurInfo = IntGetSysCursorInfo();
113
114 /* Clip cursor position */
115 if (!CurInfo->bClipped)
116 rcClip = DesktopWindow->rcClient;
117 else
118 rcClip = CurInfo->rcClip;
119
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;
124
125 pt.x = x;
126 pt.y = y;
127
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);
132 Msg.pt = pt;
133 co_MsqInsertMouseMessage(&Msg, flags, dwExtraInfo, Hook);
134
135 /* 2. Store the new cursor position */
136 gpsi->ptCursor = pt;
137
138 return TRUE;
139 }
140
141 /*
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
148 * life...
149 */
150 static BOOLEAN FASTCALL
151 ReferenceCurIconByProcess(PCURICON_OBJECT CurIcon)
152 {
153 PPROCESSINFO Win32Process;
154 PCURICON_PROCESS Current;
155
156 Win32Process = PsGetCurrentProcessWin32Process();
157
158 LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
159 {
160 if (Current->Process == Win32Process)
161 {
162 /* Already registered for this process */
163 return TRUE;
164 }
165 }
166
167 /* Not registered yet */
168 Current = ExAllocateFromPagedLookasideList(pgProcessLookasideList);
169 if (NULL == Current)
170 {
171 return FALSE;
172 }
173 InsertHeadList(&CurIcon->ProcessList, &Current->ListEntry);
174 Current->Process = Win32Process;
175
176 return TRUE;
177 }
178
179 static
180 PCURICON_OBJECT
181 FASTCALL
182 IntFindExistingCurIconObject(
183 PUNICODE_STRING pustrModule,
184 PUNICODE_STRING pustrRsrc,
185 FINDEXISTINGCURICONPARAM* param)
186 {
187 PCURICON_OBJECT CurIcon;
188
189 LIST_FOR_EACH(CurIcon, &gCurIconList, CURICON_OBJECT, ListEntry)
190 {
191 /* See if we are looking for an icon or a cursor */
192 if(MAKEINTRESOURCE(CurIcon->rt) != (param->bIcon ? RT_ICON : RT_CURSOR))
193 continue;
194 /* See if module names match */
195 if(RtlCompareUnicodeString(pustrModule, &CurIcon->ustrModule, TRUE) == 0)
196 {
197 /* They do. Now see if this is the same resource */
198 if(IS_INTRESOURCE(CurIcon->strName.Buffer) && IS_INTRESOURCE(pustrRsrc->Buffer))
199 {
200 if(CurIcon->strName.Buffer != pustrRsrc->Buffer)
201 continue;
202 }
203 else if(IS_INTRESOURCE(CurIcon->strName.Buffer) || IS_INTRESOURCE(pustrRsrc->Buffer))
204 continue;
205 else if(RtlCompareUnicodeString(pustrRsrc, &CurIcon->strName, TRUE) != 0)
206 continue;
207
208 if ((param->cx == CurIcon->cx) && (param->cy == CurIcon->cy))
209 {
210 if (! ReferenceCurIconByProcess(CurIcon))
211 {
212 return NULL;
213 }
214
215 return CurIcon;
216 }
217 }
218 }
219
220 return NULL;
221 }
222
223 PCURICON_OBJECT
224 IntCreateCurIconHandle(DWORD dwNumber)
225 {
226 PCURICON_OBJECT CurIcon;
227 HANDLE hCurIcon;
228
229 if(dwNumber == 0)
230 dwNumber = 1;
231
232 CurIcon = UserCreateObject(gHandleTable, NULL, NULL, &hCurIcon, otCursorIcon, sizeof(CURICON_OBJECT));
233
234 if (!CurIcon)
235 {
236 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
237 return FALSE;
238 }
239
240 CurIcon->Self = hCurIcon;
241 InitializeListHead(&CurIcon->ProcessList);
242
243 if (! ReferenceCurIconByProcess(CurIcon))
244 {
245 ERR("Failed to add process\n");
246 UserDereferenceObject(CurIcon);
247 UserDeleteObject(hCurIcon, otCursorIcon);
248 return NULL;
249 }
250
251 InsertHeadList(&gCurIconList, &CurIcon->ListEntry);
252
253 return CurIcon;
254 }
255
256 BOOLEAN FASTCALL
257 IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, PPROCESSINFO ppi, BOOLEAN bForce)
258 {
259 HBITMAP bmpMask, bmpColor, bmpAlpha;
260 BOOLEAN Ret, bListEmpty, bFound = FALSE;
261 PCURICON_PROCESS Current = NULL;
262
263 /* Check if this is the current cursor */
264 if(CurIcon->CURSORF_flags & CURSORF_CURRENT)
265 {
266 UserDereferenceObject(CurIcon);
267 return FALSE;
268 }
269
270 /* For handles created without any data (error handling) */
271 if(IsListEmpty(&CurIcon->ProcessList))
272 goto emptyList;
273
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)
277 {
278 if (Current->Process == ppi)
279 {
280 bFound = TRUE;
281 bListEmpty = RemoveEntryList(&Current->ListEntry);
282 break;
283 }
284 }
285
286 if(!bFound)
287 {
288 /* This object doesn't belong to this process */
289 EngSetLastError(ERROR_INVALID_HANDLE);
290 /* Caller expects us to dereference! */
291 UserDereferenceObject(CurIcon);
292 return FALSE;
293 }
294
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)
297 {
298 /* Tests show this is a valid call */
299 UserDereferenceObject(CurIcon);
300 return TRUE;
301 }
302
303 ExFreeToPagedLookasideList(pgProcessLookasideList, Current);
304
305 /* If there are still processes referencing this object we can't destroy it yet */
306 if (!bListEmpty)
307 {
308 if(CurIcon->head.ppi == ppi)
309 {
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);
313 }
314 UserDereferenceObject(CurIcon);
315 return TRUE;
316 }
317
318 emptyList:
319 /* Remove it from the list */
320 RemoveEntryList(&CurIcon->ListEntry);
321
322 bmpMask = CurIcon->hbmMask;
323 bmpColor = CurIcon->hbmColor;
324 bmpAlpha = CurIcon->hbmAlpha;
325
326 /* Delete bitmaps */
327 if (bmpMask)
328 {
329 GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED);
330 GreDeleteObject(bmpMask);
331 CurIcon->hbmMask = NULL;
332 }
333 if (bmpColor)
334 {
335 GreSetObjectOwner(bmpColor, GDI_OBJ_HMGR_POWNED);
336 GreDeleteObject(bmpColor);
337 CurIcon->hbmColor = NULL;
338 }
339 if (bmpAlpha)
340 {
341 GreSetObjectOwner(bmpAlpha, GDI_OBJ_HMGR_POWNED);
342 GreDeleteObject(bmpAlpha);
343 CurIcon->hbmAlpha = NULL;
344 }
345
346 if(!IS_INTRESOURCE(CurIcon->strName.Buffer))
347 ExFreePoolWithTag(CurIcon->strName.Buffer, TAG_STRING);
348 if(CurIcon->ustrModule.Buffer)
349 ReleaseCapturedUnicodeString(&CurIcon->ustrModule, UserMode);
350
351 /* We were given a pointer, no need to keep the reference anylonger! */
352 UserDereferenceObject(CurIcon);
353 Ret = UserDeleteObject(CurIcon->Self, otCursorIcon);
354
355 return Ret;
356 }
357
358 VOID FASTCALL
359 IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process)
360 {
361 PCURICON_OBJECT CurIcon, tmp;
362
363 /* Run through the list of icon objects */
364 LIST_FOR_EACH_SAFE(CurIcon, tmp, &gCurIconList, CURICON_OBJECT, ListEntry)
365 {
366 UserReferenceObject(CurIcon);
367 IntDestroyCurIconObject(CurIcon, Win32Process, TRUE);
368 }
369 }
370
371
372 /*
373 * @implemented
374 */
375 BOOL
376 APIENTRY
377 NtUserGetIconInfo(
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
383 _In_ BOOL bInternal)
384 {
385 ICONINFO ii;
386 PCURICON_OBJECT CurIcon;
387 NTSTATUS Status = STATUS_SUCCESS;
388 BOOL Ret = FALSE;
389 DWORD colorBpp = 0;
390
391 TRACE("Enter NtUserGetIconInfo\n");
392
393 /* Check if something was actually asked */
394 if (!IconInfo && !lpModule && !lpResName)
395 {
396 WARN("Nothing to fill.\n");
397 EngSetLastError(ERROR_INVALID_PARAMETER);
398 return FALSE;
399 }
400
401 UserEnterExclusive();
402
403 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
404 {
405 WARN("UserGetIconObject(0x%08x) Failed.\n", hCurIcon);
406 UserLeave();
407 return FALSE;
408 }
409
410 /* Give back the icon information */
411 if(IconInfo)
412 {
413 /* Fill data */
414 ii.fIcon = is_icon(CurIcon);
415 ii.xHotspot = CurIcon->xHotspot;
416 ii.yHotspot = CurIcon->yHotspot;
417
418 /* Copy bitmaps */
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;
424
425 /* Copy fields */
426 _SEH2_TRY
427 {
428 ProbeForWrite(IconInfo, sizeof(ICONINFO), 1);
429 RtlCopyMemory(IconInfo, &ii, sizeof(ICONINFO));
430
431 if (pbpp)
432 {
433 ProbeForWrite(pbpp, sizeof(DWORD), 1);
434 *pbpp = colorBpp;
435 }
436 }
437 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
438 {
439 Status = _SEH2_GetExceptionCode();
440 }
441 _SEH2_END
442 }
443
444 if (!NT_SUCCESS(Status))
445 {
446 WARN("Status: 0x%08x.\n", Status);
447 SetLastNtError(Status);
448 goto leave;
449 }
450
451 /* Give back the module name */
452 if(lpModule)
453 {
454 if(!CurIcon->ustrModule.Buffer)
455 {
456 EngSetLastError(ERROR_INVALID_HANDLE);
457 goto leave;
458 }
459 /* Copy what we can */
460 _SEH2_TRY
461 {
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);
466 }
467 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
468 {
469 Status = _SEH2_GetExceptionCode();
470 }
471 _SEH2_END
472 }
473
474 if (!NT_SUCCESS(Status))
475 {
476 SetLastNtError(Status);
477 goto leave;
478 }
479
480 if(lpResName)
481 {
482 if(!CurIcon->strName.Buffer)
483 {
484 EngSetLastError(ERROR_INVALID_HANDLE);
485 goto leave;
486 }
487 /* Copy it */
488 _SEH2_TRY
489 {
490 ProbeForWrite(lpResName, sizeof(UNICODE_STRING), 1);
491 if(IS_INTRESOURCE(CurIcon->strName.Buffer))
492 {
493 lpResName->Buffer = CurIcon->strName.Buffer;
494 lpResName->Length = 0;
495 }
496 else
497 {
498 lpResName->Length = min(lpResName->MaximumLength, CurIcon->strName.Length);
499 RtlCopyMemory(lpResName->Buffer, CurIcon->strName.Buffer, lpResName->Length);
500 }
501 }
502 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
503 {
504 Status = _SEH2_GetExceptionCode();
505 }
506 _SEH2_END
507 }
508
509 if (!NT_SUCCESS(Status))
510 {
511 SetLastNtError(Status);
512 goto leave;
513 }
514
515 Ret = TRUE;
516
517 leave:
518 UserDereferenceObject(CurIcon);
519
520 TRACE("Leave NtUserGetIconInfo, ret=%i\n", Ret);
521 UserLeave();
522
523 return Ret;
524 }
525
526
527 /*
528 * @implemented
529 */
530 BOOL
531 APIENTRY
532 NtUserGetIconSize(
533 HANDLE hCurIcon,
534 UINT istepIfAniCur,
535 PLONG plcx, // &size.cx
536 PLONG plcy) // &size.cy
537 {
538 PCURICON_OBJECT CurIcon;
539 NTSTATUS Status = STATUS_SUCCESS;
540 BOOL bRet = FALSE;
541
542 TRACE("Enter NtUserGetIconSize\n");
543 UserEnterExclusive();
544
545 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
546 {
547 goto cleanup;
548 }
549
550 _SEH2_TRY
551 {
552 ProbeForWrite(plcx, sizeof(LONG), 1);
553 *plcx = CurIcon->cx;
554 ProbeForWrite(plcy, sizeof(LONG), 1);
555 *plcy = CurIcon->cy;
556 }
557 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
558 {
559 Status = _SEH2_GetExceptionCode();
560 }
561 _SEH2_END
562
563 if (NT_SUCCESS(Status))
564 bRet = TRUE;
565 else
566 SetLastNtError(Status); // Maybe not, test this
567
568 UserDereferenceObject(CurIcon);
569
570 cleanup:
571 TRACE("Leave NtUserGetIconSize, ret=%i\n", bRet);
572 UserLeave();
573 return bRet;
574 }
575
576
577 /*
578 * @implemented
579 */
580 BOOL
581 APIENTRY
582 NtUserGetCursorInfo(
583 PCURSORINFO pci)
584 {
585 CURSORINFO SafeCi;
586 PSYSTEM_CURSORINFO CurInfo;
587 NTSTATUS Status = STATUS_SUCCESS;
588 PCURICON_OBJECT CurIcon;
589 BOOL Ret = FALSE;
590 DECLARE_RETURN(BOOL);
591
592 TRACE("Enter NtUserGetCursorInfo\n");
593 UserEnterExclusive();
594
595 CurInfo = IntGetSysCursorInfo();
596 CurIcon = (PCURICON_OBJECT)CurInfo->CurrentCursorObject;
597
598 SafeCi.cbSize = sizeof(CURSORINFO);
599 SafeCi.flags = ((CurIcon && CurInfo->ShowingCursor >= 0) ? CURSOR_SHOWING : 0);
600 SafeCi.hCursor = (CurIcon ? (HCURSOR)CurIcon->Self : (HCURSOR)0);
601
602 SafeCi.ptScreenPos = gpsi->ptCursor;
603
604 _SEH2_TRY
605 {
606 if (pci->cbSize == sizeof(CURSORINFO))
607 {
608 ProbeForWrite(pci, sizeof(CURSORINFO), 1);
609 RtlCopyMemory(pci, &SafeCi, sizeof(CURSORINFO));
610 Ret = TRUE;
611 }
612 else
613 {
614 EngSetLastError(ERROR_INVALID_PARAMETER);
615 }
616 }
617 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
618 {
619 Status = _SEH2_GetExceptionCode();
620 }
621 _SEH2_END;
622 if (!NT_SUCCESS(Status))
623 {
624 SetLastNtError(Status);
625 }
626
627 RETURN(Ret);
628
629 CLEANUP:
630 TRACE("Leave NtUserGetCursorInfo, ret=%i\n",_ret_);
631 UserLeave();
632 END_CLEANUP;
633 }
634
635 BOOL
636 APIENTRY
637 UserClipCursor(
638 RECTL *prcl)
639 {
640 /* FIXME: Check if process has WINSTA_WRITEATTRIBUTES */
641 PSYSTEM_CURSORINFO CurInfo;
642 PWND DesktopWindow = NULL;
643
644 CurInfo = IntGetSysCursorInfo();
645
646 DesktopWindow = UserGetDesktopWindow();
647
648 if (prcl != NULL && DesktopWindow != NULL)
649 {
650 if (prcl->right < prcl->left || prcl->bottom < prcl->top)
651 {
652 EngSetLastError(ERROR_INVALID_PARAMETER);
653 return FALSE;
654 }
655
656 CurInfo->bClipped = TRUE;
657
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;
664
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;
669
670 /* Make sure cursor is in clipping region */
671 UserSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y, 0, 0, FALSE);
672 }
673 else
674 {
675 CurInfo->bClipped = FALSE;
676 }
677
678 return TRUE;
679 }
680
681 /*
682 * @implemented
683 */
684 BOOL
685 APIENTRY
686 NtUserClipCursor(
687 RECTL *prcl)
688 {
689 RECTL rclLocal;
690 BOOL bResult;
691
692 if (prcl)
693 {
694 _SEH2_TRY
695 {
696 /* Probe and copy rect */
697 ProbeForRead(prcl, sizeof(RECTL), 1);
698 rclLocal = *prcl;
699 }
700 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
701 {
702 EngSetLastError(ERROR_INVALID_PARAMETER);
703 _SEH2_YIELD(return FALSE;)
704 }
705 _SEH2_END
706
707 prcl = &rclLocal;
708 }
709
710 UserEnterExclusive();
711
712 /* Call the internal function */
713 bResult = UserClipCursor(prcl);
714
715 UserLeave();
716
717 return bResult;
718 }
719
720
721 /*
722 * @implemented
723 */
724 BOOL
725 APIENTRY
726 NtUserDestroyCursor(
727 _In_ HANDLE hCurIcon,
728 _In_ BOOL bForce)
729 {
730 PCURICON_OBJECT CurIcon;
731 BOOL ret;
732 DECLARE_RETURN(BOOL);
733
734 TRACE("Enter NtUserDestroyCursorIcon\n");
735 UserEnterExclusive();
736
737 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
738 {
739 RETURN(FALSE);
740 }
741
742 ret = IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process(), bForce);
743 /* Note: IntDestroyCurIconObject will remove our reference for us! */
744
745 RETURN(ret);
746
747 CLEANUP:
748 TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_);
749 UserLeave();
750 END_CLEANUP;
751 }
752
753
754 /*
755 * @implemented
756 */
757 HICON
758 NTAPI
759 NtUserFindExistingCursorIcon(
760 _In_ PUNICODE_STRING pustrModule,
761 _In_ PUNICODE_STRING pustrRsrc,
762 _In_ FINDEXISTINGCURICONPARAM* param)
763 {
764 PCURICON_OBJECT CurIcon;
765 HICON Ret = NULL;
766 UNICODE_STRING ustrModuleSafe, ustrRsrcSafe;
767 FINDEXISTINGCURICONPARAM paramSafe;
768 NTSTATUS Status;
769
770 TRACE("Enter NtUserFindExistingCursorIcon\n");
771
772 /* Capture resource name (it can be an INTRESOURCE == ATOM) */
773 Status = ProbeAndCaptureUnicodeStringOrAtom(&ustrRsrcSafe, pustrRsrc);
774 if(!NT_SUCCESS(Status))
775 return NULL;
776 Status = ProbeAndCaptureUnicodeString(&ustrModuleSafe, UserMode, pustrModule);
777 if(!NT_SUCCESS(Status))
778 goto done;
779
780 _SEH2_TRY
781 {
782 ProbeForRead(param, sizeof(*param), 1);
783 RtlCopyMemory(&paramSafe, param, sizeof(paramSafe));
784 }
785 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
786 {
787 Status = _SEH2_GetExceptionCode();
788 }
789 _SEH2_END
790
791 UserEnterExclusive();
792 CurIcon = IntFindExistingCurIconObject(&ustrModuleSafe, &ustrRsrcSafe, &paramSafe);
793 if (CurIcon)
794 Ret = CurIcon->Self;
795 UserLeave();
796
797 done:
798 if(!IS_INTRESOURCE(ustrRsrcSafe.Buffer))
799 ExFreePoolWithTag(ustrRsrcSafe.Buffer, TAG_STRING);
800 ReleaseCapturedUnicodeString(&ustrModuleSafe, UserMode);
801
802 return Ret;
803 }
804
805
806 /*
807 * @implemented
808 */
809 BOOL
810 APIENTRY
811 NtUserGetClipCursor(
812 RECTL *lpRect)
813 {
814 /* FIXME: Check if process has WINSTA_READATTRIBUTES */
815 PSYSTEM_CURSORINFO CurInfo;
816 RECTL Rect;
817 NTSTATUS Status;
818 DECLARE_RETURN(BOOL);
819
820 TRACE("Enter NtUserGetClipCursor\n");
821 UserEnterExclusive();
822
823 if (!lpRect)
824 RETURN(FALSE);
825
826 CurInfo = IntGetSysCursorInfo();
827 if (CurInfo->bClipped)
828 {
829 Rect = CurInfo->rcClip;
830 }
831 else
832 {
833 Rect.left = 0;
834 Rect.top = 0;
835 Rect.right = UserGetSystemMetrics(SM_CXSCREEN);
836 Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
837 }
838
839 Status = MmCopyToCaller(lpRect, &Rect, sizeof(RECT));
840 if (!NT_SUCCESS(Status))
841 {
842 SetLastNtError(Status);
843 RETURN(FALSE);
844 }
845
846 RETURN(TRUE);
847
848 CLEANUP:
849 TRACE("Leave NtUserGetClipCursor, ret=%i\n",_ret_);
850 UserLeave();
851 END_CLEANUP;
852 }
853
854
855 /*
856 * @implemented
857 */
858 HCURSOR
859 APIENTRY
860 NtUserSetCursor(
861 HCURSOR hCursor)
862 {
863 PCURICON_OBJECT pcurOld, pcurNew;
864 HCURSOR hOldCursor = NULL;
865
866 TRACE("Enter NtUserSetCursor\n");
867 UserEnterExclusive();
868
869 if (hCursor)
870 {
871 pcurNew = UserGetCurIconObject(hCursor);
872 if (!pcurNew)
873 {
874 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
875 goto leave;
876 }
877 pcurNew->CURSORF_flags |= CURSORF_CURRENT;
878 }
879 else
880 {
881 pcurNew = NULL;
882 }
883
884 pcurOld = UserSetCursor(pcurNew, FALSE);
885 if (pcurOld)
886 {
887 hOldCursor = (HCURSOR)pcurOld->Self;
888 pcurOld->CURSORF_flags &= ~CURSORF_CURRENT;
889 UserDereferenceObject(pcurOld);
890 }
891
892 leave:
893 UserLeave();
894 return hOldCursor;
895 }
896
897
898 /*
899 * @unimplemented
900 */
901 BOOL
902 APIENTRY
903 NtUserSetCursorContents(
904 HANDLE hCurIcon,
905 PICONINFO UnsafeIconInfo)
906 {
907 FIXME(" is UNIMPLEMENTED.\n");
908 return FALSE;
909 }
910
911
912 /*
913 * @implemented
914 */
915 BOOL
916 APIENTRY
917 NtUserSetCursorIconData(
918 _In_ HCURSOR Handle,
919 _In_opt_ PUNICODE_STRING pustrModule,
920 _In_opt_ PUNICODE_STRING pustrRsrc,
921 _In_ PCURSORDATA pCursorData)
922 {
923 PCURICON_OBJECT CurIcon;
924 NTSTATUS Status = STATUS_SUCCESS;
925 BOOL Ret = FALSE;
926
927 TRACE("Enter NtUserSetCursorIconData\n");
928
929 /* If a module name is provided, we need a resource name, and vice versa */
930 if((pustrModule && !pustrRsrc) || (!pustrModule && pustrRsrc))
931 return FALSE;
932
933 UserEnterExclusive();
934
935 if (!(CurIcon = UserGetCurIconObject(Handle)))
936 {
937 UserLeave();
938 EngSetLastError(ERROR_INVALID_HANDLE);
939 return FALSE;
940 }
941
942 _SEH2_TRY
943 {
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;
954 }
955 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
956 {
957 Status = _SEH2_GetExceptionCode();
958 }
959 _SEH2_END
960
961 if (!NT_SUCCESS(Status))
962 {
963 SetLastNtError(Status);
964 goto done;
965 }
966
967 if(pustrModule)
968 {
969 /* We use this convenient function, because INTRESOURCEs and ATOMs are the same */
970 Status = ProbeAndCaptureUnicodeStringOrAtom(&CurIcon->strName, pustrRsrc);
971 if(!NT_SUCCESS(Status))
972 goto done;
973 Status = ProbeAndCaptureUnicodeString(&CurIcon->ustrModule, UserMode, pustrModule);
974 if(!NT_SUCCESS(Status))
975 goto done;
976 }
977
978
979 if(!CurIcon->hbmMask)
980 {
981 ERR("NtUserSetCursorIconData was got no hbmMask.\n");
982 EngSetLastError(ERROR_INVALID_PARAMETER);
983 goto done;
984 }
985
986 GreSetObjectOwner(CurIcon->hbmMask, GDI_OBJ_HMGR_PUBLIC);
987
988 if(CurIcon->hbmColor)
989 GreSetObjectOwner(CurIcon->hbmColor, GDI_OBJ_HMGR_PUBLIC);
990
991 if(CurIcon->hbmAlpha)
992 GreSetObjectOwner(CurIcon->hbmAlpha, GDI_OBJ_HMGR_PUBLIC);
993
994 Ret = TRUE;
995
996 done:
997 if(!Ret)
998 {
999 if(!IS_INTRESOURCE(CurIcon->strName.Buffer))
1000 ExFreePoolWithTag(CurIcon->strName.Buffer, TAG_STRING);
1001 if(CurIcon->ustrModule.Buffer)
1002 ReleaseCapturedUnicodeString(&CurIcon->ustrModule, UserMode);
1003 }
1004 UserDereferenceObject(CurIcon);
1005 TRACE("Leave NtUserSetCursorIconData, ret=%i\n",Ret);
1006 UserLeave();
1007
1008 return Ret;
1009 }
1010
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.
1018 */
1019 BOOL
1020 UserDrawIconEx(
1021 HDC hDc,
1022 INT xLeft,
1023 INT yTop,
1024 PCURICON_OBJECT pIcon,
1025 INT cxWidth,
1026 INT cyHeight,
1027 UINT istepIfAniCur,
1028 HBRUSH hbrFlickerFreeDraw,
1029 UINT diFlags)
1030 {
1031 PSURFACE psurfDest, psurfMask, psurfColor; //, psurfOffScreen = NULL;
1032 PDC pdc = NULL;
1033 BOOL Ret = FALSE;
1034 HBITMAP hbmMask, hbmColor, hbmAlpha;
1035 BOOL bOffScreen;
1036 RECTL rcDest, rcSrc;
1037 CLIPOBJ* pdcClipObj = NULL;
1038 EXLATEOBJ exlo;
1039
1040 /* Stupid case */
1041 if((diFlags & DI_NORMAL) == 0)
1042 {
1043 ERR("DrawIconEx called without mask or color bitmap to draw.\n");
1044 return FALSE;
1045 }
1046
1047 hbmMask = pIcon->hbmMask;
1048 hbmColor = pIcon->hbmColor;
1049 hbmAlpha = pIcon->hbmAlpha;
1050
1051 if (istepIfAniCur)
1052 ERR("NtUserDrawIconEx: istepIfAniCur is not supported!\n");
1053
1054 /*
1055 * Get our objects.
1056 * Shared locks are enough, we are only reading those bitmaps
1057 */
1058 psurfMask = SURFACE_ShareLockSurface(hbmMask);
1059 if(psurfMask == NULL)
1060 {
1061 ERR("Unable to lock the mask surface.\n");
1062 return FALSE;
1063 }
1064
1065 /* Color bitmap is not mandatory */
1066 if(hbmColor == NULL)
1067 {
1068 /* But then the mask bitmap must have the information in it's bottom half */
1069 ASSERT(psurfMask->SurfObj.sizlBitmap.cy == 2*pIcon->cy);
1070 psurfColor = NULL;
1071 }
1072 else if ((psurfColor = SURFACE_ShareLockSurface(hbmColor)) == NULL)
1073 {
1074 ERR("Unable to lock the color bitmap.\n");
1075 SURFACE_ShareUnlockSurface(psurfMask);
1076 return FALSE;
1077 }
1078
1079 pdc = DC_LockDc(hDc);
1080 if(!pdc)
1081 {
1082 ERR("Could not lock the destination DC.\n");
1083 SURFACE_ShareUnlockSurface(psurfMask);
1084 if(psurfColor) SURFACE_ShareUnlockSurface(psurfColor);
1085 return FALSE;
1086 }
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);
1091
1092 /* Prepare the underlying surface */
1093 DC_vPrepareDCsForBlit(pdc, rcDest, NULL, rcDest);
1094
1095 /* We now have our destination surface and rectangle */
1096 psurfDest = pdc->dclevel.pSurface;
1097
1098 if(psurfDest == NULL)
1099 {
1100 /* Empty DC */
1101 DC_vFinishBlit(pdc, NULL);
1102 DC_UnlockDc(pdc);
1103 SURFACE_ShareUnlockSurface(psurfMask);
1104 if(psurfColor) SURFACE_ShareUnlockSurface(psurfColor);
1105 return FALSE;
1106 }
1107
1108 /* Set source rect */
1109 RECTL_vSetRect(&rcSrc, 0, 0, pIcon->cx, pIcon->cy);
1110
1111 /* Fix width parameter, if needed */
1112 if (!cxWidth)
1113 {
1114 if(diFlags & DI_DEFAULTSIZE)
1115 cxWidth = is_icon(pIcon) ?
1116 UserGetSystemMetrics(SM_CXICON) : UserGetSystemMetrics(SM_CXCURSOR);
1117 else
1118 cxWidth = pIcon->cx;
1119 }
1120
1121 /* Fix height parameter, if needed */
1122 if (!cyHeight)
1123 {
1124 if(diFlags & DI_DEFAULTSIZE)
1125 cyHeight = is_icon(pIcon) ?
1126 UserGetSystemMetrics(SM_CYICON) : UserGetSystemMetrics(SM_CYCURSOR);
1127 else
1128 cyHeight = pIcon->cy;
1129 }
1130
1131 /* Should we render off-screen? */
1132 bOffScreen = hbrFlickerFreeDraw &&
1133 (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw) == GDI_OBJECT_TYPE_BRUSH);
1134
1135 if (bOffScreen)
1136 {
1137 /* Yes: Allocate and paint the offscreen surface */
1138 EBRUSHOBJ eboFill;
1139 PBRUSH pbrush = BRUSH_ShareLockBrush(hbrFlickerFreeDraw);
1140
1141 TRACE("Performing off-screen rendering.\n");
1142
1143 if(!pbrush)
1144 {
1145 ERR("Failed to get brush object.\n");
1146 goto Cleanup;
1147 }
1148
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,
1152 0, 0, NULL);
1153 if(!psurfOffScreen)
1154 {
1155 ERR("Failed to allocate the off-screen surface.\n");
1156 BRUSH_ShareUnlockBrush(pbrush);
1157 goto Cleanup;
1158 }
1159
1160 /* Paint the brush */
1161 EBRUSHOBJ_vInit(&eboFill, pbrush, psurfOffScreen, 0x00FFFFFF, 0, NULL);
1162 RECTL_vSetRect(&rcDest, 0, 0, cxWidth, cyHeight);
1163
1164 Ret = IntEngBitBlt(&psurfOffScreen->SurfObj,
1165 NULL,
1166 NULL,
1167 NULL,
1168 NULL,
1169 &rcDest,
1170 NULL,
1171 NULL,
1172 &eboFill.BrushObject,
1173 &pbrush->ptOrigin,
1174 ROP4_PATCOPY);
1175
1176 /* Clean up everything */
1177 EBRUSHOBJ_vCleanup(&eboFill);
1178 BRUSH_ShareUnlockBrush(pbrush);
1179
1180 if(!Ret)
1181 {
1182 ERR("Failed to paint the off-screen surface.\n");
1183 goto Cleanup;
1184 }
1185
1186 /* We now have our destination surface */
1187 psurfDest = psurfOffScreen;
1188 #else
1189 pdcClipObj = pdc->rosdc.CombinedClip;
1190 /* Paint the brush */
1191 EBRUSHOBJ_vInit(&eboFill, pbrush, psurfDest, 0x00FFFFFF, 0, NULL);
1192
1193 Ret = IntEngBitBlt(&psurfDest->SurfObj,
1194 NULL,
1195 NULL,
1196 pdcClipObj,
1197 NULL,
1198 &rcDest,
1199 NULL,
1200 NULL,
1201 &eboFill.BrushObject,
1202 &pbrush->ptOrigin,
1203 ROP4_PATCOPY);
1204
1205 /* Clean up everything */
1206 EBRUSHOBJ_vCleanup(&eboFill);
1207 BRUSH_ShareUnlockBrush(pbrush);
1208
1209 if(!Ret)
1210 {
1211 ERR("Failed to paint the off-screen surface.\n");
1212 goto Cleanup;
1213 }
1214 #endif
1215 }
1216 else
1217 {
1218 /* We directly draw to the DC */
1219 TRACE("Performing on screen rendering.\n");
1220 pdcClipObj = pdc->rosdc.CombinedClip;
1221 // psurfOffScreen = NULL;
1222 }
1223
1224 /* Now do the rendering */
1225 if(hbmAlpha && ((diFlags & DI_NORMAL) == DI_NORMAL))
1226 {
1227 BLENDOBJ blendobj = { {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA } };
1228 PSURFACE psurf = NULL;
1229
1230 psurf = SURFACE_ShareLockSurface(hbmAlpha);
1231 if(!psurf)
1232 {
1233 ERR("SURFACE_LockSurface failed!\n");
1234 goto NoAlpha;
1235 }
1236
1237 /* Initialize color translation object */
1238 EXLATEOBJ_vInitialize(&exlo, psurf->ppal, psurfDest->ppal, 0xFFFFFFFF, 0xFFFFFFFF, 0);
1239
1240 /* Now do it */
1241 Ret = IntEngAlphaBlend(&psurfDest->SurfObj,
1242 &psurf->SurfObj,
1243 pdcClipObj,
1244 &exlo.xlo,
1245 &rcDest,
1246 &rcSrc,
1247 &blendobj);
1248
1249 EXLATEOBJ_vCleanup(&exlo);
1250 SURFACE_ShareUnlockSurface(psurf);
1251 if(Ret) goto done;
1252 ERR("NtGdiAlphaBlend failed!\n");
1253 }
1254 NoAlpha:
1255 if (diFlags & DI_MASK)
1256 {
1257 DWORD rop4 = (diFlags & DI_IMAGE) ? ROP4_SRCAND : ROP4_SRCCOPY;
1258
1259 EXLATEOBJ_vInitSrcMonoXlate(&exlo, psurfDest->ppal, 0x00FFFFFF, 0);
1260
1261 Ret = IntEngStretchBlt(&psurfDest->SurfObj,
1262 &psurfMask->SurfObj,
1263 NULL,
1264 pdcClipObj,
1265 &exlo.xlo,
1266 NULL,
1267 &rcDest,
1268 &rcSrc,
1269 NULL,
1270 NULL,
1271 NULL,
1272 rop4);
1273
1274 EXLATEOBJ_vCleanup(&exlo);
1275
1276 if(!Ret)
1277 {
1278 ERR("Failed to mask the bitmap data.\n");
1279 goto Cleanup;
1280 }
1281 }
1282
1283 if(diFlags & DI_IMAGE)
1284 {
1285 if (psurfColor)
1286 {
1287 DWORD rop4 = (diFlags & DI_MASK) ? ROP4_SRCINVERT : ROP4_SRCCOPY ;
1288
1289 EXLATEOBJ_vInitialize(&exlo, psurfColor->ppal, psurfDest->ppal, 0x00FFFFFF, 0x00FFFFFF, 0);
1290
1291 Ret = IntEngStretchBlt(&psurfDest->SurfObj,
1292 &psurfColor->SurfObj,
1293 NULL,
1294 pdcClipObj,
1295 &exlo.xlo,
1296 NULL,
1297 &rcDest,
1298 &rcSrc,
1299 NULL,
1300 NULL,
1301 NULL,
1302 rop4);
1303
1304 EXLATEOBJ_vCleanup(&exlo);
1305
1306 if(!Ret)
1307 {
1308 ERR("Failed to render the icon bitmap.\n");
1309 goto Cleanup;
1310 }
1311 }
1312 else
1313 {
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);
1317
1318 EXLATEOBJ_vInitSrcMonoXlate(&exlo, psurfDest->ppal, 0x00FFFFFF, 0);
1319
1320 Ret = IntEngStretchBlt(&psurfDest->SurfObj,
1321 &psurfMask->SurfObj,
1322 NULL,
1323 pdcClipObj,
1324 &exlo.xlo,
1325 NULL,
1326 &rcDest,
1327 &rcSrc,
1328 NULL,
1329 NULL,
1330 NULL,
1331 rop4);
1332
1333 EXLATEOBJ_vCleanup(&exlo);
1334
1335 if(!Ret)
1336 {
1337 ERR("Failed to render the icon bitmap.\n");
1338 goto Cleanup;
1339 }
1340 }
1341 }
1342
1343 done:
1344 #if 0
1345 /* We're done. Was it a double buffered draw ? */
1346 if(bOffScreen)
1347 {
1348 /* Yes. Draw it back to our DC */
1349 POINTL ptSrc = {0, 0};
1350
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);
1355
1356 /* Get the clip object */
1357 pdcClipObj = pdc->rosdc.CombinedClip;
1358
1359 /* We now have our destination surface and rectangle */
1360 psurfDest = pdc->dclevel.pSurface;
1361
1362 /* Color translation */
1363 EXLATEOBJ_vInitialize(&exlo, psurfOffScreen->ppal, psurfDest->ppal, 0x00FFFFFF, 0x00FFFFFF, 0);
1364
1365 /* Blt it! */
1366 Ret = IntEngBitBlt(&psurfDest->SurfObj,
1367 &psurfOffScreen->SurfObj,
1368 NULL,
1369 pdcClipObj,
1370 &exlo.xlo,
1371 &rcDest,
1372 &ptSrc,
1373 NULL,
1374 NULL,
1375 NULL,
1376 ROP4_SRCCOPY);
1377
1378 EXLATEOBJ_vCleanup(&exlo);
1379 }
1380 #endif
1381 Cleanup:
1382 if(pdc)
1383 {
1384 DC_vFinishBlit(pdc, NULL);
1385 DC_UnlockDc(pdc);
1386 }
1387
1388 #if 0
1389 /* Delete off screen rendering surface */
1390 if(psurfOffScreen)
1391 GDIOBJ_vDeleteObject(&psurfOffScreen->BaseObject);
1392 #endif
1393
1394 /* Unlock other surfaces */
1395 SURFACE_ShareUnlockSurface(psurfMask);
1396 if(psurfColor) SURFACE_ShareUnlockSurface(psurfColor);
1397
1398 return Ret;
1399 }
1400
1401 /*
1402 * @implemented
1403 */
1404 BOOL
1405 APIENTRY
1406 NtUserDrawIconEx(
1407 HDC hdc,
1408 int xLeft,
1409 int yTop,
1410 HICON hIcon,
1411 int cxWidth,
1412 int cyHeight,
1413 UINT istepIfAniCur,
1414 HBRUSH hbrFlickerFreeDraw,
1415 UINT diFlags,
1416 BOOL bMetaHDC, // When TRUE, GDI functions need to be handled in User32!
1417 PVOID pDIXData)
1418 {
1419 PCURICON_OBJECT pIcon;
1420 BOOL Ret;
1421
1422 TRACE("Enter NtUserDrawIconEx\n");
1423 UserEnterExclusive();
1424
1425 if (!(pIcon = UserGetCurIconObject(hIcon)))
1426 {
1427 ERR("UserGetCurIconObject(0x%08x) failed!\n", hIcon);
1428 UserLeave();
1429 return FALSE;
1430 }
1431
1432 Ret = UserDrawIconEx(hdc,
1433 xLeft,
1434 yTop,
1435 pIcon,
1436 cxWidth,
1437 cyHeight,
1438 istepIfAniCur,
1439 hbrFlickerFreeDraw,
1440 diFlags);
1441
1442 UserDereferenceObject(pIcon);
1443
1444 UserLeave();
1445 return Ret;
1446 }
1447
1448 /* EOF */