a2ee81234a8c8b227d75849191948100c6f11454
[reactos.git] / reactos / 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 EngSetLastError(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 EngSetLastError(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 co_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 EngSetLastError(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 EngSetLastError(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 * @implemented
623 */
624 BOOL
625 APIENTRY
626 NtUserGetCursorInfo(
627 PCURSORINFO pci)
628 {
629 CURSORINFO SafeCi;
630 PSYSTEM_CURSORINFO CurInfo;
631 NTSTATUS Status = STATUS_SUCCESS;
632 PCURICON_OBJECT CurIcon;
633 BOOL Ret = FALSE;
634 DECLARE_RETURN(BOOL);
635
636 DPRINT("Enter NtUserGetCursorInfo\n");
637 UserEnterExclusive();
638
639 CurInfo = IntGetSysCursorInfo();
640 CurIcon = (PCURICON_OBJECT)CurInfo->CurrentCursorObject;
641
642 SafeCi.cbSize = sizeof(CURSORINFO);
643 SafeCi.flags = ((CurInfo->ShowingCursor && CurIcon) ? CURSOR_SHOWING : 0);
644 SafeCi.hCursor = (CurIcon ? (HCURSOR)CurIcon->Self : (HCURSOR)0);
645
646 SafeCi.ptScreenPos = gpsi->ptCursor;
647
648 _SEH2_TRY
649 {
650 if (pci->cbSize == sizeof(CURSORINFO))
651 {
652 ProbeForWrite(pci, sizeof(CURSORINFO), 1);
653 RtlCopyMemory(pci, &SafeCi, sizeof(CURSORINFO));
654 Ret = TRUE;
655 }
656 else
657 {
658 EngSetLastError(ERROR_INVALID_PARAMETER);
659 }
660 }
661 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
662 {
663 Status = _SEH2_GetExceptionCode();
664 }
665 _SEH2_END;
666 if (!NT_SUCCESS(Status))
667 {
668 SetLastNtError(Status);
669 }
670
671 RETURN(Ret);
672
673 CLEANUP:
674 DPRINT("Leave NtUserGetCursorInfo, ret=%i\n",_ret_);
675 UserLeave();
676 END_CLEANUP;
677 }
678
679 BOOL
680 APIENTRY
681 UserClipCursor(
682 RECTL *prcl)
683 {
684 /* FIXME - check if process has WINSTA_WRITEATTRIBUTES */
685 PSYSTEM_CURSORINFO CurInfo;
686 PWND DesktopWindow = NULL;
687
688 CurInfo = IntGetSysCursorInfo();
689
690 DesktopWindow = UserGetDesktopWindow();
691
692 if (prcl != NULL &&
693 (prcl->right > prcl->left) &&
694 (prcl->bottom > prcl->top) &&
695 DesktopWindow != NULL)
696 {
697 CurInfo->bClipped = TRUE;
698 RECTL_bIntersectRect(&CurInfo->rcClip, prcl, &DesktopWindow->rcWindow);
699 UserSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y, FALSE);
700 }
701 else
702 {
703 CurInfo->bClipped = FALSE;
704 }
705
706 return TRUE;
707 }
708
709 /*
710 * @implemented
711 */
712 BOOL
713 APIENTRY
714 NtUserClipCursor(
715 RECTL *prcl)
716 {
717 RECTL rclLocal;
718 BOOL bResult;
719
720 if (prcl)
721 {
722 _SEH2_TRY
723 {
724 /* Probe and copy rect */
725 ProbeForRead(prcl, sizeof(RECTL), 1);
726 rclLocal = *prcl;
727 }
728 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
729 {
730 EngSetLastError(ERROR_INVALID_PARAMETER);
731 _SEH2_YIELD(return FALSE;)
732 }
733 _SEH2_END
734
735 prcl = &rclLocal;
736 }
737
738 UserEnterExclusive();
739
740 /* Call the internal function */
741 bResult = UserClipCursor(prcl);
742
743 UserLeave();
744
745 return bResult;
746 }
747
748
749 /*
750 * @implemented
751 */
752 BOOL
753 APIENTRY
754 NtUserDestroyCursor(
755 HANDLE hCurIcon,
756 DWORD Unknown)
757 {
758 PCURICON_OBJECT CurIcon;
759 BOOL ret;
760 DECLARE_RETURN(BOOL);
761
762 DPRINT("Enter NtUserDestroyCursorIcon\n");
763 UserEnterExclusive();
764
765 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
766 {
767 RETURN(FALSE);
768 }
769
770 ret = IntDestroyCurIconObject(CurIcon, FALSE);
771 /* Note: IntDestroyCurIconObject will remove our reference for us! */
772
773 RETURN(ret);
774
775 CLEANUP:
776 DPRINT("Leave NtUserDestroyCursorIcon, ret=%i\n",_ret_);
777 UserLeave();
778 END_CLEANUP;
779 }
780
781
782 /*
783 * @implemented
784 */
785 HICON
786 APIENTRY
787 NtUserFindExistingCursorIcon(
788 HMODULE hModule,
789 HRSRC hRsrc,
790 LONG cx,
791 LONG cy)
792 {
793 PCURICON_OBJECT CurIcon;
794 HANDLE Ret = (HANDLE)0;
795 DECLARE_RETURN(HICON);
796
797 DPRINT("Enter NtUserFindExistingCursorIcon\n");
798 UserEnterExclusive();
799
800 CurIcon = IntFindExistingCurIconObject(hModule, hRsrc, cx, cy);
801 if (CurIcon)
802 {
803 Ret = CurIcon->Self;
804
805 // IntReleaseCurIconObject(CurIcon);//faxme: is this correct? does IntFindExistingCurIconObject add a ref?
806 RETURN(Ret);
807 }
808
809 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
810 RETURN((HANDLE)0);
811
812 CLEANUP:
813 DPRINT("Leave NtUserFindExistingCursorIcon, ret=%i\n",_ret_);
814 UserLeave();
815 END_CLEANUP;
816 }
817
818
819 /*
820 * @implemented
821 */
822 BOOL
823 APIENTRY
824 NtUserGetClipCursor(
825 RECTL *lpRect)
826 {
827 /* FIXME - check if process has WINSTA_READATTRIBUTES */
828 PSYSTEM_CURSORINFO CurInfo;
829 RECTL Rect;
830 NTSTATUS Status;
831 DECLARE_RETURN(BOOL);
832
833 DPRINT("Enter NtUserGetClipCursor\n");
834 UserEnterExclusive();
835
836 if (!lpRect)
837 RETURN(FALSE);
838
839 CurInfo = IntGetSysCursorInfo();
840 if (CurInfo->bClipped)
841 {
842 Rect = CurInfo->rcClip;
843 }
844 else
845 {
846 Rect.left = 0;
847 Rect.top = 0;
848 Rect.right = UserGetSystemMetrics(SM_CXSCREEN);
849 Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
850 }
851
852 Status = MmCopyToCaller(lpRect, &Rect, sizeof(RECT));
853 if (!NT_SUCCESS(Status))
854 {
855 SetLastNtError(Status);
856 RETURN(FALSE);
857 }
858
859 RETURN(TRUE);
860
861 CLEANUP:
862 DPRINT("Leave NtUserGetClipCursor, ret=%i\n",_ret_);
863 UserLeave();
864 END_CLEANUP;
865 }
866
867
868 /*
869 * @implemented
870 */
871 HCURSOR
872 APIENTRY
873 NtUserSetCursor(
874 HCURSOR hCursor)
875 {
876 PCURICON_OBJECT CurIcon;
877 HICON OldCursor;
878 DECLARE_RETURN(HCURSOR);
879
880 DPRINT("Enter NtUserSetCursor\n");
881 UserEnterExclusive();
882
883 if (hCursor)
884 {
885 if (!(CurIcon = UserGetCurIconObject(hCursor)))
886 {
887 RETURN(NULL);
888 }
889 }
890 else
891 {
892 CurIcon = NULL;
893 }
894
895 OldCursor = UserSetCursor(CurIcon, FALSE);
896
897 if (CurIcon)
898 {
899 UserDereferenceObject(CurIcon);
900 }
901
902 RETURN(OldCursor);
903
904 CLEANUP:
905 DPRINT("Leave NtUserSetCursor, ret=%i\n",_ret_);
906 UserLeave();
907 END_CLEANUP;
908 }
909
910
911 /*
912 * @implemented
913 */
914 BOOL
915 APIENTRY
916 NtUserSetCursorContents(
917 HANDLE hCurIcon,
918 PICONINFO UnsafeIconInfo)
919 {
920 PCURICON_OBJECT CurIcon;
921 ICONINFO IconInfo;
922 PSURFACE psurfBmp;
923 NTSTATUS Status;
924 BOOL Ret = FALSE;
925 DECLARE_RETURN(BOOL);
926
927 DPRINT("Enter NtUserSetCursorContents\n");
928 UserEnterExclusive();
929
930 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
931 {
932 RETURN(FALSE);
933 }
934
935 /* Copy fields */
936 Status = MmCopyFromCaller(&IconInfo, UnsafeIconInfo, sizeof(ICONINFO));
937 if (!NT_SUCCESS(Status))
938 {
939 SetLastNtError(Status);
940 goto done;
941 }
942
943 /* Delete old bitmaps */
944 if ((CurIcon->IconInfo.hbmColor)
945 && (CurIcon->IconInfo.hbmColor != IconInfo.hbmColor))
946 {
947 GreDeleteObject(CurIcon->IconInfo.hbmColor);
948 }
949 if ((CurIcon->IconInfo.hbmMask)
950 && CurIcon->IconInfo.hbmMask != IconInfo.hbmMask)
951 {
952 GreDeleteObject(CurIcon->IconInfo.hbmMask);
953 }
954
955 /* Copy new IconInfo field */
956 CurIcon->IconInfo = IconInfo;
957
958 psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmColor);
959 if (psurfBmp)
960 {
961 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
962 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
963 SURFACE_UnlockSurface(psurfBmp);
964 GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmColor, NULL);
965 }
966 else
967 {
968 psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmMask);
969 if (!psurfBmp)
970 goto done;
971
972 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
973 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy / 2;
974
975 SURFACE_UnlockSurface(psurfBmp);
976 }
977 GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmMask, NULL);
978
979 Ret = TRUE;
980
981 done:
982
983 if (CurIcon)
984 {
985 UserDereferenceObject(CurIcon);
986 }
987 RETURN(Ret);
988
989 CLEANUP:
990 DPRINT("Leave NtUserSetCursorContents, ret=%i\n",_ret_);
991 UserLeave();
992 END_CLEANUP;
993 }
994
995
996 /*
997 * @implemented
998 */
999 #if 0
1000 BOOL
1001 APIENTRY
1002 NtUserSetCursorIconData(
1003 HANDLE Handle,
1004 HMODULE hModule,
1005 PUNICODE_STRING pstrResName,
1006 PICONINFO pIconInfo)
1007 {
1008 PCURICON_OBJECT CurIcon;
1009 PSURFACE psurfBmp;
1010 NTSTATUS Status = STATUS_SUCCESS;
1011 BOOL Ret = FALSE;
1012 DECLARE_RETURN(BOOL);
1013
1014 DPRINT("Enter NtUserSetCursorIconData\n");
1015 UserEnterExclusive();
1016
1017 if (!(CurIcon = UserGetCurIconObject(Handle)))
1018 {
1019 RETURN(FALSE);
1020 }
1021
1022 CurIcon->hModule = hModule;
1023 CurIcon->hRsrc = NULL; //hRsrc;
1024 CurIcon->hGroupRsrc = NULL; //hGroupRsrc;
1025
1026 _SEH2_TRY
1027 {
1028 ProbeForRead(pIconInfo, sizeof(ICONINFO), 1);
1029 RtlCopyMemory(&CurIcon->IconInfo, pIconInfo, sizeof(ICONINFO));
1030
1031 CurIcon->IconInfo.hbmMask = BITMAP_CopyBitmap(pIconInfo->hbmMask);
1032 CurIcon->IconInfo.hbmColor = BITMAP_CopyBitmap(pIconInfo->hbmColor);
1033
1034 if (CurIcon->IconInfo.hbmColor)
1035 {
1036 if ((psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmColor)))
1037 {
1038 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
1039 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
1040 SURFACE_UnlockSurface(psurfBmp);
1041 GDIOBJ_SetOwnership(GdiHandleTable, CurIcon->IconInfo.hbmMask, NULL);
1042 }
1043 }
1044 if (CurIcon->IconInfo.hbmMask)
1045 {
1046 if (CurIcon->IconInfo.hbmColor == NULL)
1047 {
1048 if ((psurfBmp = SURFACE_LockSurface(CurIcon->IconInfo.hbmMask)))
1049 {
1050 CurIcon->Size.cx = psurfBmp->SurfObj.sizlBitmap.cx;
1051 CurIcon->Size.cy = psurfBmp->SurfObj.sizlBitmap.cy;
1052 SURFACE_UnlockSurface(psurfBmp);
1053 }
1054 }
1055 GDIOBJ_SetOwnership(GdiHandleTable, CurIcon->IconInfo.hbmMask, NULL);
1056 }
1057 }
1058 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1059 {
1060 Status = _SEH2_GetExceptionCode();
1061 }
1062 _SEH2_END
1063
1064 if (!NT_SUCCESS(Status))
1065 SetLastNtError(Status);
1066 else
1067 Ret = TRUE;
1068
1069 UserDereferenceObject(CurIcon);
1070 RETURN(Ret);
1071
1072 CLEANUP:
1073 DPRINT("Leave NtUserSetCursorIconData, ret=%i\n",_ret_);
1074 UserLeave();
1075 END_CLEANUP;
1076 }
1077 #else
1078 BOOL
1079 APIENTRY
1080 NtUserSetCursorIconData(
1081 HANDLE hCurIcon,
1082 PBOOL fIcon,
1083 POINT *Hotspot,
1084 HMODULE hModule,
1085 HRSRC hRsrc,
1086 HRSRC hGroupRsrc)
1087 {
1088 PCURICON_OBJECT CurIcon;
1089 NTSTATUS Status;
1090 POINT SafeHotspot;
1091 BOOL Ret = FALSE;
1092 DECLARE_RETURN(BOOL);
1093
1094 DPRINT("Enter NtUserSetCursorIconData\n");
1095 UserEnterExclusive();
1096
1097 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
1098 {
1099 RETURN(FALSE);
1100 }
1101
1102 CurIcon->hModule = hModule;
1103 CurIcon->hRsrc = hRsrc;
1104 CurIcon->hGroupRsrc = hGroupRsrc;
1105
1106 /* Copy fields */
1107 if (fIcon)
1108 {
1109 Status = MmCopyFromCaller(&CurIcon->IconInfo.fIcon, fIcon, sizeof(BOOL));
1110 if (!NT_SUCCESS(Status))
1111 {
1112 SetLastNtError(Status);
1113 goto done;
1114 }
1115 }
1116 else
1117 {
1118 if (!Hotspot)
1119 Ret = TRUE;
1120 }
1121
1122 if (Hotspot)
1123 {
1124 Status = MmCopyFromCaller(&SafeHotspot, Hotspot, sizeof(POINT));
1125 if (NT_SUCCESS(Status))
1126 {
1127 CurIcon->IconInfo.xHotspot = SafeHotspot.x;
1128 CurIcon->IconInfo.yHotspot = SafeHotspot.y;
1129
1130 Ret = TRUE;
1131 }
1132 else
1133 SetLastNtError(Status);
1134 }
1135
1136 if (!fIcon && !Hotspot)
1137 {
1138 Ret = TRUE;
1139 }
1140
1141 done:
1142 if(Ret)
1143 {
1144 /* This icon is shared now */
1145 GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmMask, NULL);
1146 if(CurIcon->IconInfo.hbmColor)
1147 {
1148 GDIOBJ_SetOwnership(CurIcon->IconInfo.hbmColor, NULL);
1149 }
1150 }
1151 UserDereferenceObject(CurIcon);
1152 RETURN(Ret);
1153
1154
1155 CLEANUP:
1156 DPRINT("Leave NtUserSetCursorIconData, ret=%i\n",_ret_);
1157 UserLeave();
1158 END_CLEANUP;
1159 }
1160 #endif
1161
1162 /* Mostly inspired from wine code */
1163 BOOL
1164 UserDrawIconEx(
1165 HDC hDc,
1166 INT xLeft,
1167 INT yTop,
1168 PCURICON_OBJECT pIcon,
1169 INT cxWidth,
1170 INT cyHeight,
1171 UINT istepIfAniCur,
1172 HBRUSH hbrFlickerFreeDraw,
1173 UINT diFlags)
1174 {
1175 BOOL Ret = FALSE;
1176 HBITMAP hbmMask, hbmColor;
1177 BITMAP bmpColor, bm;
1178 BOOL DoFlickerFree;
1179 INT iOldBkColor = 0, iOldTxtColor = 0;
1180
1181 HDC hMemDC, hDestDC = hDc;
1182 HGDIOBJ hOldOffBrush = 0;
1183 HGDIOBJ hOldOffBmp = 0;
1184 HBITMAP hTmpBmp = 0, hOffBmp = 0;
1185 BOOL bAlpha = FALSE;
1186 INT x=xLeft, y=yTop;
1187
1188 hbmMask = pIcon->IconInfo.hbmMask;
1189 hbmColor = pIcon->IconInfo.hbmColor;
1190
1191 if (istepIfAniCur)
1192 DPRINT1("NtUserDrawIconEx: istepIfAniCur is not supported!\n");
1193
1194 if (!hbmMask || !IntGdiGetObject(hbmMask, sizeof(BITMAP), (PVOID)&bm))
1195 {
1196 return FALSE;
1197 }
1198
1199 if (hbmColor && !IntGdiGetObject(hbmColor, sizeof(BITMAP), (PVOID)&bmpColor))
1200 {
1201 return FALSE;
1202 }
1203
1204 if(!(hMemDC = NtGdiCreateCompatibleDC(hDc)))
1205 {
1206 DPRINT1("NtGdiCreateCompatibleDC failed!\n");
1207 return FALSE;
1208 }
1209
1210 /* Check for alpha */
1211 if (hbmColor
1212 && (bmpColor.bmBitsPixel == 32)
1213 && (diFlags & DI_IMAGE))
1214 {
1215 SURFACE *psurfOff = NULL;
1216 PFN_DIB_GetPixel fnSource_GetPixel = NULL;
1217 INT i, j;
1218
1219 /* In order to correctly display 32 bit icons Windows first scans the image,
1220 because information about transparency is not stored in any image's headers */
1221 psurfOff = SURFACE_LockSurface(hbmColor);
1222 if (psurfOff)
1223 {
1224 fnSource_GetPixel = DibFunctionsForBitmapFormat[psurfOff->SurfObj.iBitmapFormat].DIB_GetPixel;
1225 if (fnSource_GetPixel)
1226 {
1227 for (i = 0; i < psurfOff->SurfObj.sizlBitmap.cx; i++)
1228 {
1229 for (j = 0; j < psurfOff->SurfObj.sizlBitmap.cy; j++)
1230 {
1231 bAlpha = ((BYTE)(fnSource_GetPixel(&psurfOff->SurfObj, i, j) >> 24) & 0xff);
1232 if (bAlpha)
1233 break;
1234 }
1235 if (bAlpha)
1236 break;
1237 }
1238 }
1239 SURFACE_UnlockSurface(psurfOff);
1240 }
1241 }
1242
1243 if (!cxWidth)
1244 cxWidth = ((diFlags & DI_DEFAULTSIZE) ?
1245 UserGetSystemMetrics(SM_CXICON) : pIcon->Size.cx);
1246
1247 if (!cyHeight)
1248 cyHeight = ((diFlags & DI_DEFAULTSIZE) ?
1249 UserGetSystemMetrics(SM_CYICON) : pIcon->Size.cy);
1250
1251 DoFlickerFree = (hbrFlickerFreeDraw &&
1252 (GDI_HANDLE_GET_TYPE(hbrFlickerFreeDraw) == GDI_OBJECT_TYPE_BRUSH));
1253
1254 if (DoFlickerFree)
1255 {
1256 hDestDC = NtGdiCreateCompatibleDC(hDc);
1257 if(!hDestDC)
1258 {
1259 DPRINT1("NtGdiCreateCompatibleDC failed!\n");
1260 Ret = FALSE;
1261 goto Cleanup ;
1262 }
1263 hOffBmp = NtGdiCreateCompatibleBitmap(hDc, cxWidth, cyHeight);
1264 if(!hOffBmp)
1265 {
1266 DPRINT1("NtGdiCreateCompatibleBitmap failed!\n");
1267 goto Cleanup ;
1268 }
1269 hOldOffBmp = NtGdiSelectBitmap(hDestDC, hOffBmp);
1270 hOldOffBrush = NtGdiSelectBrush(hDestDC, hbrFlickerFreeDraw);
1271 NtGdiPatBlt(hDestDC, 0, 0, cxWidth, cyHeight, PATCOPY);
1272 NtGdiSelectBrush(hDestDC, hOldOffBrush);
1273 x=y=0;
1274 }
1275
1276 /* Set Background/foreground colors */
1277 iOldTxtColor = IntGdiSetTextColor(hDc, 0); //black
1278 iOldBkColor = IntGdiSetBkColor(hDc, 0x00FFFFFF); //white
1279
1280 if(bAlpha && (diFlags & DI_IMAGE))
1281 {
1282 BLENDFUNCTION pixelblend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
1283 BYTE Alpha;
1284 INT i, j;
1285 PSURFACE psurf;
1286 PBYTE ptr ;
1287 HBITMAP hMemBmp = NULL;
1288
1289 hMemBmp = BITMAP_CopyBitmap(hbmColor);
1290 if(!hMemBmp)
1291 {
1292 DPRINT1("BITMAP_CopyBitmap failed!");
1293 goto CleanupAlpha;
1294 }
1295
1296 psurf = SURFACE_LockSurface(hMemBmp);
1297 if(!psurf)
1298 {
1299 DPRINT1("SURFACE_LockSurface failed!\n");
1300 goto CleanupAlpha;
1301 }
1302
1303 /* premultiply with the alpha channel value */
1304 for (i = 0; i < psurf->SurfObj.sizlBitmap.cy; i++)
1305 {
1306 ptr = (PBYTE)psurf->SurfObj.pvScan0 + i*psurf->SurfObj.lDelta;
1307 for (j = 0; j < psurf->SurfObj.sizlBitmap.cx; j++)
1308 {
1309 Alpha = ptr[3];
1310 ptr[0] = (ptr[0] * Alpha) / 0xff;
1311 ptr[1] = (ptr[1] * Alpha) / 0xff;
1312 ptr[2] = (ptr[2] * Alpha) / 0xff;
1313
1314 ptr += 4;
1315 }
1316 }
1317
1318 SURFACE_UnlockSurface(psurf);
1319
1320 hTmpBmp = NtGdiSelectBitmap(hMemDC, hMemBmp);
1321
1322 Ret = NtGdiAlphaBlend(hDestDC,
1323 x,
1324 y,
1325 cxWidth,
1326 cyHeight,
1327 hMemDC,
1328 0,
1329 0,
1330 pIcon->Size.cx,
1331 pIcon->Size.cy,
1332 pixelblend,
1333 NULL);
1334 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1335 CleanupAlpha:
1336 if(hMemBmp) NtGdiDeleteObjectApp(hMemBmp);
1337 if(Ret) goto done;
1338 }
1339
1340 if (diFlags & DI_MASK)
1341 {
1342 hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmMask);
1343 NtGdiStretchBlt(hDestDC,
1344 x,
1345 y,
1346 cxWidth,
1347 cyHeight,
1348 hMemDC,
1349 0,
1350 0,
1351 pIcon->Size.cx,
1352 pIcon->Size.cy,
1353 SRCAND,
1354 0);
1355 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1356 }
1357
1358 if(diFlags & DI_IMAGE)
1359 {
1360 if (hbmColor)
1361 {
1362 DWORD rop = (diFlags & DI_MASK) ? SRCINVERT : SRCCOPY ;
1363 hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmColor);
1364 NtGdiStretchBlt(hDestDC,
1365 x,
1366 y,
1367 cxWidth,
1368 cyHeight,
1369 hMemDC,
1370 0,
1371 0,
1372 pIcon->Size.cx,
1373 pIcon->Size.cy,
1374 rop,
1375 0);
1376 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1377 }
1378 else
1379 {
1380 /* Mask bitmap holds the information in its second half */
1381 DWORD rop = (diFlags & DI_MASK) ? SRCINVERT : SRCCOPY ;
1382 hTmpBmp = NtGdiSelectBitmap(hMemDC, hbmMask);
1383 NtGdiStretchBlt(hDestDC,
1384 x,
1385 y,
1386 cxWidth,
1387 cyHeight,
1388 hMemDC,
1389 0,
1390 pIcon->Size.cy,
1391 pIcon->Size.cx,
1392 pIcon->Size.cy,
1393 rop,
1394 0);
1395 NtGdiSelectBitmap(hMemDC, hTmpBmp);
1396 }
1397 }
1398
1399 done:
1400 if(hDestDC != hDc)
1401 {
1402 NtGdiBitBlt(hDc, xLeft, yTop, cxWidth, cyHeight, hDestDC, 0, 0, SRCCOPY, 0, 0);
1403 }
1404
1405 /* Restore foreground and background colors */
1406 IntGdiSetBkColor(hDc, iOldBkColor);
1407 IntGdiSetTextColor(hDc, iOldTxtColor);
1408
1409 Ret = TRUE ;
1410
1411 Cleanup:
1412 NtGdiDeleteObjectApp(hMemDC);
1413 if(hDestDC != hDc)
1414 {
1415 if(hOldOffBmp) NtGdiSelectBitmap(hDestDC, hOldOffBmp);
1416 NtGdiDeleteObjectApp(hDestDC);
1417 if(hOffBmp) NtGdiDeleteObjectApp(hOffBmp);
1418 }
1419
1420 return Ret;
1421 }
1422
1423 /*
1424 * @implemented
1425 */
1426 BOOL
1427 APIENTRY
1428 NtUserDrawIconEx(
1429 HDC hdc,
1430 int xLeft,
1431 int yTop,
1432 HICON hIcon,
1433 int cxWidth,
1434 int cyHeight,
1435 UINT istepIfAniCur,
1436 HBRUSH hbrFlickerFreeDraw,
1437 UINT diFlags,
1438 BOOL bMetaHDC, // When TRUE, GDI functions need to be handled in User32!
1439 PVOID pDIXData)
1440 {
1441 PCURICON_OBJECT pIcon;
1442 BOOL Ret;
1443
1444 DPRINT("Enter NtUserDrawIconEx\n");
1445 UserEnterExclusive();
1446
1447 if (!(pIcon = UserGetCurIconObject(hIcon)))
1448 {
1449 DPRINT1("UserGetCurIconObject() failed!\n");
1450 UserLeave();
1451 return FALSE;
1452 }
1453
1454 Ret = UserDrawIconEx(hdc,
1455 xLeft,
1456 yTop,
1457 pIcon,
1458 cxWidth,
1459 cyHeight,
1460 istepIfAniCur,
1461 hbrFlickerFreeDraw,
1462 diFlags);
1463
1464 UserDereferenceObject(pIcon);
1465
1466 UserLeave();
1467 return Ret;
1468 }
1469