37af3f006909295be86cc7c42e8ef2ce6372e44d
[reactos.git] / subsystems / win32 / win32k / ntuser / cursoricon.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21 * We handle two types of cursors/icons:
22 * - Private
23 * Loaded without LR_SHARED flag
24 * Private to a process
25 * Can be deleted by calling NtDestroyCursorIcon()
26 * CurIcon->hModule, CurIcon->hRsrc and CurIcon->hGroupRsrc set to NULL
27 * - Shared
28 * Loaded with LR_SHARED flag
29 * Possibly shared by multiple processes
30 * Immune to NtDestroyCursorIcon()
31 * CurIcon->hModule, CurIcon->hRsrc and CurIcon->hGroupRsrc are valid
32 * There's a M:N relationship between processes and (shared) cursor/icons.
33 * A process can have multiple cursor/icons and a cursor/icon can be used
34 * by multiple processes. To keep track of this we keep a list of all
35 * cursor/icons (CurIconList) and per cursor/icon we keep a list of
36 * CURICON_PROCESS structs starting at CurIcon->ProcessList.
37 */
38
39 #include <win32k.h>
40
41 #define NDEBUG
42 #include <debug.h>
43
44 static PAGED_LOOKASIDE_LIST gProcessLookasideList;
45 static LIST_ENTRY gCurIconList;
46
47 SYSTEM_CURSORINFO gSysCursorInfo;
48
49 BOOL
50 InitCursorImpl()
51 {
52 ExInitializePagedLookasideList(&gProcessLookasideList,
53 NULL,
54 NULL,
55 0,
56 sizeof(CURICON_PROCESS),
57 TAG_DIB,
58 128);
59 InitializeListHead(&gCurIconList);
60
61 gSysCursorInfo.Enabled = FALSE;
62 gSysCursorInfo.ButtonsDown = 0;
63 gSysCursorInfo.bClipped = FALSE;
64 gSysCursorInfo.LastBtnDown = 0;
65 gSysCursorInfo.CurrentCursorObject = NULL;
66 gSysCursorInfo.ShowingCursor = 0;
67 gSysCursorInfo.ClickLockActive = FALSE;
68 gSysCursorInfo.ClickLockTime = 0;
69
70 return TRUE;
71 }
72
73 PSYSTEM_CURSORINFO
74 IntGetSysCursorInfo()
75 {
76 return &gSysCursorInfo;
77 }
78
79 /* This function creates a reference for the object! */
80 PCURICON_OBJECT FASTCALL UserGetCurIconObject(HCURSOR hCurIcon)
81 {
82 PCURICON_OBJECT CurIcon;
83
84 if (!hCurIcon)
85 {
86 SetLastWin32Error(ERROR_INVALID_CURSOR_HANDLE);
87 return NULL;
88 }
89
90 CurIcon = (PCURICON_OBJECT)UserReferenceObjectByHandle(hCurIcon, otCursorIcon);
91 if (!CurIcon)
92 {
93 /* we never set ERROR_INVALID_ICON_HANDLE. lets hope noone ever checks for it */
94 SetLastWin32Error(ERROR_INVALID_CURSOR_HANDLE);
95 return NULL;
96 }
97
98 ASSERT(CurIcon->head.cLockObj >= 1);
99 return CurIcon;
100 }
101
102 HCURSOR
103 FASTCALL
104 UserSetCursor(
105 PCURICON_OBJECT NewCursor,
106 BOOL ForceChange)
107 {
108 PSYSTEM_CURSORINFO CurInfo;
109 PCURICON_OBJECT OldCursor;
110 HCURSOR hOldCursor = (HCURSOR)0;
111 HDC hdcScreen;
112
113 CurInfo = IntGetSysCursorInfo();
114
115 OldCursor = CurInfo->CurrentCursorObject;
116 if (OldCursor)
117 {
118 hOldCursor = (HCURSOR)OldCursor->Self;
119 }
120
121 /* Is the new cursor the same as the old cursor? */
122 if (OldCursor == NewCursor)
123 {
124 /* Nothing to to do in this case */
125 return hOldCursor;
126 }
127
128 /* Get the screen DC */
129 if(!(hdcScreen = IntGetScreenDC()))
130 {
131 return (HCURSOR)0;
132 }
133
134 /* Do we have a new cursor? */
135 if (NewCursor)
136 {
137 UserReferenceObject(NewCursor);
138
139 CurInfo->ShowingCursor = 1;
140 CurInfo->CurrentCursorObject = NewCursor;
141
142 /* Call GDI to set the new screen cursor */
143 GreSetPointerShape(hdcScreen,
144 NewCursor->IconInfo.hbmMask,
145 NewCursor->IconInfo.hbmColor,
146 NewCursor->IconInfo.xHotspot,
147 NewCursor->IconInfo.yHotspot,
148 gpsi->ptCursor.x,
149 gpsi->ptCursor.y);
150
151
152 }
153 else
154 {
155 /* Check if were diplaying a cursor */
156 if (OldCursor && CurInfo->ShowingCursor)
157 {
158 /* Remove the cursor */
159 GreMovePointer(hdcScreen, -1, -1);
160 DPRINT("Removing pointer!\n");
161 }
162
163 CurInfo->CurrentCursorObject = NULL;
164 CurInfo->ShowingCursor = 0;
165 }
166
167 /* OldCursor is not in use anymore */
168 if (OldCursor)
169 {
170 UserDereferenceObject(OldCursor);
171 }
172
173 /* Return handle of the old cursor */
174 return hOldCursor;
175 }
176
177 BOOL UserSetCursorPos( INT x, INT y, BOOL SendMouseMoveMsg)
178 {
179 PWND DesktopWindow;
180 PSYSTEM_CURSORINFO CurInfo;
181 HDC hDC;
182 MSG Msg;
183 RECTL rcClip;
184 POINT pt;
185
186 if(!(hDC = IntGetScreenDC()))
187 {
188 return FALSE;
189 }
190
191 if(!(DesktopWindow = UserGetDesktopWindow()))
192 {
193 return FALSE;
194 }
195
196 CurInfo = IntGetSysCursorInfo();
197
198 /* Clip cursor position */
199 if (!CurInfo->bClipped)
200 rcClip = DesktopWindow->rcClient;
201 else
202 rcClip = CurInfo->rcClip;
203
204 if(x >= rcClip.right) x = rcClip.right - 1;
205 if(x < rcClip.left) x = rcClip.left;
206 if(y >= rcClip.bottom) y = rcClip.bottom - 1;
207 if(y < rcClip.top) y = rcClip.top;
208
209 pt.x = x;
210 pt.y = y;
211
212
213 if (SendMouseMoveMsg)
214 {
215 /* Generate a mouse move message */
216 Msg.message = WM_MOUSEMOVE;
217 Msg.wParam = CurInfo->ButtonsDown;
218 Msg.lParam = MAKELPARAM(x, y);
219 Msg.pt = pt;
220 MsqInsertMouseMessage(&Msg);
221 }
222
223 /* Store the new cursor position */
224 gpsi->ptCursor = pt;
225
226 /* Move the mouse pointer */
227 GreMovePointer(hDC, x, y);
228
229 return TRUE;
230 }
231
232 /* Called from NtUserCallOneParam with Routine ONEPARAM_ROUTINE_SHOWCURSOR
233 * User32 macro NtUserShowCursor */
234 int UserShowCursor(BOOL bShow)
235 {
236 PSYSTEM_CURSORINFO CurInfo = IntGetSysCursorInfo();
237 HDC hdcScreen;
238
239 if (!(hdcScreen = IntGetScreenDC()))
240 {
241 return 0; /* No mouse */
242 }
243
244 if (bShow == FALSE)
245 {
246 /* Check if were diplaying a cursor */
247 if (CurInfo->ShowingCursor == 1)
248 {
249 /* Remove the pointer */
250 GreMovePointer(hdcScreen, -1, -1);
251 DPRINT("Removing pointer!\n");
252 }
253 CurInfo->ShowingCursor--;
254 }
255 else
256 {
257 if (CurInfo->ShowingCursor == 0)
258 {
259 /*Show the pointer*/
260 GreMovePointer(hdcScreen, gpsi->ptCursor.x, gpsi->ptCursor.y);
261 }
262 CurInfo->ShowingCursor++;
263 }
264
265 return CurInfo->ShowingCursor;
266 }
267
268 /*
269 * We have to register that this object is in use by the current
270 * process. The only way to do that seems to be to walk the list
271 * of cursor/icon objects starting at W32Process->CursorIconListHead.
272 * If the object is already present in the list, we don't have to do
273 * anything, if it's not present we add it and inc the ProcessCount
274 * in the object. Having to walk the list kind of sucks, but that's
275 * life...
276 */
277 static BOOLEAN FASTCALL
278 ReferenceCurIconByProcess(PCURICON_OBJECT CurIcon)
279 {
280 PPROCESSINFO Win32Process;
281 PCURICON_PROCESS Current;
282
283 Win32Process = PsGetCurrentProcessWin32Process();
284
285 LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
286 {
287 if (Current->Process == Win32Process)
288 {
289 /* Already registered for this process */
290 return TRUE;
291 }
292 }
293
294 /* Not registered yet */
295 Current = ExAllocateFromPagedLookasideList(&gProcessLookasideList);
296 if (NULL == Current)
297 {
298 return FALSE;
299 }
300 InsertHeadList(&CurIcon->ProcessList, &Current->ListEntry);
301 Current->Process = Win32Process;
302
303 return TRUE;
304 }
305
306 PCURICON_OBJECT FASTCALL
307 IntFindExistingCurIconObject(HMODULE hModule,
308 HRSRC hRsrc, LONG cx, LONG cy)
309 {
310 PCURICON_OBJECT CurIcon;
311
312 LIST_FOR_EACH(CurIcon, &gCurIconList, CURICON_OBJECT, ListEntry)
313 {
314
315 // if(NT_SUCCESS(UserReferenceObjectByPointer(Object, otCursorIcon))) //<- huh????
316 // UserReferenceObject( CurIcon);
317 // {
318 if ((CurIcon->hModule == hModule) && (CurIcon->hRsrc == hRsrc))
319 {
320 if (cx && ((cx != CurIcon->Size.cx) || (cy != CurIcon->Size.cy)))
321 {
322 // UserDereferenceObject(CurIcon);
323 continue;
324 }
325 if (! ReferenceCurIconByProcess(CurIcon))
326 {
327 return NULL;
328 }
329
330 return CurIcon;
331 }
332 // }
333 // UserDereferenceObject(CurIcon);
334
335 }
336
337 return NULL;
338 }
339
340 PCURICON_OBJECT
341 IntCreateCurIconHandle()
342 {
343 PCURICON_OBJECT CurIcon;
344 HANDLE hCurIcon;
345
346 CurIcon = UserCreateObject(gHandleTable, NULL, &hCurIcon, otCursorIcon, sizeof(CURICON_OBJECT));
347
348 if (!CurIcon)
349 {
350 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
351 return FALSE;
352 }
353
354 CurIcon->Self = hCurIcon;
355 InitializeListHead(&CurIcon->ProcessList);
356
357 if (! ReferenceCurIconByProcess(CurIcon))
358 {
359 DPRINT1("Failed to add process\n");
360 UserDeleteObject(hCurIcon, otCursorIcon);
361 UserDereferenceObject(CurIcon);
362 return NULL;
363 }
364
365 InsertHeadList(&gCurIconList, &CurIcon->ListEntry);
366
367 return CurIcon;
368 }
369
370 BOOLEAN FASTCALL
371 IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, BOOL ProcessCleanup)
372 {
373 PSYSTEM_CURSORINFO CurInfo;
374 HBITMAP bmpMask, bmpColor;
375 BOOLEAN Ret;
376 PCURICON_PROCESS Current = NULL;
377 PPROCESSINFO W32Process = PsGetCurrentProcessWin32Process();
378
379 /* Private objects can only be destroyed by their own process */
380 if (NULL == CurIcon->hModule)
381 {
382 ASSERT(CurIcon->ProcessList.Flink->Flink == &CurIcon->ProcessList);
383 Current = CONTAINING_RECORD(CurIcon->ProcessList.Flink, CURICON_PROCESS, ListEntry);
384 if (Current->Process != W32Process)
385 {
386 DPRINT1("Trying to destroy private icon/cursor of another process\n");
387 return FALSE;
388 }
389 }
390 else if (! ProcessCleanup)
391 {
392 DPRINT("Trying to destroy shared icon/cursor\n");
393 return FALSE;
394 }
395
396 /* Now find this process in the list of processes referencing this object and
397 remove it from that list */
398 LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
399 {
400 if (Current->Process == W32Process)
401 {
402 RemoveEntryList(&Current->ListEntry);
403 break;
404 }
405 }
406
407 ExFreeToPagedLookasideList(&gProcessLookasideList, Current);
408
409 /* If there are still processes referencing this object we can't destroy it yet */
410 if (! IsListEmpty(&CurIcon->ProcessList))
411 {
412 return TRUE;
413 }
414
415
416 if (! ProcessCleanup)
417 {
418 RemoveEntryList(&CurIcon->ListEntry);
419 }
420
421 CurInfo = IntGetSysCursorInfo();
422
423 if (CurInfo->CurrentCursorObject == CurIcon)
424 {
425 /* Hide the cursor if we're destroying the current cursor */
426 UserSetCursor(NULL, TRUE);
427 }
428
429 bmpMask = CurIcon->IconInfo.hbmMask;
430 bmpColor = CurIcon->IconInfo.hbmColor;
431
432 /* delete bitmaps */
433 if (bmpMask)
434 {
435 GDIOBJ_SetOwnership(bmpMask, PsGetCurrentProcess());
436 GreDeleteObject(bmpMask);
437 CurIcon->IconInfo.hbmMask = NULL;
438 }
439 if (bmpColor)
440 {
441 GDIOBJ_SetOwnership(bmpColor, PsGetCurrentProcess());
442 GreDeleteObject(bmpColor);
443 CurIcon->IconInfo.hbmColor = NULL;
444 }
445
446 /* We were given a pointer, no need to keep the reference anylonger! */
447 UserDereferenceObject(CurIcon);
448 Ret = UserDeleteObject(CurIcon->Self, otCursorIcon);
449
450 return Ret;
451 }
452
453 VOID FASTCALL
454 IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process)
455 {
456 PCURICON_OBJECT CurIcon, tmp;
457 PCURICON_PROCESS ProcessData;
458
459 LIST_FOR_EACH_SAFE(CurIcon, tmp, &gCurIconList, CURICON_OBJECT, ListEntry)
460 {
461 UserReferenceObject(CurIcon);
462 // if(NT_SUCCESS(UserReferenceObjectByPointer(Object, otCursorIcon)))
463 {
464 LIST_FOR_EACH(ProcessData, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
465 {
466 if (Win32Process == ProcessData->Process)
467 {
468 RemoveEntryList(&CurIcon->ListEntry);
469 IntDestroyCurIconObject(CurIcon, TRUE);
470 CurIcon = NULL;
471 break;
472 }
473 }
474
475 // UserDereferenceObject(Object);
476 }
477
478 if (CurIcon)
479 {
480 UserDereferenceObject(CurIcon);
481 }
482 }
483
484 }
485
486
487 /*
488 * @implemented
489 */
490 BOOL
491 APIENTRY
492 NtUserGetIconInfo(
493 HANDLE hCurIcon,
494 PICONINFO IconInfo,
495 PUNICODE_STRING lpInstName, // optional
496 PUNICODE_STRING lpResName, // optional
497 LPDWORD pbpp, // optional
498 BOOL bInternal)
499 {
500 ICONINFO ii;
501 PCURICON_OBJECT CurIcon;
502 NTSTATUS Status = STATUS_SUCCESS;
503 BOOL Ret = FALSE;
504 DWORD colorBpp = 0;
505
506 DPRINT("Enter NtUserGetIconInfo\n");
507 UserEnterExclusive();
508
509 if (!IconInfo)
510 {
511 SetLastWin32Error(ERROR_INVALID_PARAMETER);
512 goto leave;
513 }
514
515 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
516 {
517 goto leave;
518 }
519
520 RtlCopyMemory(&ii, &CurIcon->IconInfo, sizeof(ICONINFO));
521
522 /* Copy bitmaps */
523 ii.hbmMask = BITMAP_CopyBitmap(CurIcon->IconInfo.hbmMask);
524 ii.hbmColor = BITMAP_CopyBitmap(CurIcon->IconInfo.hbmColor);
525
526 if (pbpp)
527 {
528 PSURFACE psurfBmp;
529
530 psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmColor);
531 if (psurfBmp)
532 {
533 colorBpp = BitsPerFormat(psurfBmp->SurfObj.iBitmapFormat);
534 SURFACE_UnlockSurface(psurfBmp);
535 }
536 }
537
538 /* Copy fields */
539 _SEH2_TRY
540 {
541 ProbeForWrite(IconInfo, sizeof(ICONINFO), 1);
542 RtlCopyMemory(IconInfo, &ii, sizeof(ICONINFO));
543
544 if (pbpp)
545 {
546 ProbeForWrite(pbpp, sizeof(DWORD), 1);
547 *pbpp = colorBpp;
548 }
549 }
550 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
551 {
552 Status = _SEH2_GetExceptionCode();
553 }
554 _SEH2_END
555
556 if (NT_SUCCESS(Status))
557 Ret = TRUE;
558 else
559 SetLastNtError(Status);
560
561 UserDereferenceObject(CurIcon);
562
563 leave:
564 DPRINT("Leave NtUserGetIconInfo, ret=%i\n", Ret);
565 UserLeave();
566
567 return Ret;
568 }
569
570
571 /*
572 * @implemented
573 */
574 BOOL
575 APIENTRY
576 NtUserGetIconSize(
577 HANDLE hCurIcon,
578 UINT istepIfAniCur,
579 PLONG plcx, // &size.cx
580 PLONG plcy) // &size.cy
581 {
582 PCURICON_OBJECT CurIcon;
583 NTSTATUS Status = STATUS_SUCCESS;
584 BOOL bRet = FALSE;
585
586 DPRINT("Enter NtUserGetIconSize\n");
587 UserEnterExclusive();
588
589 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
590 {
591 goto cleanup;
592 }
593
594 _SEH2_TRY
595 {
596 ProbeForWrite(plcx, sizeof(LONG), 1);
597 RtlCopyMemory(plcx, &CurIcon->Size.cx, sizeof(LONG));
598 ProbeForWrite(plcy, sizeof(LONG), 1);
599 RtlCopyMemory(plcy, &CurIcon->Size.cy, sizeof(LONG));
600 }
601 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
602 {
603 Status = _SEH2_GetExceptionCode();
604 }
605 _SEH2_END
606
607 if (NT_SUCCESS(Status))
608 bRet = TRUE;
609 else
610 SetLastNtError(Status); // maybe not, test this
611
612 UserDereferenceObject(CurIcon);
613
614 cleanup:
615 DPRINT("Leave NtUserGetIconSize, ret=%i\n", bRet);
616 UserLeave();
617 return bRet;
618 }
619
620
621 /*
622 * @unimplemented
623 */
624 DWORD
625 APIENTRY
626 NtUserGetCursorFrameInfo(
627 DWORD Unknown0,
628 DWORD Unknown1,
629 DWORD Unknown2,
630 DWORD Unknown3)
631 {
632 UNIMPLEMENTED
633
634 return 0;
635 }
636
637
638 /*
639 * @implemented
640 */
641 BOOL
642 APIENTRY
643 NtUserGetCursorInfo(
644 PCURSORINFO pci)
645 {
646 CURSORINFO SafeCi;
647 PSYSTEM_CURSORINFO CurInfo;
648 NTSTATUS Status = STATUS_SUCCESS;
649 PCURICON_OBJECT CurIcon;
650 BOOL Ret = FALSE;
651 DECLARE_RETURN(BOOL);
652
653 DPRINT("Enter NtUserGetCursorInfo\n");
654 UserEnterExclusive();
655
656 CurInfo = IntGetSysCursorInfo();
657 CurIcon = (PCURICON_OBJECT)CurInfo->CurrentCursorObject;
658
659 SafeCi.cbSize = sizeof(CURSORINFO);
660 SafeCi.flags = ((CurInfo->ShowingCursor && CurIcon) ? CURSOR_SHOWING : 0);
661 SafeCi.hCursor = (CurIcon ? (HCURSOR)CurIcon->Self : (HCURSOR)0);
662
663 SafeCi.ptScreenPos = gpsi->ptCursor;
664
665 _SEH2_TRY
666 {
667 if (pci->cbSize == sizeof(CURSORINFO))
668 {
669 ProbeForWrite(pci, sizeof(CURSORINFO), 1);
670 RtlCopyMemory(pci, &SafeCi, sizeof(CURSORINFO));
671 Ret = TRUE;
672 }
673 else
674 {
675 SetLastWin32Error(ERROR_INVALID_PARAMETER);
676 }
677 }
678 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
679 {
680 Status = _SEH2_GetExceptionCode();
681 }
682 _SEH2_END;
683 if (!NT_SUCCESS(Status))
684 {
685 SetLastNtError(Status);
686 }
687
688 RETURN(Ret);
689
690 CLEANUP:
691 DPRINT("Leave NtUserGetCursorInfo, ret=%i\n",_ret_);
692 UserLeave();
693 END_CLEANUP;
694 }
695
696 BOOL
697 APIENTRY
698 UserClipCursor(
699 RECTL *prcl)
700 {
701 /* FIXME - check if process has WINSTA_WRITEATTRIBUTES */
702 PSYSTEM_CURSORINFO CurInfo;
703 PWND DesktopWindow = NULL;
704
705 CurInfo = IntGetSysCursorInfo();
706
707 DesktopWindow = UserGetDesktopWindow();
708
709 if (prcl != NULL &&
710 (prcl->right > prcl->left) &&
711 (prcl->bottom > prcl->top) &&
712 DesktopWindow != NULL)
713 {
714 CurInfo->bClipped = TRUE;
715 RECTL_bIntersectRect(&CurInfo->rcClip, prcl, &DesktopWindow->rcWindow);
716 UserSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y, FALSE);
717 }
718 else
719 {
720 CurInfo->bClipped = FALSE;
721 }
722
723 return TRUE;
724 }
725
726 /*
727 * @implemented
728 */
729 BOOL
730 APIENTRY
731 NtUserClipCursor(
732 RECTL *prcl)
733 {
734 RECTL rclLocal;
735 BOOL bResult;
736
737 if (prcl)
738 {
739 _SEH2_TRY
740 {
741 /* Probe and copy rect */
742 ProbeForRead(prcl, sizeof(RECTL), 1);
743 rclLocal = *prcl;
744 }
745 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
746 {
747 SetLastWin32Error(ERROR_INVALID_PARAMETER);
748 _SEH2_YIELD(return FALSE;)
749 }
750 _SEH2_END
751
752 prcl = &rclLocal;
753 }
754
755 UserEnterExclusive();
756
757 /* Call the internal function */
758 bResult = UserClipCursor(prcl);
759
760 UserLeave();
761
762 return bResult;
763 }
764
765
766 /*
767 * @implemented
768 */
769 BOOL
770 APIENTRY
771 NtUserDestroyCursor(
772 HANDLE hCurIcon,
773 DWORD Unknown)
774 {
775 PCURICON_OBJECT CurIcon;
776 BOOL ret;
777 DECLARE_RETURN(BOOL);
778
779 DPRINT("Enter NtUserDestroyCursorIcon\n");
780 UserEnterExclusive();
781
782 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
783 {
784 RETURN(FALSE);
785 }
786
787 ret = IntDestroyCurIconObject(CurIcon, FALSE);
788 /* Note: IntDestroyCurIconObject will remove our reference for us! */
789
790 RETURN(ret);
791
792 CLEANUP:
793 DPRINT("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_);
794 UserLeave();
795 END_CLEANUP;
796 }
797
798
799 /*
800 * @implemented
801 */
802 HICON
803 APIENTRY
804 NtUserFindExistingCursorIcon(
805 HMODULE hModule,
806 HRSRC hRsrc,
807 LONG cx,
808 LONG cy)
809 {
810 PCURICON_OBJECT CurIcon;
811 HANDLE Ret = (HANDLE)0;
812 DECLARE_RETURN(HICON);
813
814 DPRINT("Enter NtUserFindExistingCursorIcon\n");
815 UserEnterExclusive();
816
817 CurIcon = IntFindExistingCurIconObject(hModule, hRsrc, cx, cy);
818 if (CurIcon)
819 {
820 Ret = CurIcon->Self;
821
822 // IntReleaseCurIconObject(CurIcon);//faxme: is this correct? does IntFindExistingCurIconObject add a ref?
823 RETURN(Ret);
824 }
825
826 SetLastWin32Error(ERROR_INVALID_CURSOR_HANDLE);
827 RETURN((HANDLE)0);
828
829 CLEANUP:
830 DPRINT("Leave NtUserFindExistingCursorIcon, ret=%i\n",_ret_);
831 UserLeave();
832 END_CLEANUP;
833 }
834
835
836 /*
837 * @implemented
838 */
839 BOOL
840 APIENTRY
841 NtUserGetClipCursor(
842 RECTL *lpRect)
843 {
844 /* FIXME - check if process has WINSTA_READATTRIBUTES */
845 PSYSTEM_CURSORINFO CurInfo;
846 RECTL Rect;
847 NTSTATUS Status;
848 DECLARE_RETURN(BOOL);
849
850 DPRINT("Enter NtUserGetClipCursor\n");
851 UserEnterExclusive();
852
853 if (!lpRect)
854 RETURN(FALSE);
855
856 CurInfo = IntGetSysCursorInfo();
857 if (CurInfo->bClipped)
858 {
859 Rect = CurInfo->rcClip;
860 }
861 else
862 {
863 Rect.left = 0;
864 Rect.top = 0;
865 Rect.right = UserGetSystemMetrics(SM_CXSCREEN);
866 Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
867 }
868
869 Status = MmCopyToCaller(lpRect, &Rect, sizeof(RECT));
870 if (!NT_SUCCESS(Status))
871 {
872 SetLastNtError(Status);
873 RETURN(FALSE);
874 }
875
876 RETURN(TRUE);
877
878 CLEANUP:
879 DPRINT("Leave NtUserGetClipCursor, ret=%i\n",_ret_);
880 UserLeave();
881 END_CLEANUP;
882 }
883
884
885 /*
886 * @implemented
887 */
888 HCURSOR
889 APIENTRY
890 NtUserSetCursor(
891 HCURSOR hCursor)
892 {
893 PCURICON_OBJECT CurIcon;
894 HICON OldCursor;
895 DECLARE_RETURN(HCURSOR);
896
897 DPRINT("Enter NtUserSetCursor\n");
898 UserEnterExclusive();
899
900 if (hCursor)
901 {
902 if (!(CurIcon = UserGetCurIconObject(hCursor)))
903 {
904 RETURN(NULL);
905 }
906 }
907 else
908 {
909 CurIcon = NULL;
910 }
911
912 OldCursor = UserSetCursor(CurIcon, FALSE);
913
914 if (CurIcon)
915 {
916 UserDereferenceObject(CurIcon);
917 }
918
919 RETURN(OldCursor);
920
921 CLEANUP:
922 DPRINT("Leave NtUserSetCursor, ret=%i\n",_ret_);
923 UserLeave();
924 END_CLEANUP;
925 }
926
927
928 /*
929 * @implemented
930 */
931 BOOL
932 APIENTRY
933 NtUserSetCursorContents(
934 HANDLE hCurIcon,
935 PICONINFO UnsafeIconInfo)
936 {
937 PCURICON_OBJECT CurIcon;
938 ICONINFO IconInfo;
939 PSURFACE psurfBmp;
940 NTSTATUS Status;
941 BOOL Ret = FALSE;
942 DECLARE_RETURN(BOOL);
943
944 DPRINT("Enter NtUserSetCursorContents\n");
945 UserEnterExclusive();
946
947 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
948 {
949 RETURN(FALSE);
950 }
951
952 /* Copy fields */
953 Status = MmCopyFromCaller(&IconInfo, UnsafeIconInfo, sizeof(ICONINFO));
954 if (!NT_SUCCESS(Status))
955 {
956 SetLastNtError(Status);
957 goto done;
958 }
959
960 /* Delete old bitmaps */
961 if ((CurIcon->IconInfo.hbmColor)
962 && (CurIcon->IconInfo.hbmColor != IconInfo.hbmColor))
963 {
964 GreDeleteObject(CurIcon->IconInfo.hbmColor);
965 }
966 if ((CurIcon->IconInfo.hbmMask)
967 && CurIcon->IconInfo.hbmMask != IconInfo.hbmMask)
968 {
969 GreDeleteObject(CurIcon->IconInfo.hbmMask);
970 }
971
972 /* Copy new IconInfo field */
973 CurIcon->IconInfo = IconInfo;
974
975 psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmColor);
976 if (psurfBmp)
977 {
978 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
979 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
980 SURFACE_UnlockSurface(psurfBmp);
981 GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmColor, NULL);
982 }
983 else
984 {
985 psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmMask);
986 if (!psurfBmp)
987 goto done;
988
989 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
990 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy / 2;
991
992 SURFACE_UnlockSurface(psurfBmp);
993 }
994 GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmMask, NULL);
995
996 Ret = TRUE;
997
998 done:
999
1000 if (CurIcon)
1001 {
1002 UserDereferenceObject(CurIcon);
1003 }
1004 RETURN(Ret);
1005
1006 CLEANUP:
1007 DPRINT("Leave NtUserSetCursorContents, ret=%i\n",_ret_);
1008 UserLeave();
1009 END_CLEANUP;
1010 }
1011
1012
1013 /*
1014 * @implemented
1015 */
1016 #if 0
1017 BOOL
1018 APIENTRY
1019 NtUserSetCursorIconData(
1020 HANDLE Handle,
1021 HMODULE hModule,
1022 PUNICODE_STRING pstrResName,
1023 PICONINFO pIconInfo)
1024 {
1025 PCURICON_OBJECT CurIcon;
1026 PSURFACE psurfBmp;
1027 NTSTATUS Status = STATUS_SUCCESS;
1028 BOOL Ret = FALSE;
1029 DECLARE_RETURN(BOOL);
1030
1031 DPRINT("Enter NtUserSetCursorIconData\n");
1032 UserEnterExclusive();
1033
1034 if (!(CurIcon = UserGetCurIconObject(Handle)))
1035 {
1036 RETURN(FALSE);
1037 }
1038
1039 CurIcon->hModule = hModule;
1040 CurIcon->hRsrc = NULL; //hRsrc;
1041 CurIcon->hGroupRsrc = NULL; //hGroupRsrc;
1042
1043 _SEH2_TRY
1044 {
1045 ProbeForRead(pIconInfo, sizeof(ICONINFO), 1);
1046 RtlCopyMemory(&CurIcon->IconInfo, pIconInfo, sizeof(ICONINFO));
1047
1048 CurIcon->IconInfo.hbmMask = BITMAP_CopyBitmap(pIconInfo->hbmMask);
1049 CurIcon->IconInfo.hbmColor = BITMAP_CopyBitmap(pIconInfo->hbmColor);
1050
1051 if (CurIcon->IconInfo.hbmColor)
1052 {
1053 if ((psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmColor)))
1054 {
1055 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
1056 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
1057 SURFACE_UnlockSurface(psurfBmp);
1058 GDIOBJ_SetOwnership(GdiHandleTable, CurIcon->IconInfo.hbmMask, NULL);
1059 }
1060 }
1061 if (CurIcon->IconInfo.hbmMask)
1062 {
1063 if (CurIcon->IconInfo.hbmColor == NULL)
1064 {
1065 if ((psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmMask)))
1066 {
1067 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
1068 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
1069 SURFACE_UnlockSurface(psurfBmp);
1070 }
1071 }
1072 GDIOBJ_SetOwnership(GdiHandleTable, CurIcon->IconInfo.hbmMask, NULL);
1073 }
1074 }
1075 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1076 {
1077 Status = _SEH2_GetExceptionCode();
1078 }
1079 _SEH2_END
1080
1081 if (!NT_SUCCESS(Status))
1082 SetLastNtError(Status);
1083 else
1084 Ret = TRUE;
1085
1086 UserDereferenceObject(CurIcon);
1087 RETURN(Ret);
1088
1089 CLEANUP:
1090 DPRINT("Leave NtUserSetCursorIconData, ret=%i\n",_ret_);
1091 UserLeave();
1092 END_CLEANUP;
1093 }
1094 #else
1095 BOOL
1096 APIENTRY
1097 NtUserSetCursorIconData(
1098 HANDLE hCurIcon,
1099 PBOOL fIcon,
1100 POINT *Hotspot,
1101 HMODULE hModule,
1102 HRSRC hRsrc,
1103 HRSRC hGroupRsrc)
1104 {
1105 PCURICON_OBJECT CurIcon;
1106 NTSTATUS Status;
1107 POINT SafeHotspot;
1108 BOOL Ret = FALSE;
1109 DECLARE_RETURN(BOOL);
1110
1111 DPRINT("Enter NtUserSetCursorIconData\n");
1112 UserEnterExclusive();
1113
1114 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
1115 {
1116 RETURN(FALSE);
1117 }
1118
1119 CurIcon->hModule = hModule;
1120 CurIcon->hRsrc = hRsrc;
1121 CurIcon->hGroupRsrc = hGroupRsrc;
1122
1123 /* Copy fields */
1124 if (fIcon)
1125 {
1126 Status = MmCopyFromCaller(&CurIcon->IconInfo.fIcon, fIcon, sizeof(BOOL));
1127 if (!NT_SUCCESS(Status))
1128 {
1129 SetLastNtError(Status);
1130 goto done;
1131 }
1132 }
1133 else
1134 {
1135 if (!Hotspot)
1136 Ret = TRUE;
1137 }
1138
1139 if (Hotspot)
1140 {
1141 Status = MmCopyFromCaller(&SafeHotspot, Hotspot, sizeof(POINT));
1142 if (NT_SUCCESS(Status))
1143 {
1144 CurIcon->IconInfo.xHotspot = SafeHotspot.x;
1145 CurIcon->IconInfo.yHotspot = SafeHotspot.y;
1146
1147 Ret = TRUE;
1148 }
1149 else
1150 SetLastNtError(Status);
1151 }
1152
1153 if (!fIcon && !Hotspot)
1154 {
1155 Ret = TRUE;
1156 }
1157
1158 done:
1159 if(Ret)
1160 {
1161 /* This icon is shared now */
1162 GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmMask, NULL);
1163 if(CurIcon->IconInfo.hbmColor)
1164 {
1165 GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmColor, NULL);
1166 }
1167 }
1168 UserDereferenceObject(CurIcon);
1169 RETURN(Ret);
1170
1171
1172 CLEANUP:
1173 DPRINT("Leave NtUserSetCursorIconData, ret=%i\n",_ret_);
1174 UserLeave();
1175 END_CLEANUP;
1176 }
1177 #endif
1178
1179 /*
1180 * @unimplemented
1181 */
1182 BOOL
1183 APIENTRY
1184 NtUserSetSystemCursor(
1185 HCURSOR hcur,
1186 DWORD id)
1187 {
1188 return FALSE;
1189 }
1190
1191 /* Mostly inspired from wine code */
1192 BOOL
1193 UserDrawIconEx(
1194 HDC hDc,
1195 INT xLeft,
1196 INT yTop,
1197 PCURICON_OBJECT pIcon,
1198 INT cxWidth,
1199 INT cyHeight,
1200 UINT istepIfAniCur,
1201 HBRUSH hbrFlickerFreeDraw,
1202 UINT diFlags)
1203 {
1204 BOOL Ret = FALSE;
1205 HBITMAP hbmMask, hbmColor;
1206 BITMAP bmpColor, bm;
1207 BOOL DoFlickerFree;
1208 INT iOldBkColor = 0, iOldTxtColor = 0;
1209
1210 HDC hMemDC, hDestDC = hDc;
1211 HGDIOBJ hOldOffBrush = 0;
1212 HGDIOBJ hOldOffBmp = 0;
1213 HBITMAP hTmpBmp = 0, hOffBmp = 0;
1214 BOOL bAlpha = FALSE;
1215 INT x=xLeft, y=yTop;
1216
1217 hbmMask = pIcon->IconInfo.hbmMask;
1218 hbmColor = pIcon->IconInfo.hbmColor;
1219
1220 if (istepIfAniCur)
1221 DPRINT1("NtUserDrawIconEx: istepIfAniCur is not supported!\n");
1222
1223 if (!hbmMask || !IntGdiGetObject(hbmMask, sizeof(BITMAP), (PVOID)&bm))
1224 {
1225 return FALSE;
1226 }
1227
1228 if (hbmColor && !IntGdiGetObject(hbmColor, sizeof(BITMAP), (PVOID)&bmpColor))
1229 {
1230 return FALSE;
1231 }
1232
1233 if(!(hMemDC = NtGdiCreateCompatibleDC(hDc)))
1234 {
1235 DPRINT1("NtGdiCreateCompatibleDC failed!\n");
1236 return FALSE;
1237 }
1238
1239 /* Check for alpha */
1240 if (hbmColor
1241 && (bmpColor.bmBitsPixel == 32)
1242 && (diFlags & DI_IMAGE))
1243 {
1244 SURFACE *psurfOff = NULL;
1245 PFN_DIB_GetPixel fnSource_GetPixel = NULL;
1246 INT i, j;
1247
1248 /* In order to correctly display 32 bit icons Windows first scans the image,
1249 because information about transparency is not stored in any image's headers */
1250 psurfOff = SURFACE_LockSurface(hbmColor);
1251 if (psurfOff)
1252 {
1253 fnSource_GetPixel = DibFunctionsForBitmapFormat[psurfOff->SurfObj.iBitmapFormat].DIB_GetPixel;
1254 if (fnSource_GetPixel)
1255 {
1256 for (i = 0; i < psurfOff->SurfObj.sizlBitmap.cx; i++)
1257 {
1258 for (j = 0; j < psurfOff->SurfObj.sizlBitmap.cy; j++)
1259 {
1260 bAlpha = ((BYTE)(fnSource_GetPixel(&psurfOff->SurfObj, i, j) >> 24) & 0xff);
1261 if (bAlpha)
1262 break;
1263 }
1264 if (bAlpha)
1265 break;
1266 }
1267 }
1268 SURFACE_UnlockSurface(psurfOff);
1269 }
1270 }
1271
1272 if (!cxWidth)
1273 cxWidth = ((diFlags & DI_DEFAULTSIZE) ?
1274 UserGetSystemMetrics(SM_CXICON) : pIcon->Size.cx);
1275
1276 if (!cyHeight)
1277 cyHeight = ((diFlags & DI_DEFAULTSIZE) ?
1278 UserGetSystemMetrics(SM_CYICON) : pIcon->Size.cy);
1279
1280 DoFlickerFree = (hbrFlickerFreeDraw &&
1281 (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw) == GDI_OBJECT_TYPE_BRUSH));
1282
1283 if (DoFlickerFree)
1284 {
1285 hDestDC = NtGdiCreateCompatibleDC(hDc);
1286 if(!hDestDC)
1287 {
1288 DPRINT1("NtGdiCreateCompatibleDC failed!\n");
1289 Ret = FALSE;
1290 goto Cleanup ;
1291 }
1292 hOffBmp = NtGdiCreateCompatibleBitmap(hDc, cxWidth, cyHeight);
1293 if(!hOffBmp)
1294 {
1295 DPRINT1("NtGdiCreateCompatibleBitmap failed!\n");
1296 goto Cleanup ;
1297 }
1298 hOldOffBmp = NtGdiSelectBitmap(hDestDC, hOffBmp);
1299 hOldOffBrush = NtGdiSelectBrush(hDestDC, hbrFlickerFreeDraw);
1300 NtGdiPatBlt(hDestDC, 0, 0, cxWidth, cyHeight, PATCOPY);
1301 NtGdiSelectBrush(hDestDC, hOldOffBrush);
1302 x=y=0;
1303 }
1304
1305 /* Set Background/foreground colors */
1306 iOldTxtColor = IntGdiSetTextColor(hDc, 0); //black
1307 iOldBkColor = IntGdiSetBkColor(hDc, 0x00FFFFFF); //white
1308
1309 if(bAlpha && (diFlags & DI_IMAGE))
1310 {
1311 BLENDFUNCTION pixelblend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
1312 BYTE Alpha;
1313 INT i, j;
1314 PSURFACE psurf;
1315 PBYTE ptr ;
1316 HBITMAP hMemBmp = NULL;
1317
1318 hMemBmp = BITMAP_CopyBitmap(hbmColor);
1319 if(!hMemBmp)
1320 {
1321 DPRINT1("BITMAP_CopyBitmap failed!");
1322 goto CleanupAlpha;
1323 }
1324
1325 psurf = SURFACE_LockSurface(hMemBmp);
1326 if(!psurf)
1327 {
1328 DPRINT1("SURFACE_LockSurface failed!\n");
1329 goto CleanupAlpha;
1330 }
1331
1332 /* premultiply with the alpha channel value */
1333 for (i = 0; i < psurf->SurfObj.sizlBitmap.cy; i++)
1334 {
1335 ptr = (PBYTE)psurf->SurfObj.pvScan0 + i*psurf->SurfObj.lDelta;
1336 for (j = 0; j < psurf->SurfObj.sizlBitmap.cx; j++)
1337 {
1338 Alpha = ptr[3];
1339 ptr[0] = (ptr[0] * Alpha) / 0xff;
1340 ptr[1] = (ptr[1] * Alpha) / 0xff;
1341 ptr[2] = (ptr[2] * Alpha) / 0xff;
1342
1343 ptr += 4;
1344 }
1345 }
1346
1347 SURFACE_UnlockSurface(psurf);
1348
1349 hTmpBmp = NtGdiSelectBitmap(hMemDC, hMemBmp);
1350
1351 Ret = NtGdiAlphaBlend(hDestDC,
1352 x,
1353 y,
1354 cxWidth,
1355 cyHeight,
1356 hMemDC,
1357 0,
1358 0,
1359 pIcon->Size.cx,
1360 pIcon->Size.cy,
1361 pixelblend,
1362 NULL);
1363 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1364 CleanupAlpha:
1365 if(hMemBmp) NtGdiDeleteObjectApp(hMemBmp);
1366 if(Ret) goto done;
1367 }
1368
1369 if (diFlags & DI_MASK)
1370 {
1371 hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmMask);
1372 NtGdiStretchBlt(hDestDC,
1373 x,
1374 y,
1375 cxWidth,
1376 cyHeight,
1377 hMemDC,
1378 0,
1379 0,
1380 pIcon->Size.cx,
1381 pIcon->Size.cy,
1382 SRCAND,
1383 0);
1384 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1385 }
1386
1387 if(diFlags & DI_IMAGE)
1388 {
1389 if (hbmColor)
1390 {
1391 DWORD rop = (diFlags & DI_MASK) ? SRCINVERT : SRCCOPY ;
1392 hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmColor);
1393 NtGdiStretchBlt(hDestDC,
1394 x,
1395 y,
1396 cxWidth,
1397 cyHeight,
1398 hMemDC,
1399 0,
1400 0,
1401 pIcon->Size.cx,
1402 pIcon->Size.cy,
1403 rop,
1404 0);
1405 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1406 }
1407 else
1408 {
1409 /* Mask bitmap holds the information in its second half */
1410 DWORD rop = (diFlags & DI_MASK) ? SRCINVERT : SRCCOPY ;
1411 hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmMask);
1412 NtGdiStretchBlt(hDestDC,
1413 x,
1414 y,
1415 cxWidth,
1416 cyHeight,
1417 hMemDC,
1418 0,
1419 pIcon->Size.cy,
1420 pIcon->Size.cx,
1421 pIcon->Size.cy,
1422 rop,
1423 0);
1424 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1425 }
1426 }
1427
1428 done:
1429 if(hDestDC != hDc)
1430 {
1431 NtGdiBitBlt(hDc, xLeft, yTop, cxWidth, cyHeight, hDestDC, 0, 0, SRCCOPY, 0, 0);
1432 }
1433
1434 /* Restore foreground and background colors */
1435 IntGdiSetBkColor(hDc, iOldBkColor);
1436 IntGdiSetTextColor(hDc, iOldTxtColor);
1437
1438 Ret = TRUE ;
1439
1440 Cleanup:
1441 NtGdiDeleteObjectApp(hMemDC);
1442 if(hDestDC != hDc)
1443 {
1444 if(hOldOffBmp) NtGdiSelectBitmap(hDestDC, hOldOffBmp);
1445 NtGdiDeleteObjectApp(hDestDC);
1446 if(hOffBmp) NtGdiDeleteObjectApp(hOffBmp);
1447 }
1448
1449 return Ret;
1450 }
1451
1452 /*
1453 * @implemented
1454 */
1455 BOOL
1456 APIENTRY
1457 NtUserDrawIconEx(
1458 HDC hdc,
1459 int xLeft,
1460 int yTop,
1461 HICON hIcon,
1462 int cxWidth,
1463 int cyHeight,
1464 UINT istepIfAniCur,
1465 HBRUSH hbrFlickerFreeDraw,
1466 UINT diFlags,
1467 BOOL bMetaHDC, // When TRUE, GDI functions need to be handled in User32!
1468 PVOID pDIXData)
1469 {
1470 PCURICON_OBJECT pIcon;
1471 BOOL Ret;
1472
1473 DPRINT("Enter NtUserDrawIconEx\n");
1474 UserEnterExclusive();
1475
1476 if (!(pIcon = UserGetCurIconObject(hIcon)))
1477 {
1478 DPRINT1("UserGetCurIconObject() failed!\n");
1479 UserLeave();
1480 return FALSE;
1481 }
1482
1483 Ret = UserDrawIconEx(hdc,
1484 xLeft,
1485 yTop,
1486 pIcon,
1487 cxWidth,
1488 cyHeight,
1489 istepIfAniCur,
1490 hbrFlickerFreeDraw,
1491 diFlags);
1492
1493 UserDereferenceObject(pIcon);
1494
1495 UserLeave();
1496 return Ret;
1497 }
1498