874fc3bf01d41f6c5ae685a955cf51ce1944b9e1
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / cursoricon.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Cursor and icon functions
5 * FILE: subsystem/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 PPAGED_LOOKASIDE_LIST pgProcessLookasideList;
31 static LIST_ENTRY gCurIconList;
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 /* This function creates a reference for the object! */
70 PCURICON_OBJECT FASTCALL UserGetCurIconObject(HCURSOR hCurIcon)
71 {
72 PCURICON_OBJECT CurIcon;
73
74 if (!hCurIcon)
75 {
76 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
77 return NULL;
78 }
79
80 CurIcon = (PCURICON_OBJECT)UserReferenceObjectByHandle(hCurIcon, otCursorIcon);
81 if (!CurIcon)
82 {
83 /* we never set ERROR_INVALID_ICON_HANDLE. lets hope noone ever checks for it */
84 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
85 return NULL;
86 }
87
88 ASSERT(CurIcon->head.cLockObj >= 1);
89 return CurIcon;
90 }
91
92 BOOL UserSetCursorPos( INT x, INT y, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook)
93 {
94 PWND DesktopWindow;
95 PSYSTEM_CURSORINFO CurInfo;
96 MSG Msg;
97 RECTL rcClip;
98 POINT pt;
99
100 if(!(DesktopWindow = UserGetDesktopWindow()))
101 {
102 return FALSE;
103 }
104
105 CurInfo = IntGetSysCursorInfo();
106
107 /* Clip cursor position */
108 if (!CurInfo->bClipped)
109 rcClip = DesktopWindow->rcClient;
110 else
111 rcClip = CurInfo->rcClip;
112
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;
117
118 pt.x = x;
119 pt.y = y;
120
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);
125 Msg.pt = pt;
126 co_MsqInsertMouseMessage(&Msg, flags, dwExtraInfo, Hook);
127
128 /* 2. Store the new cursor position */
129 gpsi->ptCursor = pt;
130
131 return TRUE;
132 }
133
134 /*
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
141 * life...
142 */
143 static BOOLEAN FASTCALL
144 ReferenceCurIconByProcess(PCURICON_OBJECT CurIcon)
145 {
146 PPROCESSINFO Win32Process;
147 PCURICON_PROCESS Current;
148
149 Win32Process = PsGetCurrentProcessWin32Process();
150
151 LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
152 {
153 if (Current->Process == Win32Process)
154 {
155 /* Already registered for this process */
156 return TRUE;
157 }
158 }
159
160 /* Not registered yet */
161 Current = ExAllocateFromPagedLookasideList(pgProcessLookasideList);
162 if (NULL == Current)
163 {
164 return FALSE;
165 }
166 InsertHeadList(&CurIcon->ProcessList, &Current->ListEntry);
167 Current->Process = Win32Process;
168
169 return TRUE;
170 }
171
172 PCURICON_OBJECT FASTCALL
173 IntFindExistingCurIconObject(HMODULE hModule,
174 HRSRC hRsrc, LONG cx, LONG cy)
175 {
176 PCURICON_OBJECT CurIcon;
177
178 LIST_FOR_EACH(CurIcon, &gCurIconList, CURICON_OBJECT, ListEntry)
179 {
180
181 // if(NT_SUCCESS(UserReferenceObjectByPointer(Object, otCursorIcon))) //<- huh????
182 // UserReferenceObject( CurIcon);
183 // {
184 if ((CurIcon->hModule == hModule) && (CurIcon->hRsrc == hRsrc))
185 {
186 if (cx && ((cx != CurIcon->Size.cx) || (cy != CurIcon->Size.cy)))
187 {
188 // UserDereferenceObject(CurIcon);
189 continue;
190 }
191 if (! ReferenceCurIconByProcess(CurIcon))
192 {
193 return NULL;
194 }
195
196 return CurIcon;
197 }
198 // }
199 // UserDereferenceObject(CurIcon);
200
201 }
202
203 return NULL;
204 }
205
206 PCURICON_OBJECT
207 IntCreateCurIconHandle()
208 {
209 PCURICON_OBJECT CurIcon;
210 HANDLE hCurIcon;
211
212 CurIcon = UserCreateObject(gHandleTable, NULL, &hCurIcon, otCursorIcon, sizeof(CURICON_OBJECT));
213
214 if (!CurIcon)
215 {
216 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
217 return FALSE;
218 }
219
220 CurIcon->Self = hCurIcon;
221 InitializeListHead(&CurIcon->ProcessList);
222
223 if (! ReferenceCurIconByProcess(CurIcon))
224 {
225 ERR("Failed to add process\n");
226 UserDeleteObject(hCurIcon, otCursorIcon);
227 UserDereferenceObject(CurIcon);
228 return NULL;
229 }
230
231 InsertHeadList(&gCurIconList, &CurIcon->ListEntry);
232
233 return CurIcon;
234 }
235
236 BOOLEAN FASTCALL
237 IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, BOOL ProcessCleanup)
238 {
239 PSYSTEM_CURSORINFO CurInfo;
240 HBITMAP bmpMask, bmpColor;
241 BOOLEAN Ret;
242 PCURICON_PROCESS Current = NULL;
243 PPROCESSINFO W32Process = PsGetCurrentProcessWin32Process();
244
245 /* Private objects can only be destroyed by their own process */
246 if (NULL == CurIcon->hModule)
247 {
248 ASSERT(CurIcon->ProcessList.Flink->Flink == &CurIcon->ProcessList);
249 Current = CONTAINING_RECORD(CurIcon->ProcessList.Flink, CURICON_PROCESS, ListEntry);
250 if (Current->Process != W32Process)
251 {
252 ERR("Trying to destroy private icon/cursor of another process\n");
253 return FALSE;
254 }
255 }
256 else if (! ProcessCleanup)
257 {
258 TRACE("Trying to destroy shared icon/cursor\n");
259 return FALSE;
260 }
261
262 /* Now find this process in the list of processes referencing this object and
263 remove it from that list */
264 LIST_FOR_EACH(Current, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
265 {
266 if (Current->Process == W32Process)
267 {
268 RemoveEntryList(&Current->ListEntry);
269 break;
270 }
271 }
272
273 ExFreeToPagedLookasideList(pgProcessLookasideList, Current);
274
275 /* If there are still processes referencing this object we can't destroy it yet */
276 if (! IsListEmpty(&CurIcon->ProcessList))
277 {
278 return TRUE;
279 }
280
281
282 if (! ProcessCleanup)
283 {
284 RemoveEntryList(&CurIcon->ListEntry);
285 }
286
287 CurInfo = IntGetSysCursorInfo();
288
289 if (CurInfo->CurrentCursorObject == CurIcon)
290 {
291 /* Hide the cursor if we're destroying the current cursor */
292 UserSetCursor(NULL, TRUE);
293 }
294
295 bmpMask = CurIcon->IconInfo.hbmMask;
296 bmpColor = CurIcon->IconInfo.hbmColor;
297
298 /* delete bitmaps */
299 if (bmpMask)
300 {
301 GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED);
302 GreDeleteObject(bmpMask);
303 CurIcon->IconInfo.hbmMask = NULL;
304 }
305 if (bmpColor)
306 {
307 GreSetObjectOwner(bmpColor, GDI_OBJ_HMGR_POWNED);
308 GreDeleteObject(bmpColor);
309 CurIcon->IconInfo.hbmColor = NULL;
310 }
311
312 /* We were given a pointer, no need to keep the reference anylonger! */
313 UserDereferenceObject(CurIcon);
314 Ret = UserDeleteObject(CurIcon->Self, otCursorIcon);
315
316 return Ret;
317 }
318
319 VOID FASTCALL
320 IntCleanupCurIcons(struct _EPROCESS *Process, PPROCESSINFO Win32Process)
321 {
322 PCURICON_OBJECT CurIcon, tmp;
323 PCURICON_PROCESS ProcessData;
324
325 LIST_FOR_EACH_SAFE(CurIcon, tmp, &gCurIconList, CURICON_OBJECT, ListEntry)
326 {
327 UserReferenceObject(CurIcon);
328 // if(NT_SUCCESS(UserReferenceObjectByPointer(Object, otCursorIcon)))
329 {
330 LIST_FOR_EACH(ProcessData, &CurIcon->ProcessList, CURICON_PROCESS, ListEntry)
331 {
332 if (Win32Process == ProcessData->Process)
333 {
334 RemoveEntryList(&CurIcon->ListEntry);
335 IntDestroyCurIconObject(CurIcon, TRUE);
336 CurIcon = NULL;
337 break;
338 }
339 }
340
341 // UserDereferenceObject(Object);
342 }
343
344 if (CurIcon)
345 {
346 UserDereferenceObject(CurIcon);
347 }
348 }
349
350 }
351
352
353 /*
354 * @implemented
355 */
356 BOOL
357 APIENTRY
358 NtUserGetIconInfo(
359 HANDLE hCurIcon,
360 PICONINFO IconInfo,
361 PUNICODE_STRING lpInstName, // optional
362 PUNICODE_STRING lpResName, // optional
363 LPDWORD pbpp, // optional
364 BOOL bInternal)
365 {
366 ICONINFO ii;
367 PCURICON_OBJECT CurIcon;
368 NTSTATUS Status = STATUS_SUCCESS;
369 BOOL Ret = FALSE;
370 DWORD colorBpp = 0;
371
372 TRACE("Enter NtUserGetIconInfo\n");
373 UserEnterExclusive();
374
375 if (!IconInfo)
376 {
377 EngSetLastError(ERROR_INVALID_PARAMETER);
378 goto leave;
379 }
380
381 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
382 {
383 goto leave;
384 }
385
386 RtlCopyMemory(&ii, &CurIcon->IconInfo, sizeof(ICONINFO));
387
388 /* Copy bitmaps */
389 ii.hbmMask = BITMAP_CopyBitmap(CurIcon->IconInfo.hbmMask);
390 ii.hbmColor = BITMAP_CopyBitmap(CurIcon->IconInfo.hbmColor);
391
392 if (pbpp)
393 {
394 PSURFACE psurfBmp;
395
396 psurfBmp = SURFACE_ShareLockSurface(CurIcon->IconInfo.hbmColor);
397 if (psurfBmp)
398 {
399 colorBpp = BitsPerFormat(psurfBmp->SurfObj.iBitmapFormat);
400 SURFACE_ShareUnlockSurface(psurfBmp);
401 }
402 }
403
404 /* Copy fields */
405 _SEH2_TRY
406 {
407 ProbeForWrite(IconInfo, sizeof(ICONINFO), 1);
408 RtlCopyMemory(IconInfo, &ii, sizeof(ICONINFO));
409
410 if (pbpp)
411 {
412 ProbeForWrite(pbpp, sizeof(DWORD), 1);
413 *pbpp = colorBpp;
414 }
415 }
416 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
417 {
418 Status = _SEH2_GetExceptionCode();
419 }
420 _SEH2_END
421
422 if (NT_SUCCESS(Status))
423 Ret = TRUE;
424 else
425 SetLastNtError(Status);
426
427 UserDereferenceObject(CurIcon);
428
429 leave:
430 TRACE("Leave NtUserGetIconInfo, ret=%i\n", Ret);
431 UserLeave();
432
433 return Ret;
434 }
435
436
437 /*
438 * @implemented
439 */
440 BOOL
441 APIENTRY
442 NtUserGetIconSize(
443 HANDLE hCurIcon,
444 UINT istepIfAniCur,
445 PLONG plcx, // &size.cx
446 PLONG plcy) // &size.cy
447 {
448 PCURICON_OBJECT CurIcon;
449 NTSTATUS Status = STATUS_SUCCESS;
450 BOOL bRet = FALSE;
451
452 TRACE("Enter NtUserGetIconSize\n");
453 UserEnterExclusive();
454
455 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
456 {
457 goto cleanup;
458 }
459
460 _SEH2_TRY
461 {
462 ProbeForWrite(plcx, sizeof(LONG), 1);
463 RtlCopyMemory(plcx, &CurIcon->Size.cx, sizeof(LONG));
464 ProbeForWrite(plcy, sizeof(LONG), 1);
465 RtlCopyMemory(plcy, &CurIcon->Size.cy, sizeof(LONG));
466 }
467 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
468 {
469 Status = _SEH2_GetExceptionCode();
470 }
471 _SEH2_END
472
473 if (NT_SUCCESS(Status))
474 bRet = TRUE;
475 else
476 SetLastNtError(Status); // maybe not, test this
477
478 UserDereferenceObject(CurIcon);
479
480 cleanup:
481 TRACE("Leave NtUserGetIconSize, ret=%i\n", bRet);
482 UserLeave();
483 return bRet;
484 }
485
486
487 /*
488 * @implemented
489 */
490 BOOL
491 APIENTRY
492 NtUserGetCursorInfo(
493 PCURSORINFO pci)
494 {
495 CURSORINFO SafeCi;
496 PSYSTEM_CURSORINFO CurInfo;
497 NTSTATUS Status = STATUS_SUCCESS;
498 PCURICON_OBJECT CurIcon;
499 BOOL Ret = FALSE;
500 DECLARE_RETURN(BOOL);
501
502 TRACE("Enter NtUserGetCursorInfo\n");
503 UserEnterExclusive();
504
505 CurInfo = IntGetSysCursorInfo();
506 CurIcon = (PCURICON_OBJECT)CurInfo->CurrentCursorObject;
507
508 SafeCi.cbSize = sizeof(CURSORINFO);
509 SafeCi.flags = ((CurIcon && CurInfo->ShowingCursor >= 0) ? CURSOR_SHOWING : 0);
510 SafeCi.hCursor = (CurIcon ? (HCURSOR)CurIcon->Self : (HCURSOR)0);
511
512 SafeCi.ptScreenPos = gpsi->ptCursor;
513
514 _SEH2_TRY
515 {
516 if (pci->cbSize == sizeof(CURSORINFO))
517 {
518 ProbeForWrite(pci, sizeof(CURSORINFO), 1);
519 RtlCopyMemory(pci, &SafeCi, sizeof(CURSORINFO));
520 Ret = TRUE;
521 }
522 else
523 {
524 EngSetLastError(ERROR_INVALID_PARAMETER);
525 }
526 }
527 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
528 {
529 Status = _SEH2_GetExceptionCode();
530 }
531 _SEH2_END;
532 if (!NT_SUCCESS(Status))
533 {
534 SetLastNtError(Status);
535 }
536
537 RETURN(Ret);
538
539 CLEANUP:
540 TRACE("Leave NtUserGetCursorInfo, ret=%i\n",_ret_);
541 UserLeave();
542 END_CLEANUP;
543 }
544
545 BOOL
546 APIENTRY
547 UserClipCursor(
548 RECTL *prcl)
549 {
550 /* FIXME - check if process has WINSTA_WRITEATTRIBUTES */
551 PSYSTEM_CURSORINFO CurInfo;
552 PWND DesktopWindow = NULL;
553
554 CurInfo = IntGetSysCursorInfo();
555
556 DesktopWindow = UserGetDesktopWindow();
557
558 if (prcl != NULL && DesktopWindow != NULL)
559 {
560 if (prcl->right < prcl->left || prcl->bottom < prcl->top)
561 {
562 EngSetLastError(ERROR_INVALID_PARAMETER);
563 return FALSE;
564 }
565
566 CurInfo->bClipped = TRUE;
567
568 /* Set nw cliping region. Note: we can't use RECTL_bIntersectRect because
569 it sets rect to 0 0 0 0 when it's empty. For more info see monitor winetest */
570 CurInfo->rcClip.left = max(prcl->left, DesktopWindow->rcWindow.left);
571 CurInfo->rcClip.right = min(prcl->right, DesktopWindow->rcWindow.right);
572 if (CurInfo->rcClip.right < CurInfo->rcClip.left)
573 CurInfo->rcClip.right = CurInfo->rcClip.left;
574
575 CurInfo->rcClip.top = max(prcl->top, DesktopWindow->rcWindow.top);
576 CurInfo->rcClip.bottom = min(prcl->bottom, DesktopWindow->rcWindow.bottom);
577 if (CurInfo->rcClip.bottom < CurInfo->rcClip.top)
578 CurInfo->rcClip.bottom = CurInfo->rcClip.top;
579
580 /* Make sure cursor is in clipping region */
581 UserSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y, 0, 0, FALSE);
582 }
583 else
584 {
585 CurInfo->bClipped = FALSE;
586 }
587
588 return TRUE;
589 }
590
591 /*
592 * @implemented
593 */
594 BOOL
595 APIENTRY
596 NtUserClipCursor(
597 RECTL *prcl)
598 {
599 RECTL rclLocal;
600 BOOL bResult;
601
602 if (prcl)
603 {
604 _SEH2_TRY
605 {
606 /* Probe and copy rect */
607 ProbeForRead(prcl, sizeof(RECTL), 1);
608 rclLocal = *prcl;
609 }
610 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
611 {
612 EngSetLastError(ERROR_INVALID_PARAMETER);
613 _SEH2_YIELD(return FALSE;)
614 }
615 _SEH2_END
616
617 prcl = &rclLocal;
618 }
619
620 UserEnterExclusive();
621
622 /* Call the internal function */
623 bResult = UserClipCursor(prcl);
624
625 UserLeave();
626
627 return bResult;
628 }
629
630
631 /*
632 * @implemented
633 */
634 BOOL
635 APIENTRY
636 NtUserDestroyCursor(
637 HANDLE hCurIcon,
638 DWORD Unknown)
639 {
640 PCURICON_OBJECT CurIcon;
641 BOOL ret;
642 DECLARE_RETURN(BOOL);
643
644 TRACE("Enter NtUserDestroyCursorIcon\n");
645 UserEnterExclusive();
646
647 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
648 {
649 RETURN(FALSE);
650 }
651
652 ret = IntDestroyCurIconObject(CurIcon, FALSE);
653 /* Note: IntDestroyCurIconObject will remove our reference for us! */
654
655 RETURN(ret);
656
657 CLEANUP:
658 TRACE("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_);
659 UserLeave();
660 END_CLEANUP;
661 }
662
663
664 /*
665 * @implemented
666 */
667 HICON
668 APIENTRY
669 NtUserFindExistingCursorIcon(
670 HMODULE hModule,
671 HRSRC hRsrc,
672 LONG cx,
673 LONG cy)
674 {
675 PCURICON_OBJECT CurIcon;
676 HANDLE Ret = (HANDLE)0;
677 DECLARE_RETURN(HICON);
678
679 TRACE("Enter NtUserFindExistingCursorIcon\n");
680 UserEnterExclusive();
681
682 CurIcon = IntFindExistingCurIconObject(hModule, hRsrc, cx, cy);
683 if (CurIcon)
684 {
685 Ret = CurIcon->Self;
686
687 // IntReleaseCurIconObject(CurIcon);//faxme: is this correct? does IntFindExistingCurIconObject add a ref?
688 RETURN(Ret);
689 }
690
691 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
692 RETURN((HANDLE)0);
693
694 CLEANUP:
695 TRACE("Leave NtUserFindExistingCursorIcon, ret=%i\n",_ret_);
696 UserLeave();
697 END_CLEANUP;
698 }
699
700
701 /*
702 * @implemented
703 */
704 BOOL
705 APIENTRY
706 NtUserGetClipCursor(
707 RECTL *lpRect)
708 {
709 /* FIXME - check if process has WINSTA_READATTRIBUTES */
710 PSYSTEM_CURSORINFO CurInfo;
711 RECTL Rect;
712 NTSTATUS Status;
713 DECLARE_RETURN(BOOL);
714
715 TRACE("Enter NtUserGetClipCursor\n");
716 UserEnterExclusive();
717
718 if (!lpRect)
719 RETURN(FALSE);
720
721 CurInfo = IntGetSysCursorInfo();
722 if (CurInfo->bClipped)
723 {
724 Rect = CurInfo->rcClip;
725 }
726 else
727 {
728 Rect.left = 0;
729 Rect.top = 0;
730 Rect.right = UserGetSystemMetrics(SM_CXSCREEN);
731 Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
732 }
733
734 Status = MmCopyToCaller(lpRect, &Rect, sizeof(RECT));
735 if (!NT_SUCCESS(Status))
736 {
737 SetLastNtError(Status);
738 RETURN(FALSE);
739 }
740
741 RETURN(TRUE);
742
743 CLEANUP:
744 TRACE("Leave NtUserGetClipCursor, ret=%i\n",_ret_);
745 UserLeave();
746 END_CLEANUP;
747 }
748
749
750 /*
751 * @implemented
752 */
753 HCURSOR
754 APIENTRY
755 NtUserSetCursor(
756 HCURSOR hCursor)
757 {
758 PCURICON_OBJECT pcurOld, pcurNew;
759 HCURSOR hOldCursor = NULL;
760
761 TRACE("Enter NtUserSetCursor\n");
762 UserEnterExclusive();
763
764 if (hCursor)
765 {
766 pcurNew = UserGetCurIconObject(hCursor);
767 if (!pcurNew)
768 {
769 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
770 goto leave;
771 }
772 }
773 else
774 {
775 pcurNew = NULL;
776 }
777
778 pcurOld = UserSetCursor(pcurNew, FALSE);
779 if (pcurOld)
780 {
781 hOldCursor = (HCURSOR)pcurOld->Self;
782 UserDereferenceObject(pcurOld);
783 }
784
785 leave:
786 UserLeave();
787 return hOldCursor;
788 }
789
790
791 /*
792 * @implemented
793 */
794 BOOL
795 APIENTRY
796 NtUserSetCursorContents(
797 HANDLE hCurIcon,
798 PICONINFO UnsafeIconInfo)
799 {
800 PCURICON_OBJECT CurIcon;
801 ICONINFO IconInfo;
802 PSURFACE psurfBmp;
803 NTSTATUS Status;
804 BOOL Ret = FALSE;
805 DECLARE_RETURN(BOOL);
806
807 TRACE("Enter NtUserSetCursorContents\n");
808 UserEnterExclusive();
809
810 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
811 {
812 RETURN(FALSE);
813 }
814
815 /* Copy fields */
816 Status = MmCopyFromCaller(&IconInfo, UnsafeIconInfo, sizeof(ICONINFO));
817 if (!NT_SUCCESS(Status))
818 {
819 SetLastNtError(Status);
820 goto done;
821 }
822
823 /* Delete old bitmaps */
824 if ((CurIcon->IconInfo.hbmColor)
825 && (CurIcon->IconInfo.hbmColor != IconInfo.hbmColor))
826 {
827 GreDeleteObject(CurIcon->IconInfo.hbmColor);
828 }
829 if ((CurIcon->IconInfo.hbmMask)
830 && CurIcon->IconInfo.hbmMask != IconInfo.hbmMask)
831 {
832 GreDeleteObject(CurIcon->IconInfo.hbmMask);
833 }
834
835 /* Copy new IconInfo field */
836 CurIcon->IconInfo = IconInfo;
837
838 if (CurIcon->IconInfo.hbmColor)
839 {
840 psurfBmp = SURFACE_ShareLockSurface(CurIcon->IconInfo.hbmColor);
841 if (!psurfBmp)
842 goto done;
843
844 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
845 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
846 SURFACE_ShareUnlockSurface(psurfBmp);
847 GreSetObjectOwner(CurIcon->IconInfo.hbmColor, GDI_OBJ_HMGR_PUBLIC);
848 }
849 else
850 {
851 psurfBmp = SURFACE_ShareLockSurface(CurIcon->IconInfo.hbmMask);
852 if (!psurfBmp)
853 goto done;
854
855 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
856 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy / 2;
857
858 SURFACE_ShareUnlockSurface(psurfBmp);
859 }
860 GreSetObjectOwner(CurIcon->IconInfo.hbmMask, GDI_OBJ_HMGR_PUBLIC);
861
862 Ret = TRUE;
863
864 done:
865
866 if (CurIcon)
867 {
868 UserDereferenceObject(CurIcon);
869 }
870 RETURN(Ret);
871
872 CLEANUP:
873 TRACE("Leave NtUserSetCursorContents, ret=%i\n",_ret_);
874 UserLeave();
875 END_CLEANUP;
876 }
877
878
879 /*
880 * @implemented
881 */
882 #if 0
883 BOOL
884 APIENTRY
885 NtUserSetCursorIconData(
886 HANDLE Handle,
887 HMODULE hModule,
888 PUNICODE_STRING pstrResName,
889 PICONINFO pIconInfo)
890 {
891 PCURICON_OBJECT CurIcon;
892 PSURFACE psurfBmp;
893 NTSTATUS Status = STATUS_SUCCESS;
894 BOOL Ret = FALSE;
895 DECLARE_RETURN(BOOL);
896
897 TRACE("Enter NtUserSetCursorIconData\n");
898 UserEnterExclusive();
899
900 if (!(CurIcon = UserGetCurIconObject(Handle)))
901 {
902 RETURN(FALSE);
903 }
904
905 CurIcon->hModule = hModule;
906 CurIcon->hRsrc = NULL; //hRsrc;
907 CurIcon->hGroupRsrc = NULL; //hGroupRsrc;
908
909 _SEH2_TRY
910 {
911 ProbeForRead(pIconInfo, sizeof(ICONINFO), 1);
912 RtlCopyMemory(&CurIcon->IconInfo, pIconInfo, sizeof(ICONINFO));
913
914 CurIcon->IconInfo.hbmMask = BITMAP_CopyBitmap(pIconInfo->hbmMask);
915 CurIcon->IconInfo.hbmColor = BITMAP_CopyBitmap(pIconInfo->hbmColor);
916
917 if (CurIcon->IconInfo.hbmColor)
918 {
919 if ((psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmColor)))
920 {
921 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
922 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
923 SURFACE_UnlockSurface(psurfBmp);
924 GreSetObjectOwner(CurIcon->IconInfo.hbmMask, GDI_OBJ_HMGR_PUBLIC);
925 }
926 }
927 if (CurIcon->IconInfo.hbmMask)
928 {
929 if (CurIcon->IconInfo.hbmColor == NULL)
930 {
931 if ((psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmMask)))
932 {
933 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
934 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
935 SURFACE_UnlockSurface(psurfBmp);
936 }
937 }
938 GreSetObjectOwner(CurIcon->IconInfo.hbmMask, GDI_OBJ_HMGR_PUBLIC);
939 }
940 }
941 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
942 {
943 Status = _SEH2_GetExceptionCode();
944 }
945 _SEH2_END
946
947 if (!NT_SUCCESS(Status))
948 SetLastNtError(Status);
949 else
950 Ret = TRUE;
951
952 UserDereferenceObject(CurIcon);
953 RETURN(Ret);
954
955 CLEANUP:
956 TRACE("Leave NtUserSetCursorIconData, ret=%i\n",_ret_);
957 UserLeave();
958 END_CLEANUP;
959 }
960 #else
961 BOOL
962 APIENTRY
963 NtUserSetCursorIconData(
964 HANDLE hCurIcon,
965 PBOOL fIcon,
966 POINT *Hotspot,
967 HMODULE hModule,
968 HRSRC hRsrc,
969 HRSRC hGroupRsrc)
970 {
971 PCURICON_OBJECT CurIcon;
972 NTSTATUS Status;
973 POINT SafeHotspot;
974 BOOL Ret = FALSE;
975 DECLARE_RETURN(BOOL);
976
977 TRACE("Enter NtUserSetCursorIconData\n");
978 UserEnterExclusive();
979
980 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
981 {
982 RETURN(FALSE);
983 }
984
985 CurIcon->hModule = hModule;
986 CurIcon->hRsrc = hRsrc;
987 CurIcon->hGroupRsrc = hGroupRsrc;
988
989 /* Copy fields */
990 if (fIcon)
991 {
992 Status = MmCopyFromCaller(&CurIcon->IconInfo.fIcon, fIcon, sizeof(BOOL));
993 if (!NT_SUCCESS(Status))
994 {
995 SetLastNtError(Status);
996 goto done;
997 }
998 }
999 else
1000 {
1001 if (!Hotspot)
1002 Ret = TRUE;
1003 }
1004
1005 if (Hotspot)
1006 {
1007 Status = MmCopyFromCaller(&SafeHotspot, Hotspot, sizeof(POINT));
1008 if (NT_SUCCESS(Status))
1009 {
1010 CurIcon->IconInfo.xHotspot = SafeHotspot.x;
1011 CurIcon->IconInfo.yHotspot = SafeHotspot.y;
1012
1013 Ret = TRUE;
1014 }
1015 else
1016 SetLastNtError(Status);
1017 }
1018
1019 if (!fIcon && !Hotspot)
1020 {
1021 Ret = TRUE;
1022 }
1023
1024 done:
1025 if(Ret)
1026 {
1027 /* This icon is shared now */
1028 GreSetObjectOwner(CurIcon->IconInfo.hbmMask, GDI_OBJ_HMGR_PUBLIC);
1029 if(CurIcon->IconInfo.hbmColor)
1030 {
1031 GreSetObjectOwner(CurIcon->IconInfo.hbmColor, GDI_OBJ_HMGR_PUBLIC);
1032 }
1033 }
1034 UserDereferenceObject(CurIcon);
1035 RETURN(Ret);
1036
1037
1038 CLEANUP:
1039 TRACE("Leave NtUserSetCursorIconData, ret=%i\n",_ret_);
1040 UserLeave();
1041 END_CLEANUP;
1042 }
1043 #endif
1044
1045 /* Mostly inspired from wine code */
1046 BOOL
1047 UserDrawIconEx(
1048 HDC hDc,
1049 INT xLeft,
1050 INT yTop,
1051 PCURICON_OBJECT pIcon,
1052 INT cxWidth,
1053 INT cyHeight,
1054 UINT istepIfAniCur,
1055 HBRUSH hbrFlickerFreeDraw,
1056 UINT diFlags)
1057 {
1058 BOOL Ret = FALSE;
1059 HBITMAP hbmMask, hbmColor;
1060 BITMAP bmpColor, bm;
1061 BOOL DoFlickerFree;
1062 INT iOldBkColor = 0, iOldTxtColor = 0;
1063
1064 HDC hMemDC, hDestDC = hDc;
1065 HGDIOBJ hOldOffBrush = 0;
1066 HGDIOBJ hOldOffBmp = 0;
1067 HBITMAP hTmpBmp = 0, hOffBmp = 0;
1068 BOOL bAlpha = FALSE;
1069 INT x=xLeft, y=yTop;
1070
1071 hbmMask = pIcon->IconInfo.hbmMask;
1072 hbmColor = pIcon->IconInfo.hbmColor;
1073
1074 if (istepIfAniCur)
1075 ERR("NtUserDrawIconEx: istepIfAniCur is not supported!\n");
1076
1077 if (!hbmMask || !GreGetObject(hbmMask, sizeof(BITMAP), (PVOID)&bm))
1078 {
1079 return FALSE;
1080 }
1081
1082 if (hbmColor && !GreGetObject(hbmColor, sizeof(BITMAP), (PVOID)&bmpColor))
1083 {
1084 return FALSE;
1085 }
1086
1087 if(!(hMemDC = NtGdiCreateCompatibleDC(hDc)))
1088 {
1089 ERR("NtGdiCreateCompatibleDC failed!\n");
1090 return FALSE;
1091 }
1092
1093 /* Check for alpha */
1094 if (hbmColor
1095 && (bmpColor.bmBitsPixel == 32)
1096 && (diFlags & DI_IMAGE))
1097 {
1098 SURFACE *psurfOff = NULL;
1099 PFN_DIB_GetPixel fnSource_GetPixel = NULL;
1100 INT i, j;
1101
1102 /* In order to correctly display 32 bit icons Windows first scans the image,
1103 because information about transparency is not stored in any image's headers */
1104 psurfOff = SURFACE_ShareLockSurface(hbmColor);
1105 if (psurfOff)
1106 {
1107 fnSource_GetPixel = DibFunctionsForBitmapFormat[psurfOff->SurfObj.iBitmapFormat].DIB_GetPixel;
1108 if (fnSource_GetPixel)
1109 {
1110 for (i = 0; i < psurfOff->SurfObj.sizlBitmap.cx; i++)
1111 {
1112 for (j = 0; j < psurfOff->SurfObj.sizlBitmap.cy; j++)
1113 {
1114 bAlpha = ((BYTE)(fnSource_GetPixel(&psurfOff->SurfObj, i, j) >> 24) & 0xff);
1115 if (bAlpha)
1116 break;
1117 }
1118 if (bAlpha)
1119 break;
1120 }
1121 }
1122 SURFACE_ShareUnlockSurface(psurfOff);
1123 }
1124 }
1125
1126 if (!cxWidth)
1127 cxWidth = ((diFlags & DI_DEFAULTSIZE) ?
1128 UserGetSystemMetrics(SM_CXICON) : pIcon->Size.cx);
1129
1130 if (!cyHeight)
1131 cyHeight = ((diFlags & DI_DEFAULTSIZE) ?
1132 UserGetSystemMetrics(SM_CYICON) : pIcon->Size.cy);
1133
1134 DoFlickerFree = (hbrFlickerFreeDraw &&
1135 (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw) == GDI_OBJECT_TYPE_BRUSH));
1136
1137 if (DoFlickerFree)
1138 {
1139 hDestDC = NtGdiCreateCompatibleDC(hDc);
1140 if(!hDestDC)
1141 {
1142 ERR("NtGdiCreateCompatibleDC failed!\n");
1143 Ret = FALSE;
1144 goto Cleanup ;
1145 }
1146 hOffBmp = NtGdiCreateCompatibleBitmap(hDc, cxWidth, cyHeight);
1147 if(!hOffBmp)
1148 {
1149 ERR("NtGdiCreateCompatibleBitmap failed!\n");
1150 goto Cleanup ;
1151 }
1152 hOldOffBmp = NtGdiSelectBitmap(hDestDC, hOffBmp);
1153 hOldOffBrush = NtGdiSelectBrush(hDestDC, hbrFlickerFreeDraw);
1154 NtGdiPatBlt(hDestDC, 0, 0, cxWidth, cyHeight, PATCOPY);
1155 NtGdiSelectBrush(hDestDC, hOldOffBrush);
1156 x=y=0;
1157 }
1158
1159 /* Set Background/foreground colors */
1160 iOldTxtColor = IntGdiSetTextColor(hDc, 0); //black
1161 iOldBkColor = IntGdiSetBkColor(hDc, 0x00FFFFFF); //white
1162
1163 if(bAlpha && (diFlags & DI_IMAGE))
1164 {
1165 BLENDFUNCTION pixelblend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
1166 BYTE Alpha;
1167 INT i, j;
1168 PSURFACE psurf;
1169 PBYTE ptr ;
1170 HBITMAP hMemBmp = NULL;
1171
1172 hMemBmp = BITMAP_CopyBitmap(hbmColor);
1173 if(!hMemBmp)
1174 {
1175 ERR("BITMAP_CopyBitmap failed!");
1176 goto CleanupAlpha;
1177 }
1178
1179 psurf = SURFACE_ShareLockSurface(hMemBmp);
1180 if(!psurf)
1181 {
1182 ERR("SURFACE_LockSurface failed!\n");
1183 goto CleanupAlpha;
1184 }
1185
1186 /* premultiply with the alpha channel value */
1187 for (i = 0; i < psurf->SurfObj.sizlBitmap.cy; i++)
1188 {
1189 ptr = (PBYTE)psurf->SurfObj.pvScan0 + i*psurf->SurfObj.lDelta;
1190 for (j = 0; j < psurf->SurfObj.sizlBitmap.cx; j++)
1191 {
1192 Alpha = ptr[3];
1193 ptr[0] = (ptr[0] * Alpha) / 0xff;
1194 ptr[1] = (ptr[1] * Alpha) / 0xff;
1195 ptr[2] = (ptr[2] * Alpha) / 0xff;
1196
1197 ptr += 4;
1198 }
1199 }
1200
1201 SURFACE_ShareUnlockSurface(psurf);
1202
1203 hTmpBmp = NtGdiSelectBitmap(hMemDC, hMemBmp);
1204
1205 Ret = NtGdiAlphaBlend(hDestDC,
1206 x,
1207 y,
1208 cxWidth,
1209 cyHeight,
1210 hMemDC,
1211 0,
1212 0,
1213 pIcon->Size.cx,
1214 pIcon->Size.cy,
1215 pixelblend,
1216 NULL);
1217 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1218 CleanupAlpha:
1219 if(hMemBmp) NtGdiDeleteObjectApp(hMemBmp);
1220 if(Ret) goto done;
1221 }
1222
1223 if (diFlags & DI_MASK)
1224 {
1225 hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmMask);
1226 NtGdiStretchBlt(hDestDC,
1227 x,
1228 y,
1229 cxWidth,
1230 cyHeight,
1231 hMemDC,
1232 0,
1233 0,
1234 pIcon->Size.cx,
1235 pIcon->Size.cy,
1236 SRCAND,
1237 0);
1238 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1239 }
1240
1241 if(diFlags & DI_IMAGE)
1242 {
1243 if (hbmColor)
1244 {
1245 DWORD rop = (diFlags & DI_MASK) ? SRCINVERT : SRCCOPY ;
1246 hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmColor);
1247 NtGdiStretchBlt(hDestDC,
1248 x,
1249 y,
1250 cxWidth,
1251 cyHeight,
1252 hMemDC,
1253 0,
1254 0,
1255 pIcon->Size.cx,
1256 pIcon->Size.cy,
1257 rop,
1258 0);
1259 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1260 }
1261 else
1262 {
1263 /* Mask bitmap holds the information in its second half */
1264 DWORD rop = (diFlags & DI_MASK) ? SRCINVERT : SRCCOPY ;
1265 hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmMask);
1266 NtGdiStretchBlt(hDestDC,
1267 x,
1268 y,
1269 cxWidth,
1270 cyHeight,
1271 hMemDC,
1272 0,
1273 pIcon->Size.cy,
1274 pIcon->Size.cx,
1275 pIcon->Size.cy,
1276 rop,
1277 0);
1278 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1279 }
1280 }
1281
1282 done:
1283 if(hDestDC != hDc)
1284 {
1285 NtGdiBitBlt(hDc, xLeft, yTop, cxWidth, cyHeight, hDestDC, 0, 0, SRCCOPY, 0, 0);
1286 }
1287
1288 /* Restore foreground and background colors */
1289 IntGdiSetBkColor(hDc, iOldBkColor);
1290 IntGdiSetTextColor(hDc, iOldTxtColor);
1291
1292 Ret = TRUE ;
1293
1294 Cleanup:
1295 NtGdiDeleteObjectApp(hMemDC);
1296 if(hDestDC != hDc)
1297 {
1298 if(hOldOffBmp) NtGdiSelectBitmap(hDestDC, hOldOffBmp);
1299 NtGdiDeleteObjectApp(hDestDC);
1300 if(hOffBmp) NtGdiDeleteObjectApp(hOffBmp);
1301 }
1302
1303 return Ret;
1304 }
1305
1306 /*
1307 * @implemented
1308 */
1309 BOOL
1310 APIENTRY
1311 NtUserDrawIconEx(
1312 HDC hdc,
1313 int xLeft,
1314 int yTop,
1315 HICON hIcon,
1316 int cxWidth,
1317 int cyHeight,
1318 UINT istepIfAniCur,
1319 HBRUSH hbrFlickerFreeDraw,
1320 UINT diFlags,
1321 BOOL bMetaHDC, // When TRUE, GDI functions need to be handled in User32!
1322 PVOID pDIXData)
1323 {
1324 PCURICON_OBJECT pIcon;
1325 BOOL Ret;
1326
1327 TRACE("Enter NtUserDrawIconEx\n");
1328 UserEnterExclusive();
1329
1330 if (!(pIcon = UserGetCurIconObject(hIcon)))
1331 {
1332 ERR("UserGetCurIconObject() failed!\n");
1333 UserLeave();
1334 return FALSE;
1335 }
1336
1337 Ret = UserDrawIconEx(hdc,
1338 xLeft,
1339 yTop,
1340 pIcon,
1341 cxWidth,
1342 cyHeight,
1343 istepIfAniCur,
1344 hbrFlickerFreeDraw,
1345 diFlags);
1346
1347 UserDereferenceObject(pIcon);
1348
1349 UserLeave();
1350 return Ret;
1351 }
1352