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