[WIN32K]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / desktop.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Desktops
5 * FILE: subsystems/win32/win32k/ntuser/desktop.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISION HISTORY:
8 * 06-06-2001 CSH Created
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <win32k.h>
14
15 #define NDEBUG
16 #include <debug.h>
17 #define TRACE DPRINT
18 #define WARN DPRINT1
19 #define ERR DPRINT1
20
21 static
22 VOID
23 IntFreeDesktopHeap(
24 IN OUT PDESKTOP Desktop
25 );
26
27 /* GLOBALS *******************************************************************/
28
29 /* Currently active desktop */
30 PDESKTOP InputDesktop = NULL;
31 HDESK InputDesktopHandle = NULL;
32 HDC ScreenDeviceContext = NULL;
33 BOOL g_PaintDesktopVersion = FALSE;
34
35 GENERIC_MAPPING IntDesktopMapping =
36 {
37 STANDARD_RIGHTS_READ | DESKTOP_ENUMERATE |
38 DESKTOP_READOBJECTS,
39 STANDARD_RIGHTS_WRITE | DESKTOP_CREATEMENU |
40 DESKTOP_CREATEWINDOW |
41 DESKTOP_HOOKCONTROL |
42 DESKTOP_JOURNALPLAYBACK |
43 DESKTOP_JOURNALRECORD |
44 DESKTOP_WRITEOBJECTS,
45 STANDARD_RIGHTS_EXECUTE | DESKTOP_SWITCHDESKTOP,
46 STANDARD_RIGHTS_REQUIRED | DESKTOP_CREATEMENU |
47 DESKTOP_CREATEWINDOW |
48 DESKTOP_ENUMERATE |
49 DESKTOP_HOOKCONTROL |
50 DESKTOP_JOURNALPLAYBACK |
51 DESKTOP_JOURNALRECORD |
52 DESKTOP_READOBJECTS |
53 DESKTOP_SWITCHDESKTOP |
54 DESKTOP_WRITEOBJECTS
55 };
56
57 /* OBJECT CALLBACKS **********************************************************/
58
59 NTSTATUS
60 APIENTRY
61 IntDesktopObjectParse(IN PVOID ParseObject,
62 IN PVOID ObjectType,
63 IN OUT PACCESS_STATE AccessState,
64 IN KPROCESSOR_MODE AccessMode,
65 IN ULONG Attributes,
66 IN OUT PUNICODE_STRING CompleteName,
67 IN OUT PUNICODE_STRING RemainingName,
68 IN OUT PVOID Context OPTIONAL,
69 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
70 OUT PVOID *Object)
71 {
72 NTSTATUS Status;
73 PDESKTOP Desktop;
74 OBJECT_ATTRIBUTES ObjectAttributes;
75 PLIST_ENTRY NextEntry, ListHead;
76 PWINSTATION_OBJECT WinStaObject = (PWINSTATION_OBJECT)ParseObject;
77 PUNICODE_STRING DesktopName;
78
79 /* Set the list pointers and loop the window station */
80 ListHead = &WinStaObject->DesktopListHead;
81 NextEntry = ListHead->Flink;
82 while (NextEntry != ListHead)
83 {
84 /* Get the current desktop */
85 Desktop = CONTAINING_RECORD(NextEntry, DESKTOP, ListEntry);
86
87 /* Get its name */
88 DesktopName = GET_DESKTOP_NAME(Desktop);
89 if (DesktopName)
90 {
91 /* Compare the name */
92 if (RtlEqualUnicodeString(RemainingName,
93 DesktopName,
94 (Attributes & OBJ_CASE_INSENSITIVE)))
95 {
96 /* We found a match. Did this come from a create? */
97 if (Context)
98 {
99 /* Unless OPEN_IF was given, fail with an error */
100 if (!(Attributes & OBJ_OPENIF))
101 {
102 /* Name collision */
103 return STATUS_OBJECT_NAME_COLLISION;
104 }
105 else
106 {
107 /* Otherwise, return with a warning only */
108 Status = STATUS_OBJECT_NAME_EXISTS;
109 }
110 }
111 else
112 {
113 /* This was a real open, so this is OK */
114 Status = STATUS_SUCCESS;
115 }
116
117 /* Reference the desktop and return it */
118 ObReferenceObject(Desktop);
119 *Object = Desktop;
120 return Status;
121 }
122 }
123
124 /* Go to the next desktop */
125 NextEntry = NextEntry->Flink;
126 }
127
128 /* If we got here but this isn't a create, then fail */
129 if (!Context) return STATUS_OBJECT_NAME_NOT_FOUND;
130
131 /* Create the desktop object */
132 InitializeObjectAttributes(&ObjectAttributes, RemainingName, 0, NULL, NULL);
133 Status = ObCreateObject(KernelMode,
134 ExDesktopObjectType,
135 &ObjectAttributes,
136 KernelMode,
137 NULL,
138 sizeof(DESKTOP),
139 0,
140 0,
141 (PVOID)&Desktop);
142 if (!NT_SUCCESS(Status)) return Status;
143
144 /* Initialize shell hook window list and set the parent */
145 RtlZeroMemory(Desktop, sizeof(DESKTOP));
146 InitializeListHead(&Desktop->ShellHookWindows);
147 Desktop->rpwinstaParent = (PWINSTATION_OBJECT)ParseObject;
148
149 /* Put the desktop on the window station's list of associated desktops */
150 InsertTailList(&Desktop->rpwinstaParent->DesktopListHead,
151 &Desktop->ListEntry);
152
153 /* Set the desktop object and return success */
154 *Object = Desktop;
155 return STATUS_SUCCESS;
156 }
157
158 VOID APIENTRY
159 IntDesktopObjectDelete(PWIN32_DELETEMETHOD_PARAMETERS Parameters)
160 {
161 PDESKTOP Desktop = (PDESKTOP)Parameters->Object;
162
163 DPRINT("Deleting desktop (0x%X)\n", Desktop);
164
165 /* Remove the desktop from the window station's list of associcated desktops */
166 RemoveEntryList(&Desktop->ListEntry);
167
168 IntFreeDesktopHeap(Desktop);
169 }
170
171 /* PRIVATE FUNCTIONS **********************************************************/
172
173 INIT_FUNCTION
174 NTSTATUS
175 NTAPI
176 InitDesktopImpl(VOID)
177 {
178 /* Set Desktop Object Attributes */
179 ExDesktopObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(DESKTOP);
180 ExDesktopObjectType->TypeInfo.GenericMapping = IntDesktopMapping;
181 return STATUS_SUCCESS;
182 }
183
184 NTSTATUS
185 FASTCALL
186 CleanupDesktopImpl(VOID)
187 {
188 return STATUS_SUCCESS;
189 }
190
191 static int GetSystemVersionString(LPWSTR buffer)
192 {
193 RTL_OSVERSIONINFOEXW versionInfo;
194 int len;
195
196 versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
197
198 if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo)))
199 return 0;
200
201 if (versionInfo.dwMajorVersion <= 4)
202 len = swprintf(buffer,
203 L"ReactOS Version %d.%d %s Build %d",
204 versionInfo.dwMajorVersion, versionInfo.dwMinorVersion,
205 versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
206 else
207 len = swprintf(buffer,
208 L"ReactOS %s (Build %d)",
209 versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
210
211 return len;
212 }
213
214
215 NTSTATUS FASTCALL
216 IntParseDesktopPath(PEPROCESS Process,
217 PUNICODE_STRING DesktopPath,
218 HWINSTA *hWinSta,
219 HDESK *hDesktop)
220 {
221 OBJECT_ATTRIBUTES ObjectAttributes;
222 UNICODE_STRING WinSta, Desktop, FullName;
223 BOOL DesktopPresent = FALSE;
224 BOOL WinStaPresent = FALSE;
225 NTSTATUS Status;
226
227 ASSERT(hWinSta);
228
229 *hWinSta = NULL;
230
231 if(hDesktop != NULL)
232 {
233 *hDesktop = NULL;
234 }
235
236 RtlInitUnicodeString(&WinSta, NULL);
237 RtlInitUnicodeString(&Desktop, NULL);
238
239 if(DesktopPath != NULL && DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
240 {
241 PWCHAR c = DesktopPath->Buffer;
242 USHORT wl = 0;
243 USHORT l = DesktopPath->Length;
244
245 /*
246 * Parse the desktop path string which can be in the form "WinSta\Desktop"
247 * or just "Desktop". In latter case WinSta0 will be used.
248 */
249
250 while(l > 0)
251 {
252 if(*c == L'\\')
253 {
254 wl = (ULONG_PTR)c - (ULONG_PTR)DesktopPath->Buffer;
255 break;
256 }
257 l -= sizeof(WCHAR);
258 c++;
259 }
260
261 if(wl > 0)
262 {
263 WinSta.Length = wl;
264 WinSta.MaximumLength = wl + sizeof(WCHAR);
265 WinSta.Buffer = DesktopPath->Buffer;
266
267 WinStaPresent = TRUE;
268 c++;
269 }
270
271 Desktop.Length = DesktopPath->Length - wl;
272 if(wl > 0)
273 {
274 Desktop.Length -= sizeof(WCHAR);
275 }
276 if(Desktop.Length > 0)
277 {
278 Desktop.MaximumLength = Desktop.Length + sizeof(WCHAR);
279 Desktop.Buffer = ((wl > 0) ? c : DesktopPath->Buffer);
280 DesktopPresent = TRUE;
281 }
282 }
283
284 if(!WinStaPresent)
285 {
286 #if 0
287 /* search the process handle table for (inherited) window station
288 handles, use a more appropriate one than WinSta0 if possible. */
289 if (!ObFindHandleForObject(Process,
290 NULL,
291 ExWindowStationObjectType,
292 NULL,
293 (PHANDLE)hWinSta))
294 #endif
295 {
296 /* we had no luck searching for opened handles, use WinSta0 now */
297 RtlInitUnicodeString(&WinSta, L"WinSta0");
298 }
299 }
300
301 if(!DesktopPresent && hDesktop != NULL)
302 {
303 #if 0
304 /* search the process handle table for (inherited) desktop
305 handles, use a more appropriate one than Default if possible. */
306 if (!ObFindHandleForObject(Process,
307 NULL,
308 ExDesktopObjectType,
309 NULL,
310 (PHANDLE)hDesktop))
311 #endif
312 {
313 /* we had no luck searching for opened handles, use Desktop now */
314 RtlInitUnicodeString(&Desktop, L"Default");
315 }
316 }
317
318 if(*hWinSta == NULL)
319 {
320 if(!IntGetFullWindowStationName(&FullName, &WinSta, NULL))
321 {
322 return STATUS_INSUFFICIENT_RESOURCES;
323 }
324
325 /* open the window station */
326 InitializeObjectAttributes(&ObjectAttributes,
327 &FullName,
328 OBJ_CASE_INSENSITIVE,
329 NULL,
330 NULL);
331
332 Status = ObOpenObjectByName(&ObjectAttributes,
333 ExWindowStationObjectType,
334 KernelMode,
335 NULL,
336 0,
337 NULL,
338 (HANDLE*)hWinSta);
339
340 ExFreePoolWithTag(FullName.Buffer, TAG_STRING);
341
342 if(!NT_SUCCESS(Status))
343 {
344 SetLastNtError(Status);
345 DPRINT("Failed to reference window station %wZ PID: %d!\n", &WinSta, PsGetCurrentProcessId());
346 return Status;
347 }
348 }
349
350 if(hDesktop != NULL && *hDesktop == NULL)
351 {
352 if(!IntGetFullWindowStationName(&FullName, &WinSta, &Desktop))
353 {
354 NtClose(*hWinSta);
355 *hWinSta = NULL;
356 return STATUS_INSUFFICIENT_RESOURCES;
357 }
358
359 /* open the desktop object */
360 InitializeObjectAttributes(&ObjectAttributes,
361 &FullName,
362 OBJ_CASE_INSENSITIVE,
363 NULL,
364 NULL);
365
366 Status = ObOpenObjectByName(&ObjectAttributes,
367 ExDesktopObjectType,
368 KernelMode,
369 NULL,
370 0,
371 NULL,
372 (HANDLE*)hDesktop);
373
374 ExFreePoolWithTag(FullName.Buffer, TAG_STRING);
375
376 if(!NT_SUCCESS(Status))
377 {
378 *hDesktop = NULL;
379 NtClose(*hWinSta);
380 *hWinSta = NULL;
381 SetLastNtError(Status);
382 DPRINT("Failed to reference desktop %wZ PID: %d!\n", &Desktop, PsGetCurrentProcessId());
383 return Status;
384 }
385 }
386
387 return STATUS_SUCCESS;
388 }
389
390 /*
391 * IntValidateDesktopHandle
392 *
393 * Validates the desktop handle.
394 *
395 * Remarks
396 * If the function succeeds, the handle remains referenced. If the
397 * fucntion fails, last error is set.
398 */
399
400 NTSTATUS FASTCALL
401 IntValidateDesktopHandle(
402 HDESK Desktop,
403 KPROCESSOR_MODE AccessMode,
404 ACCESS_MASK DesiredAccess,
405 PDESKTOP *Object)
406 {
407 NTSTATUS Status;
408
409 Status = ObReferenceObjectByHandle(
410 Desktop,
411 DesiredAccess,
412 ExDesktopObjectType,
413 AccessMode,
414 (PVOID*)Object,
415 NULL);
416
417 if (!NT_SUCCESS(Status))
418 SetLastNtError(Status);
419
420 return Status;
421 }
422
423 PDESKTOP FASTCALL
424 IntGetActiveDesktop(VOID)
425 {
426 return InputDesktop;
427 }
428
429 /*
430 * returns or creates a handle to the desktop object
431 */
432 HDESK FASTCALL
433 IntGetDesktopObjectHandle(PDESKTOP DesktopObject)
434 {
435 NTSTATUS Status;
436 HDESK Ret;
437
438 ASSERT(DesktopObject);
439
440 if (!ObFindHandleForObject(PsGetCurrentProcess(),
441 DesktopObject,
442 ExDesktopObjectType,
443 NULL,
444 (PHANDLE)&Ret))
445 {
446 Status = ObOpenObjectByPointer(DesktopObject,
447 0,
448 NULL,
449 0,
450 ExDesktopObjectType,
451 UserMode,
452 (PHANDLE)&Ret);
453 if(!NT_SUCCESS(Status))
454 {
455 /* unable to create a handle */
456 DPRINT1("Unable to create a desktop handle\n");
457 return NULL;
458 }
459 }
460 else
461 {
462 DPRINT1("Got handle: %lx\n", Ret);
463 }
464
465 return Ret;
466 }
467
468 PUSER_MESSAGE_QUEUE FASTCALL
469 IntGetFocusMessageQueue(VOID)
470 {
471 PDESKTOP pdo = IntGetActiveDesktop();
472 if (!pdo)
473 {
474 DPRINT("No active desktop\n");
475 return(NULL);
476 }
477 return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
478 }
479
480 VOID FASTCALL
481 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue)
482 {
483 PUSER_MESSAGE_QUEUE Old;
484 PDESKTOP pdo = IntGetActiveDesktop();
485 if (!pdo)
486 {
487 DPRINT("No active desktop\n");
488 return;
489 }
490 if(NewQueue != NULL)
491 {
492 if(NewQueue->Desktop != NULL)
493 {
494 DPRINT("Message Queue already attached to another desktop!\n");
495 return;
496 }
497 IntReferenceMessageQueue(NewQueue);
498 (void)InterlockedExchangePointer((PVOID*)&NewQueue->Desktop, pdo);
499 }
500 Old = (PUSER_MESSAGE_QUEUE)InterlockedExchangePointer((PVOID*)&pdo->ActiveMessageQueue, NewQueue);
501 if(Old != NULL)
502 {
503 (void)InterlockedExchangePointer((PVOID*)&Old->Desktop, 0);
504 IntDereferenceMessageQueue(Old);
505 }
506 }
507
508 HWND FASTCALL IntGetDesktopWindow(VOID)
509 {
510 PDESKTOP pdo = IntGetActiveDesktop();
511 if (!pdo)
512 {
513 DPRINT("No active desktop\n");
514 return NULL;
515 }
516 return pdo->DesktopWindow;
517 }
518
519 PWND FASTCALL UserGetDesktopWindow(VOID)
520 {
521 PDESKTOP pdo = IntGetActiveDesktop();
522
523 if (!pdo)
524 {
525 DPRINT("No active desktop\n");
526 return NULL;
527 }
528
529 return UserGetWindowObject(pdo->DesktopWindow);
530 }
531
532 HWND FASTCALL IntGetMessageWindow(VOID)
533 {
534 PDESKTOP pdo = IntGetActiveDesktop();
535
536 if (!pdo)
537 {
538 DPRINT("No active desktop\n");
539 return NULL;
540 }
541 return pdo->spwndMessage->head.h;
542 }
543
544 HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
545 {
546 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
547 PDESKTOP pdo = pti->rpdesk;
548 if (NULL == pdo)
549 {
550 DPRINT1("Thread doesn't have a desktop\n");
551 return NULL;
552 }
553 return pdo->DesktopWindow;
554 }
555
556 BOOL FASTCALL IntDesktopUpdatePerUserSettings(BOOL bEnable)
557 {
558 if (bEnable)
559 {
560 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
561 NTSTATUS Status;
562
563 RtlZeroMemory(QueryTable, sizeof(QueryTable));
564
565 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
566 QueryTable[0].Name = L"PaintDesktopVersion";
567 QueryTable[0].EntryContext = &g_PaintDesktopVersion;
568
569 /* Query the "PaintDesktopVersion" flag in the "Control Panel\Desktop" key */
570 Status = RtlQueryRegistryValues(RTL_REGISTRY_USER,
571 L"Control Panel\\Desktop",
572 QueryTable, NULL, NULL);
573 if (!NT_SUCCESS(Status))
574 {
575 DPRINT("RtlQueryRegistryValues failed for PaintDesktopVersion (%x)\n",
576 Status);
577 g_PaintDesktopVersion = FALSE;
578 return FALSE;
579 }
580
581 DPRINT("PaintDesktopVersion = %d\n", g_PaintDesktopVersion);
582
583 return TRUE;
584 }
585 else
586 {
587 g_PaintDesktopVersion = FALSE;
588 return TRUE;
589 }
590 }
591
592 /* PUBLIC FUNCTIONS ***********************************************************/
593
594 HDC FASTCALL
595 UserGetDesktopDC(ULONG DcType, BOOL EmptyDC, BOOL ValidatehWnd)
596 {
597 PWND DesktopObject = 0;
598 HDC DesktopHDC = 0;
599
600 if (DcType == DC_TYPE_DIRECT)
601 {
602 DesktopObject = UserGetDesktopWindow();
603 DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
604 }
605 else
606 {
607 HDEV hDev;
608 hDev = (HDEV)pPrimarySurface;
609 DesktopHDC = IntGdiCreateDisplayDC(hDev, DcType, EmptyDC);
610 }
611
612 return DesktopHDC;
613 }
614
615 VOID APIENTRY
616 UserRedrawDesktop()
617 {
618 PWND Window = NULL;
619 HRGN hRgn;
620
621 Window = UserGetDesktopWindow();
622 hRgn = IntSysCreateRectRgnIndirect(&Window->rcWindow);
623
624 IntInvalidateWindows( Window,
625 hRgn,
626 RDW_FRAME |
627 RDW_ERASE |
628 RDW_INVALIDATE |
629 RDW_ALLCHILDREN);
630
631 REGION_FreeRgnByHandle(hRgn);
632 }
633
634
635 NTSTATUS FASTCALL
636 co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height)
637 {
638 CSR_API_MESSAGE Request;
639
640 Request.Type = MAKE_CSR_API(SHOW_DESKTOP, CSR_GUI);
641 Request.Data.ShowDesktopRequest.DesktopWindow = Desktop->DesktopWindow;
642 Request.Data.ShowDesktopRequest.Width = Width;
643 Request.Data.ShowDesktopRequest.Height = Height;
644
645 return co_CsrNotify(&Request);
646 }
647
648 NTSTATUS FASTCALL
649 IntHideDesktop(PDESKTOP Desktop)
650 {
651 #if 0
652 CSRSS_API_REQUEST Request;
653 CSRSS_API_REPLY Reply;
654
655 Request.Type = CSRSS_HIDE_DESKTOP;
656 Request.Data.HideDesktopRequest.DesktopWindow = Desktop->DesktopWindow;
657
658 return NotifyCsrss(&Request, &Reply);
659 #else
660
661 PWND DesktopWnd;
662
663 DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow);
664 if (! DesktopWnd)
665 {
666 return ERROR_INVALID_WINDOW_HANDLE;
667 }
668 DesktopWnd->style &= ~WS_VISIBLE;
669
670 return STATUS_SUCCESS;
671 #endif
672 }
673
674
675
676
677 static
678 HWND* FASTCALL
679 UserBuildShellHookHwndList(PDESKTOP Desktop)
680 {
681 ULONG entries=0;
682 PSHELL_HOOK_WINDOW Current;
683 HWND* list;
684
685 /* fixme: if we save nb elements in desktop, we dont have to loop to find nb entries */
686 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
687 entries++;
688
689 if (!entries) return NULL;
690
691 list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
692 if (list)
693 {
694 HWND* cursor = list;
695
696 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
697 *cursor++ = Current->hWnd;
698
699 *cursor = NULL; /* nullterm list */
700 }
701
702 return list;
703 }
704
705 /*
706 * Send the Message to the windows registered for ShellHook
707 * notifications. The lParam contents depend on the Message. See
708 * MSDN for more details (RegisterShellHookWindow)
709 */
710 VOID co_IntShellHookNotify(WPARAM Message, LPARAM lParam)
711 {
712 PDESKTOP Desktop = IntGetActiveDesktop();
713 HWND* HwndList;
714
715 if (!gpsi->uiShellMsg)
716 {
717
718 /* Too bad, this doesn't work.*/
719 #if 0
720 UNICODE_STRING Str;
721 RtlInitUnicodeString(&Str, L"SHELLHOOK");
722 gpsi->uiShellMsg = UserRegisterWindowMessage(&Str);
723 #endif
724
725 gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK");
726
727 DPRINT("MsgType = %x\n", gpsi->uiShellMsg);
728 if (!gpsi->uiShellMsg)
729 DPRINT1("LastError: %x\n", EngGetLastError());
730 }
731
732 if (!Desktop)
733 {
734 DPRINT("IntShellHookNotify: No desktop!\n");
735 return;
736 }
737
738 HwndList = UserBuildShellHookHwndList(Desktop);
739 if (HwndList)
740 {
741 HWND* cursor = HwndList;
742
743 for (; *cursor; cursor++)
744 {
745 DPRINT("Sending notify\n");
746 co_IntPostOrSendMessage(*cursor,
747 gpsi->uiShellMsg,
748 Message,
749 lParam);
750 }
751
752 ExFreePool(HwndList);
753 }
754
755 }
756
757 /*
758 * Add the window to the ShellHookWindows list. The windows
759 * on that list get notifications that are important to shell
760 * type applications.
761 *
762 * TODO: Validate the window? I'm not sure if sending these messages to
763 * an unsuspecting application that is not your own is a nice thing to do.
764 */
765 BOOL IntRegisterShellHookWindow(HWND hWnd)
766 {
767 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
768 PDESKTOP Desktop = pti->rpdesk;
769 PSHELL_HOOK_WINDOW Entry;
770
771 DPRINT("IntRegisterShellHookWindow\n");
772
773 /* First deregister the window, so we can be sure it's never twice in the
774 * list.
775 */
776 IntDeRegisterShellHookWindow(hWnd);
777
778 Entry = ExAllocatePoolWithTag(PagedPool,
779 sizeof(SHELL_HOOK_WINDOW),
780 TAG_WINSTA);
781
782 if (!Entry)
783 return FALSE;
784
785 Entry->hWnd = hWnd;
786
787 InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
788
789 return TRUE;
790 }
791
792 /*
793 * Remove the window from the ShellHookWindows list. The windows
794 * on that list get notifications that are important to shell
795 * type applications.
796 */
797 BOOL IntDeRegisterShellHookWindow(HWND hWnd)
798 {
799 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
800 PDESKTOP Desktop = pti->rpdesk;
801 PSHELL_HOOK_WINDOW Current;
802
803 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
804 {
805 if (Current->hWnd == hWnd)
806 {
807 RemoveEntryList(&Current->ListEntry);
808 ExFreePool(Current);
809 return TRUE;
810 }
811 }
812
813 return FALSE;
814 }
815
816 static VOID
817 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
818 {
819 if (Desktop->hsectionDesktop != NULL)
820 {
821 ObDereferenceObject(Desktop->hsectionDesktop);
822 Desktop->hsectionDesktop = NULL;
823 }
824 }
825 /* SYSCALLS *******************************************************************/
826
827 /*
828 * NtUserCreateDesktop
829 *
830 * Creates a new desktop.
831 *
832 * Parameters
833 * poaAttribs
834 * Object Attributes.
835 *
836 * lpszDesktopDevice
837 * Name of the device.
838 *
839 * pDeviceMode
840 * Device Mode.
841 *
842 * dwFlags
843 * Interaction flags.
844 *
845 * dwDesiredAccess
846 * Requested type of access.
847 *
848 *
849 * Return Value
850 * If the function succeeds, the return value is a handle to the newly
851 * created desktop. If the specified desktop already exists, the function
852 * succeeds and returns a handle to the existing desktop. When you are
853 * finished using the handle, call the CloseDesktop function to close it.
854 * If the function fails, the return value is NULL.
855 *
856 * Status
857 * @implemented
858 */
859
860 HDESK APIENTRY
861 NtUserCreateDesktop(
862 POBJECT_ATTRIBUTES poa,
863 PUNICODE_STRING lpszDesktopDevice,
864 LPDEVMODEW lpdmw,
865 DWORD dwFlags,
866 ACCESS_MASK dwDesiredAccess)
867 {
868 OBJECT_ATTRIBUTES ObjectAttributes;
869 PTHREADINFO W32Thread;
870 PWINSTATION_OBJECT WinStaObject;
871 PDESKTOP DesktopObject;
872 UNICODE_STRING DesktopName;
873 NTSTATUS Status = STATUS_SUCCESS;
874 HDESK Desktop;
875 CSR_API_MESSAGE Request;
876 PVOID DesktopHeapSystemBase = NULL;
877 SIZE_T DesktopInfoSize;
878 UNICODE_STRING SafeDesktopName;
879 ULONG DummyContext;
880 ULONG_PTR HeapSize = 4 * 1024 * 1024; /* FIXME */
881 HWINSTA hWindowStation = NULL ;
882 PUNICODE_STRING lpszDesktopName = NULL;
883 UNICODE_STRING ClassName, MenuName;
884 LARGE_STRING WindowName;
885 BOOL NoHooks = FALSE;
886 PWND pWnd = NULL;
887 CREATESTRUCTW Cs;
888 INT i;
889 PTHREADINFO ptiCurrent;
890 DECLARE_RETURN(HDESK);
891
892 DPRINT("Enter NtUserCreateDesktop: %wZ\n", lpszDesktopName);
893 UserEnterExclusive();
894
895 ptiCurrent = PsGetCurrentThreadWin32Thread();
896 if (ptiCurrent)
897 {
898 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
899 NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
900 ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
901 }
902
903 _SEH2_TRY
904 {
905 ProbeForRead( poa,
906 sizeof(OBJECT_ATTRIBUTES),
907 1);
908
909 hWindowStation = poa->RootDirectory;
910 lpszDesktopName = poa->ObjectName;
911 }
912 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
913 {
914 Status =_SEH2_GetExceptionCode();
915 }
916 _SEH2_END
917
918 if (! NT_SUCCESS(Status))
919 {
920 DPRINT1("Failed reading Object Attributes from user space.\n");
921 SetLastNtError(Status);
922 RETURN( NULL);
923 }
924
925 Status = IntValidateWindowStationHandle(
926 hWindowStation,
927 KernelMode,
928 0, /* FIXME - WINSTA_CREATEDESKTOP */
929 &WinStaObject);
930
931 if (! NT_SUCCESS(Status))
932 {
933 DPRINT1("Failed validation of window station handle (0x%X), cannot create desktop %wZ\n",
934 hWindowStation, lpszDesktopName);
935 SetLastNtError(Status);
936 RETURN( NULL);
937 }
938 if(lpszDesktopName != NULL)
939 {
940 Status = IntSafeCopyUnicodeString(&SafeDesktopName, lpszDesktopName);
941 if(!NT_SUCCESS(Status))
942 {
943 SetLastNtError(Status);
944 RETURN( NULL);
945 }
946 }
947 else
948 {
949 RtlInitUnicodeString(&SafeDesktopName, NULL);
950 }
951
952 if (! IntGetFullWindowStationName(&DesktopName, &WinStaObject->Name,
953 &SafeDesktopName))
954 {
955 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
956 ObDereferenceObject(WinStaObject);
957 if (lpszDesktopName)
958 ExFreePoolWithTag(SafeDesktopName.Buffer, TAG_STRING);
959 RETURN( NULL);
960 }
961 if (lpszDesktopName)
962 ExFreePoolWithTag(SafeDesktopName.Buffer, TAG_STRING);
963 ObDereferenceObject(WinStaObject);
964
965 /*
966 * Try to open already existing desktop
967 */
968
969 DPRINT("Trying to open desktop (%wZ)\n", &DesktopName);
970
971 /* Initialize ObjectAttributes for the desktop object */
972 InitializeObjectAttributes(
973 &ObjectAttributes,
974 &DesktopName,
975 0,
976 NULL,
977 NULL);
978
979 Status = ObOpenObjectByName(
980 &ObjectAttributes,
981 ExDesktopObjectType,
982 KernelMode,
983 NULL,
984 dwDesiredAccess,
985 (PVOID)&DummyContext,
986 (HANDLE*)&Desktop);
987 if (!NT_SUCCESS(Status)) RETURN(NULL);
988 if (Status == STATUS_OBJECT_NAME_EXISTS)
989 {
990 ExFreePoolWithTag(DesktopName.Buffer, TAG_STRING);
991 RETURN( Desktop);
992 }
993
994 /* Reference the desktop */
995 Status = ObReferenceObjectByHandle(Desktop,
996 0,
997 ExDesktopObjectType,
998 KernelMode,
999 (PVOID)&DesktopObject,
1000 NULL);
1001 if (!NT_SUCCESS(Status)) RETURN(NULL);
1002
1003 DesktopObject->hsectionDesktop = NULL;
1004 DesktopObject->pheapDesktop = UserCreateHeap(&DesktopObject->hsectionDesktop,
1005 &DesktopHeapSystemBase,
1006 HeapSize);
1007 if (DesktopObject->pheapDesktop == NULL)
1008 {
1009 ObDereferenceObject(DesktopObject);
1010 DPRINT1("Failed to create desktop heap!\n");
1011 RETURN(NULL);
1012 }
1013
1014 DesktopInfoSize = FIELD_OFFSET(DESKTOPINFO,
1015 szDesktopName[(lpszDesktopName->Length / sizeof(WCHAR)) + 1]);
1016
1017 DesktopObject->pDeskInfo = RtlAllocateHeap(DesktopObject->pheapDesktop,
1018 HEAP_NO_SERIALIZE,
1019 DesktopInfoSize);
1020
1021 if (DesktopObject->pDeskInfo == NULL)
1022 {
1023 ObDereferenceObject(DesktopObject);
1024 DPRINT1("Failed to create the DESKTOP structure!\n");
1025 RETURN(NULL);
1026 }
1027
1028 RtlZeroMemory(DesktopObject->pDeskInfo,
1029 DesktopInfoSize);
1030
1031 DesktopObject->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
1032 DesktopObject->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
1033 RtlCopyMemory(DesktopObject->pDeskInfo->szDesktopName,
1034 lpszDesktopName->Buffer,
1035 lpszDesktopName->Length);
1036
1037 /* Initialize some local (to win32k) desktop state. */
1038 InitializeListHead(&DesktopObject->PtiList);
1039 DesktopObject->ActiveMessageQueue = NULL;
1040 /* Setup Global Hooks. */
1041 for (i = 0; i < NB_HOOKS; i++)
1042 {
1043 InitializeListHead(&DesktopObject->pDeskInfo->aphkStart[i]);
1044 }
1045 ExFreePoolWithTag(DesktopName.Buffer, TAG_STRING);
1046
1047 //// why is this here?
1048 #if 0
1049 if (! NT_SUCCESS(Status))
1050 {
1051 DPRINT1("Failed to create desktop handle\n");
1052 SetLastNtError(Status);
1053 RETURN( NULL);
1054 }
1055 #endif
1056 ////
1057
1058 /*
1059 * Create a handle for CSRSS and notify CSRSS for Creating Desktop Window.
1060 *
1061 * Honestly, I believe this is a cleverly written hack that allowed ReactOS
1062 * to function at the beginning of the project by ramroding the GUI into
1063 * operation and making the desktop window work from user space.
1064 * (jt)
1065 */
1066 Request.Type = MAKE_CSR_API(CREATE_DESKTOP, CSR_GUI);
1067 Status = CsrInsertObject(Desktop,
1068 GENERIC_ALL,
1069 (HANDLE*)&Request.Data.CreateDesktopRequest.DesktopHandle);
1070 if (! NT_SUCCESS(Status))
1071 {
1072 DPRINT1("Failed to create desktop handle for CSRSS\n");
1073 ZwClose(Desktop);
1074 SetLastNtError(Status);
1075 RETURN( NULL);
1076 }
1077
1078 Status = co_CsrNotify(&Request);
1079 if (! NT_SUCCESS(Status))
1080 {
1081 CsrCloseHandle(Request.Data.CreateDesktopRequest.DesktopHandle);
1082 DPRINT1("Failed to notify CSRSS about new desktop\n");
1083 ZwClose(Desktop);
1084 SetLastNtError(Status);
1085 RETURN( NULL);
1086 }
1087 #if 0 // Turn on when server side proc is ready.
1088 //
1089 // Create desktop window.
1090 //
1091 ClassName.Buffer = ((PWSTR)((ULONG_PTR)(WORD)(gpsi->atomSysClass[ICLS_DESKTOP])));
1092 ClassName.Length = 0;
1093 RtlZeroMemory(&MenuName, sizeof(MenuName));
1094 RtlZeroMemory(&WindowName, sizeof(WindowName));
1095
1096 RtlZeroMemory(&Cs, sizeof(Cs));
1097 Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN);
1098 Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN);
1099 Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN);
1100 Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN);
1101 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1102 Cs.hInstance = hModClient; // Experimental mode... Move csr stuff to User32. hModuleWin; // Server side winproc!
1103 Cs.lpszName = (LPCWSTR) &WindowName;
1104 Cs.lpszClass = (LPCWSTR) &ClassName;
1105
1106 pWndDesktop = co_UserCreateWindowEx(&Cs, &ClassName, &WindowName);
1107 if (!pWnd)
1108 {
1109 DPRINT1("Failed to create Desktop window handle\n");
1110 }
1111 else
1112 {
1113 DesktopObject->pDeskInfo->spwnd = pWndDesktop;
1114 }
1115 #endif
1116 W32Thread = PsGetCurrentThreadWin32Thread();
1117
1118 if (!W32Thread->rpdesk) IntSetThreadDesktop(DesktopObject,FALSE);
1119
1120 /*
1121 Based on wine/server/window.c in get_desktop_window.
1122 */
1123
1124 ClassName.Buffer = ((PWSTR)((ULONG_PTR)(WORD)(gpsi->atomSysClass[ICLS_HWNDMESSAGE])));
1125 ClassName.Length = 0;
1126 RtlZeroMemory(&MenuName, sizeof(MenuName));
1127 RtlZeroMemory(&WindowName, sizeof(WindowName));
1128
1129 RtlZeroMemory(&Cs, sizeof(Cs));
1130 Cs.cx = Cs.cy = 100;
1131 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1132 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc! Leave it to Timo to not pass on notes!
1133 Cs.lpszName = (LPCWSTR) &WindowName;
1134 Cs.lpszClass = (LPCWSTR) &ClassName;
1135
1136 pWnd = co_UserCreateWindowEx(&Cs, &ClassName, &WindowName);
1137 if (!pWnd)
1138 {
1139 DPRINT1("Failed to create Message window handle\n");
1140 }
1141 else
1142 {
1143 DesktopObject->spwndMessage = pWnd;
1144 }
1145
1146 /* Now,,,
1147 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1148 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1149 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1150 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1151 The rest is same as message window.
1152 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1153 */
1154 RETURN( Desktop);
1155
1156 CLEANUP:
1157 if (!NoHooks && ptiCurrent) ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
1158 DPRINT("Leave NtUserCreateDesktop, ret=%i\n",_ret_);
1159 UserLeave();
1160 END_CLEANUP;
1161 }
1162
1163 /*
1164 * NtUserOpenDesktop
1165 *
1166 * Opens an existing desktop.
1167 *
1168 * Parameters
1169 * lpszDesktopName
1170 * Name of the existing desktop.
1171 *
1172 * dwFlags
1173 * Interaction flags.
1174 *
1175 * dwDesiredAccess
1176 * Requested type of access.
1177 *
1178 * Return Value
1179 * Handle to the desktop or zero on failure.
1180 *
1181 * Status
1182 * @implemented
1183 */
1184
1185 HDESK APIENTRY
1186 NtUserOpenDesktop(
1187 PUNICODE_STRING lpszDesktopName,
1188 DWORD dwFlags,
1189 ACCESS_MASK dwDesiredAccess)
1190 {
1191 OBJECT_ATTRIBUTES ObjectAttributes;
1192 HWINSTA WinSta;
1193 PWINSTATION_OBJECT WinStaObject;
1194 UNICODE_STRING DesktopName;
1195 UNICODE_STRING SafeDesktopName;
1196 NTSTATUS Status;
1197 HDESK Desktop;
1198 BOOL Result;
1199 DECLARE_RETURN(HDESK);
1200
1201 DPRINT("Enter NtUserOpenDesktop: %wZ\n", lpszDesktopName);
1202 UserEnterExclusive();
1203
1204 /*
1205 * Validate the window station handle and compose the fully
1206 * qualified desktop name
1207 */
1208
1209 WinSta = UserGetProcessWindowStation();
1210 Status = IntValidateWindowStationHandle(
1211 WinSta,
1212 KernelMode,
1213 0,
1214 &WinStaObject);
1215
1216 if (!NT_SUCCESS(Status))
1217 {
1218 DPRINT1("Failed validation of window station handle (0x%X)\n", WinSta);
1219 SetLastNtError(Status);
1220 RETURN( 0);
1221 }
1222
1223 if(lpszDesktopName != NULL)
1224 {
1225 Status = IntSafeCopyUnicodeString(&SafeDesktopName, lpszDesktopName);
1226 if(!NT_SUCCESS(Status))
1227 {
1228 SetLastNtError(Status);
1229 RETURN( NULL);
1230 }
1231 }
1232 else
1233 {
1234 RtlInitUnicodeString(&SafeDesktopName, NULL);
1235 }
1236
1237 Result = IntGetFullWindowStationName(&DesktopName, &WinStaObject->Name,
1238 &SafeDesktopName);
1239
1240 if (lpszDesktopName)
1241 ExFreePoolWithTag(SafeDesktopName.Buffer, TAG_STRING);
1242 ObDereferenceObject(WinStaObject);
1243
1244
1245 if (!Result)
1246 {
1247 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
1248 RETURN( 0);
1249 }
1250
1251
1252 DPRINT("Trying to open desktop (%wZ)\n", &DesktopName);
1253
1254 /* Initialize ObjectAttributes for the desktop object */
1255 InitializeObjectAttributes(
1256 &ObjectAttributes,
1257 &DesktopName,
1258 0,
1259 NULL,
1260 NULL);
1261
1262 Status = ObOpenObjectByName(
1263 &ObjectAttributes,
1264 ExDesktopObjectType,
1265 KernelMode,
1266 NULL,
1267 dwDesiredAccess,
1268 NULL,
1269 (HANDLE*)&Desktop);
1270
1271 if (!NT_SUCCESS(Status))
1272 {
1273 SetLastNtError(Status);
1274 ExFreePool(DesktopName.Buffer);
1275 RETURN( 0);
1276 }
1277
1278 DPRINT("Successfully opened desktop (%wZ)\n", &DesktopName);
1279 ExFreePool(DesktopName.Buffer);
1280
1281 RETURN( Desktop);
1282
1283 CLEANUP:
1284 DPRINT("Leave NtUserOpenDesktop, ret=%i\n",_ret_);
1285 UserLeave();
1286 END_CLEANUP;
1287 }
1288
1289 /*
1290 * NtUserOpenInputDesktop
1291 *
1292 * Opens the input (interactive) desktop.
1293 *
1294 * Parameters
1295 * dwFlags
1296 * Interaction flags.
1297 *
1298 * fInherit
1299 * Inheritance option.
1300 *
1301 * dwDesiredAccess
1302 * Requested type of access.
1303 *
1304 * Return Value
1305 * Handle to the input desktop or zero on failure.
1306 *
1307 * Status
1308 * @implemented
1309 */
1310
1311 HDESK APIENTRY
1312 NtUserOpenInputDesktop(
1313 DWORD dwFlags,
1314 BOOL fInherit,
1315 ACCESS_MASK dwDesiredAccess)
1316 {
1317 PDESKTOP Object;
1318 NTSTATUS Status;
1319 HDESK Desktop;
1320 DECLARE_RETURN(HDESK);
1321
1322 DPRINT("Enter NtUserOpenInputDesktop\n");
1323 UserEnterExclusive();
1324
1325 DPRINT("About to open input desktop\n");
1326
1327 /* Get a pointer to the desktop object */
1328
1329 Status = IntValidateDesktopHandle(
1330 InputDesktopHandle,
1331 UserMode,
1332 0,
1333 &Object);
1334
1335 if (!NT_SUCCESS(Status))
1336 {
1337 DPRINT("Validation of input desktop handle (0x%X) failed\n", InputDesktop);
1338 RETURN((HDESK)0);
1339 }
1340
1341 /* Create a new handle to the object */
1342
1343 Status = ObOpenObjectByPointer(
1344 Object,
1345 0,
1346 NULL,
1347 dwDesiredAccess,
1348 ExDesktopObjectType,
1349 UserMode,
1350 (HANDLE*)&Desktop);
1351
1352 ObDereferenceObject(Object);
1353
1354 if (NT_SUCCESS(Status))
1355 {
1356 DPRINT("Successfully opened input desktop\n");
1357 RETURN((HDESK)Desktop);
1358 }
1359
1360 SetLastNtError(Status);
1361 RETURN((HDESK)0);
1362
1363 CLEANUP:
1364 DPRINT("Leave NtUserOpenInputDesktop, ret=%i\n",_ret_);
1365 UserLeave();
1366 END_CLEANUP;
1367 }
1368
1369 /*
1370 * NtUserCloseDesktop
1371 *
1372 * Closes a desktop handle.
1373 *
1374 * Parameters
1375 * hDesktop
1376 * Handle to the desktop.
1377 *
1378 * Return Value
1379 * Status
1380 *
1381 * Remarks
1382 * The desktop handle can be created with NtUserCreateDesktop or
1383 * NtUserOpenDesktop. This function will fail if any thread in the calling
1384 * process is using the specified desktop handle or if the handle refers
1385 * to the initial desktop of the calling process.
1386 *
1387 * Status
1388 * @implemented
1389 */
1390
1391 BOOL APIENTRY
1392 NtUserCloseDesktop(HDESK hDesktop)
1393 {
1394 PDESKTOP Object;
1395 NTSTATUS Status;
1396 DECLARE_RETURN(BOOL);
1397
1398 DPRINT("Enter NtUserCloseDesktop\n");
1399 UserEnterExclusive();
1400
1401 DPRINT("About to close desktop handle (0x%X)\n", hDesktop);
1402
1403 Status = IntValidateDesktopHandle(
1404 hDesktop,
1405 UserMode,
1406 0,
1407 &Object);
1408
1409 if (!NT_SUCCESS(Status))
1410 {
1411 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
1412 RETURN(FALSE);
1413 }
1414
1415 ObDereferenceObject(Object);
1416
1417 DPRINT("Closing desktop handle (0x%X)\n", hDesktop);
1418
1419 Status = ZwClose(hDesktop);
1420 if (!NT_SUCCESS(Status))
1421 {
1422 SetLastNtError(Status);
1423 RETURN(FALSE);
1424 }
1425
1426 RETURN(TRUE);
1427
1428 CLEANUP:
1429 DPRINT("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
1430 UserLeave();
1431 END_CLEANUP;
1432 }
1433
1434
1435
1436
1437 /*
1438 * NtUserPaintDesktop
1439 *
1440 * The NtUserPaintDesktop function fills the clipping region in the
1441 * specified device context with the desktop pattern or wallpaper. The
1442 * function is provided primarily for shell desktops.
1443 *
1444 * Parameters
1445 * hDC
1446 * Handle to the device context.
1447 *
1448 * Status
1449 * @implemented
1450 */
1451
1452 BOOL APIENTRY
1453 NtUserPaintDesktop(HDC hDC)
1454 {
1455 RECTL Rect;
1456 HBRUSH DesktopBrush, PreviousBrush;
1457 HWND hWndDesktop;
1458 BOOL doPatBlt = TRUE;
1459 PWND WndDesktop;
1460 int len;
1461 COLORREF color_old;
1462 UINT align_old;
1463 int mode_old;
1464 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1465 PWINSTATION_OBJECT WinSta = pti->rpdesk->rpwinstaParent;
1466 DECLARE_RETURN(BOOL);
1467
1468 UserEnterExclusive();
1469 DPRINT("Enter NtUserPaintDesktop\n");
1470
1471 GdiGetClipBox(hDC, &Rect);
1472
1473 hWndDesktop = IntGetDesktopWindow();
1474
1475 WndDesktop = UserGetWindowObject(hWndDesktop);
1476 if (!WndDesktop)
1477 {
1478 RETURN(FALSE);
1479 }
1480
1481 DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground;
1482
1483
1484 /*
1485 * Paint desktop background
1486 */
1487
1488 if (WinSta->hbmWallpaper != NULL)
1489 {
1490 PWND DeskWin;
1491
1492 DeskWin = UserGetWindowObject(hWndDesktop);
1493
1494 if (DeskWin)
1495 {
1496 SIZE sz;
1497 int x, y;
1498 HDC hWallpaperDC;
1499
1500 sz.cx = DeskWin->rcWindow.right - DeskWin->rcWindow.left;
1501 sz.cy = DeskWin->rcWindow.bottom - DeskWin->rcWindow.top;
1502
1503 if (WinSta->WallpaperMode == wmStretch ||
1504 WinSta->WallpaperMode == wmTile)
1505 {
1506 x = 0;
1507 y = 0;
1508 }
1509 else
1510 {
1511 /* Find the upper left corner, can be negtive if the bitmap is bigger then the screen */
1512 x = (sz.cx / 2) - (WinSta->cxWallpaper / 2);
1513 y = (sz.cy / 2) - (WinSta->cyWallpaper / 2);
1514 }
1515
1516 hWallpaperDC = NtGdiCreateCompatibleDC(hDC);
1517 if(hWallpaperDC != NULL)
1518 {
1519 HBITMAP hOldBitmap;
1520
1521 /* fill in the area that the bitmap is not going to cover */
1522 if (x > 0 || y > 0)
1523 {
1524 /* FIXME - clip out the bitmap
1525 can be replaced with "NtGdiPatBlt(hDC, x, y, WinSta->cxWallpaper, WinSta->cyWallpaper, PATCOPY | DSTINVERT);"
1526 once we support DSTINVERT */
1527 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1528 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1529 NtGdiSelectBrush(hDC, PreviousBrush);
1530 }
1531
1532 /*Do not fill the background after it is painted no matter the size of the picture */
1533 doPatBlt = FALSE;
1534
1535 hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, WinSta->hbmWallpaper);
1536
1537 if (WinSta->WallpaperMode == wmStretch)
1538 {
1539 if(Rect.right && Rect.bottom)
1540 NtGdiStretchBlt(hDC,
1541 x,
1542 y,
1543 sz.cx,
1544 sz.cy,
1545 hWallpaperDC,
1546 0,
1547 0,
1548 WinSta->cxWallpaper,
1549 WinSta->cyWallpaper,
1550 SRCCOPY,
1551 0);
1552
1553 }
1554 else if (WinSta->WallpaperMode == wmTile)
1555 {
1556 /* paint the bitmap across the screen then down */
1557 for(y = 0; y < Rect.bottom; y += WinSta->cyWallpaper)
1558 {
1559 for(x = 0; x < Rect.right; x += WinSta->cxWallpaper)
1560 {
1561 NtGdiBitBlt(hDC,
1562 x,
1563 y,
1564 WinSta->cxWallpaper,
1565 WinSta->cyWallpaper,
1566 hWallpaperDC,
1567 0,
1568 0,
1569 SRCCOPY,
1570 0,
1571 0);
1572 }
1573 }
1574 }
1575 else
1576 {
1577 NtGdiBitBlt(hDC,
1578 x,
1579 y,
1580 WinSta->cxWallpaper,
1581 WinSta->cyWallpaper,
1582 hWallpaperDC,
1583 0,
1584 0,
1585 SRCCOPY,
1586 0,
1587 0);
1588 }
1589 NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
1590 NtGdiDeleteObjectApp(hWallpaperDC);
1591 }
1592 }
1593 }
1594
1595 /* Back ground is set to none, clear the screen */
1596 if (doPatBlt)
1597 {
1598 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1599 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1600 NtGdiSelectBrush(hDC, PreviousBrush);
1601 }
1602
1603 /*
1604 * Display system version on the desktop background
1605 */
1606
1607 if (g_PaintDesktopVersion)
1608 {
1609 static WCHAR s_wszVersion[256] = {0};
1610 RECTL rect;
1611
1612 if (*s_wszVersion)
1613 {
1614 len = wcslen(s_wszVersion);
1615 }
1616 else
1617 {
1618 len = GetSystemVersionString(s_wszVersion);
1619 }
1620
1621 if (len)
1622 {
1623 if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0))
1624 {
1625 rect.right = UserGetSystemMetrics(SM_CXSCREEN);
1626 rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
1627 }
1628
1629 color_old = IntGdiSetTextColor(hDC, RGB(255,255,255));
1630 align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
1631 mode_old = IntGdiSetBkMode(hDC, TRANSPARENT);
1632
1633 GreExtTextOutW(hDC, rect.right-16, rect.bottom-48, 0, NULL, s_wszVersion, len, NULL, 0);
1634
1635 IntGdiSetBkMode(hDC, mode_old);
1636 IntGdiSetTextAlign(hDC, align_old);
1637 IntGdiSetTextColor(hDC, color_old);
1638 }
1639 }
1640
1641 RETURN(TRUE);
1642
1643 CLEANUP:
1644 DPRINT("Leave NtUserPaintDesktop, ret=%i\n",_ret_);
1645 UserLeave();
1646 END_CLEANUP;
1647 }
1648
1649
1650 /*
1651 * NtUserSwitchDesktop
1652 *
1653 * Sets the current input (interactive) desktop.
1654 *
1655 * Parameters
1656 * hDesktop
1657 * Handle to desktop.
1658 *
1659 * Return Value
1660 * Status
1661 *
1662 * Status
1663 * @unimplemented
1664 */
1665
1666 BOOL APIENTRY
1667 NtUserSwitchDesktop(HDESK hDesktop)
1668 {
1669 PDESKTOP DesktopObject;
1670 NTSTATUS Status;
1671 DECLARE_RETURN(BOOL);
1672
1673 UserEnterExclusive();
1674 DPRINT("Enter NtUserSwitchDesktop\n");
1675
1676 DPRINT("About to switch desktop (0x%X)\n", hDesktop);
1677
1678 Status = IntValidateDesktopHandle(
1679 hDesktop,
1680 UserMode,
1681 0,
1682 &DesktopObject);
1683
1684 if (!NT_SUCCESS(Status))
1685 {
1686 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
1687 RETURN(FALSE);
1688 }
1689
1690 /*
1691 * Don't allow applications switch the desktop if it's locked, unless the caller
1692 * is the logon application itself
1693 */
1694 if((DesktopObject->rpwinstaParent->Flags & WSS_LOCKED) &&
1695 LogonProcess != NULL && LogonProcess != PsGetCurrentProcessWin32Process())
1696 {
1697 ObDereferenceObject(DesktopObject);
1698 DPRINT1("Switching desktop 0x%x denied because the work station is locked!\n", hDesktop);
1699 RETURN(FALSE);
1700 }
1701
1702 if(DesktopObject->rpwinstaParent != InputWindowStation)
1703 {
1704 ObDereferenceObject(DesktopObject);
1705 DPRINT1("Switching desktop 0x%x denied because desktop doesn't belong to the interactive winsta!\n", hDesktop);
1706 RETURN(FALSE);
1707 }
1708
1709 /* FIXME: Fail if the process is associated with a secured
1710 desktop such as Winlogon or Screen-Saver */
1711 /* FIXME: Connect to input device */
1712
1713 /* Set the active desktop in the desktop's window station. */
1714 InputWindowStation->ActiveDesktop = DesktopObject;
1715
1716 /* Set the global state. */
1717 InputDesktop = DesktopObject;
1718 InputDesktopHandle = hDesktop;
1719
1720 ObDereferenceObject(DesktopObject);
1721
1722 RETURN(TRUE);
1723
1724 CLEANUP:
1725 DPRINT("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
1726 UserLeave();
1727 END_CLEANUP;
1728 }
1729
1730 /*
1731 * NtUserGetThreadDesktop
1732 *
1733 * Status
1734 * @implemented
1735 */
1736
1737 HDESK APIENTRY
1738 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
1739 {
1740 NTSTATUS Status;
1741 PETHREAD Thread;
1742 PDESKTOP DesktopObject;
1743 HDESK Ret, hThreadDesktop;
1744 OBJECT_HANDLE_INFORMATION HandleInformation;
1745 DECLARE_RETURN(HDESK);
1746
1747 UserEnterExclusive();
1748 DPRINT("Enter NtUserGetThreadDesktop\n");
1749
1750 if(!dwThreadId)
1751 {
1752 EngSetLastError(ERROR_INVALID_PARAMETER);
1753 RETURN(0);
1754 }
1755
1756 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
1757 if(!NT_SUCCESS(Status))
1758 {
1759 EngSetLastError(ERROR_INVALID_PARAMETER);
1760 RETURN(0);
1761 }
1762
1763 if(Thread->ThreadsProcess == PsGetCurrentProcess())
1764 {
1765 /* just return the handle, we queried the desktop handle of a thread running
1766 in the same context */
1767 Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk;
1768 ObDereferenceObject(Thread);
1769 RETURN(Ret);
1770 }
1771
1772 /* get the desktop handle and the desktop of the thread */
1773 if(!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) ||
1774 !(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk))
1775 {
1776 ObDereferenceObject(Thread);
1777 DPRINT1("Desktop information of thread 0x%x broken!?\n", dwThreadId);
1778 RETURN(NULL);
1779 }
1780
1781 /* we could just use DesktopObject instead of looking up the handle, but latter
1782 may be a bit safer (e.g. when the desktop is being destroyed */
1783 /* switch into the context of the thread we're trying to get the desktop from,
1784 so we can use the handle */
1785 KeAttachProcess(&Thread->ThreadsProcess->Pcb);
1786 Status = ObReferenceObjectByHandle(hThreadDesktop,
1787 GENERIC_ALL,
1788 ExDesktopObjectType,
1789 UserMode,
1790 (PVOID*)&DesktopObject,
1791 &HandleInformation);
1792 KeDetachProcess();
1793
1794 /* the handle couldn't be found, there's nothing to get... */
1795 if(!NT_SUCCESS(Status))
1796 {
1797 ObDereferenceObject(Thread);
1798 RETURN(NULL);
1799 }
1800
1801 /* lookup our handle table if we can find a handle to the desktop object,
1802 if not, create one */
1803 Ret = IntGetDesktopObjectHandle(DesktopObject);
1804
1805 /* all done, we got a valid handle to the desktop */
1806 ObDereferenceObject(DesktopObject);
1807 ObDereferenceObject(Thread);
1808 RETURN(Ret);
1809
1810 CLEANUP:
1811 DPRINT("Leave NtUserGetThreadDesktop, ret=%i\n",_ret_);
1812 UserLeave();
1813 END_CLEANUP;
1814 }
1815
1816 static NTSTATUS
1817 IntUnmapDesktopView(IN PDESKTOP DesktopObject)
1818 {
1819 PTHREADINFO ti;
1820 PPROCESSINFO CurrentWin32Process;
1821 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1822 NTSTATUS Status = STATUS_SUCCESS;
1823
1824 TRACE("DO %p\n");
1825
1826 CurrentWin32Process = PsGetCurrentProcessWin32Process();
1827 PrevLink = &CurrentWin32Process->HeapMappings.Next;
1828
1829 /* unmap if we're the last thread using the desktop */
1830 HeapMapping = *PrevLink;
1831 while (HeapMapping != NULL)
1832 {
1833 if (HeapMapping->KernelMapping == (PVOID)DesktopObject->pheapDesktop)
1834 {
1835 if (--HeapMapping->Count == 0)
1836 {
1837 *PrevLink = HeapMapping->Next;
1838
1839 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
1840 HeapMapping->UserMapping);
1841
1842 ObDereferenceObject(DesktopObject);
1843
1844 UserHeapFree(HeapMapping);
1845 break;
1846 }
1847 }
1848
1849 PrevLink = &HeapMapping->Next;
1850 HeapMapping = HeapMapping->Next;
1851 }
1852
1853 ti = GetW32ThreadInfo();
1854 if (ti != NULL)
1855 {
1856 GetWin32ClientInfo()->pDeskInfo = NULL;
1857 }
1858 GetWin32ClientInfo()->ulClientDelta = 0;
1859
1860 return Status;
1861 }
1862
1863 static NTSTATUS
1864 IntMapDesktopView(IN PDESKTOP DesktopObject)
1865 {
1866 PPROCESSINFO CurrentWin32Process;
1867 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1868 PVOID UserBase = NULL;
1869 SIZE_T ViewSize = 0;
1870 LARGE_INTEGER Offset;
1871 NTSTATUS Status;
1872
1873 CurrentWin32Process = PsGetCurrentProcessWin32Process();
1874 PrevLink = &CurrentWin32Process->HeapMappings.Next;
1875
1876 /* find out if another thread already mapped the desktop heap */
1877 HeapMapping = *PrevLink;
1878 while (HeapMapping != NULL)
1879 {
1880 if (HeapMapping->KernelMapping == (PVOID)DesktopObject->pheapDesktop)
1881 {
1882 HeapMapping->Count++;
1883 return STATUS_SUCCESS;
1884 }
1885
1886 PrevLink = &HeapMapping->Next;
1887 HeapMapping = HeapMapping->Next;
1888 }
1889
1890 /* we're the first, map the heap */
1891 DPRINT("Noone mapped the desktop heap %p yet, so - map it!\n", DesktopObject->pheapDesktop);
1892 Offset.QuadPart = 0;
1893 Status = MmMapViewOfSection(DesktopObject->hsectionDesktop,
1894 PsGetCurrentProcess(),
1895 &UserBase,
1896 0,
1897 0,
1898 &Offset,
1899 &ViewSize,
1900 ViewUnmap,
1901 SEC_NO_CHANGE,
1902 PAGE_EXECUTE_READ); /* would prefer PAGE_READONLY, but thanks to RTL heaps... */
1903 if (!NT_SUCCESS(Status))
1904 {
1905 DPRINT1("Failed to map desktop\n");
1906 return Status;
1907 }
1908
1909 /* add the mapping */
1910 HeapMapping = UserHeapAlloc(sizeof(W32HEAP_USER_MAPPING));
1911 if (HeapMapping == NULL)
1912 {
1913 MmUnmapViewOfSection(PsGetCurrentProcess(),
1914 UserBase);
1915 DPRINT1("UserHeapAlloc() failed!\n");
1916 return STATUS_NO_MEMORY;
1917 }
1918
1919 HeapMapping->Next = NULL;
1920 HeapMapping->KernelMapping = (PVOID)DesktopObject->pheapDesktop;
1921 HeapMapping->UserMapping = UserBase;
1922 HeapMapping->Limit = ViewSize;
1923 HeapMapping->Count = 1;
1924 *PrevLink = HeapMapping;
1925
1926 ObReferenceObject(DesktopObject);
1927
1928 return STATUS_SUCCESS;
1929 }
1930
1931 BOOL
1932 IntSetThreadDesktop(IN PDESKTOP DesktopObject,
1933 IN BOOL FreeOnFailure)
1934 {
1935 PDESKTOP OldDesktop;
1936 PTHREADINFO W32Thread;
1937 NTSTATUS Status;
1938 BOOL MapHeap;
1939 CLIENTTHREADINFO ctiSave;
1940
1941 DPRINT("IntSetThreadDesktop() DO=%p, FOF=%d\n", DesktopObject, FreeOnFailure);
1942 MapHeap = (PsGetCurrentProcess() != PsInitialSystemProcess);
1943 W32Thread = PsGetCurrentThreadWin32Thread();
1944
1945 if (W32Thread->rpdesk != DesktopObject)
1946 {
1947 OldDesktop = W32Thread->rpdesk;
1948
1949 if (!IsListEmpty(&W32Thread->WindowListHead))
1950 {
1951 DPRINT1("Attempted to change thread desktop although the thread has windows!\n");
1952 EngSetLastError(ERROR_BUSY);
1953 return FALSE;
1954 }
1955
1956 W32Thread->rpdesk = DesktopObject;
1957
1958 if (MapHeap && DesktopObject != NULL)
1959 {
1960 Status = IntMapDesktopView(DesktopObject);
1961 if (!NT_SUCCESS(Status))
1962 {
1963 SetLastNtError(Status);
1964 return FALSE;
1965 }
1966 W32Thread->pDeskInfo = DesktopObject->pDeskInfo;
1967 }
1968
1969 RtlZeroMemory(&ctiSave, sizeof(CLIENTTHREADINFO));
1970
1971 if (W32Thread->pcti && OldDesktop && NtCurrentTeb())
1972 {
1973 RtlCopyMemory(&ctiSave, W32Thread->pcti, sizeof(CLIENTTHREADINFO));
1974 DPRINT("Free ClientThreadInfo\n");
1975 DesktopHeapFree(OldDesktop, W32Thread->pcti);
1976 W32Thread->pcti = NULL;
1977 }
1978
1979 if (!W32Thread->pcti && DesktopObject && NtCurrentTeb())
1980 {
1981 DPRINT("Allocate ClientThreadInfo\n");
1982 W32Thread->pcti = DesktopHeapAlloc( DesktopObject,
1983 sizeof(CLIENTTHREADINFO));
1984 RtlCopyMemory(W32Thread->pcti, &ctiSave, sizeof(CLIENTTHREADINFO));
1985 }
1986
1987 if (NtCurrentTeb())
1988 {
1989 PCLIENTINFO pci = GetWin32ClientInfo();
1990 pci->ulClientDelta = DesktopHeapGetUserDelta();
1991 if (DesktopObject)
1992 {
1993 pci->pDeskInfo = (PVOID)((ULONG_PTR)DesktopObject->pDeskInfo - pci->ulClientDelta);
1994 if (W32Thread->pcti) pci->pClientThreadInfo = (PVOID)((ULONG_PTR)W32Thread->pcti - pci->ulClientDelta);
1995 }
1996 }
1997
1998 if (OldDesktop != NULL &&
1999 !IntCheckProcessDesktopClasses(OldDesktop,
2000 FreeOnFailure))
2001 {
2002 DPRINT1("Failed to move process classes to shared heap!\n");
2003
2004 /* failed to move desktop classes to the shared heap,
2005 unmap the view and return the error */
2006 if (MapHeap && DesktopObject != NULL)
2007 IntUnmapDesktopView(DesktopObject);
2008
2009 return FALSE;
2010 }
2011
2012 /* Remove the thread from the old desktop's list */
2013 RemoveEntryList(&W32Thread->PtiLink);
2014
2015 if (DesktopObject != NULL)
2016 {
2017 ObReferenceObject(DesktopObject);
2018 /* Insert into new desktop's list */
2019 InsertTailList(&DesktopObject->PtiList, &W32Thread->PtiLink);
2020 }
2021
2022 if (OldDesktop != NULL)
2023 {
2024 if (MapHeap)
2025 {
2026 IntUnmapDesktopView(OldDesktop);
2027 }
2028
2029 ObDereferenceObject(OldDesktop);
2030 }
2031 }
2032
2033 return TRUE;
2034 }
2035
2036 /*
2037 * NtUserSetThreadDesktop
2038 *
2039 * Status
2040 * @implemented
2041 */
2042
2043 BOOL APIENTRY
2044 NtUserSetThreadDesktop(HDESK hDesktop)
2045 {
2046 PDESKTOP DesktopObject;
2047 NTSTATUS Status;
2048 DECLARE_RETURN(BOOL);
2049
2050 UserEnterExclusive();
2051 DPRINT("Enter NtUserSetThreadDesktop\n");
2052
2053 /* Validate the new desktop. */
2054 Status = IntValidateDesktopHandle(
2055 hDesktop,
2056 UserMode,
2057 0,
2058 &DesktopObject);
2059
2060 if (!NT_SUCCESS(Status))
2061 {
2062 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
2063 RETURN(FALSE);
2064 }
2065
2066 /* FIXME: Should check here to see if the thread has any windows. */
2067
2068 if (!IntSetThreadDesktop(DesktopObject,
2069 FALSE))
2070 {
2071 RETURN(FALSE);
2072 }
2073
2074 RETURN(TRUE);
2075
2076 CLEANUP:
2077 DPRINT("Leave NtUserSetThreadDesktop, ret=%i\n",_ret_);
2078 UserLeave();
2079 END_CLEANUP;
2080 }
2081
2082 /* EOF */