9b6e098c198bb7c1febf7cf1c41b08c78b7921d2
[reactos.git] / reactos / subsys / win32k / ntuser / desktop.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Desktops
24 * FILE: subsys/win32k/ntuser/desktop.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * REVISION HISTORY:
27 * 06-06-2001 CSH Created
28 */
29
30 /* INCLUDES ******************************************************************/
31
32 #include <w32k.h>
33
34 #define NDEBUG
35 #include <debug.h>
36
37 /* GLOBALS *******************************************************************/
38
39 /* Currently active desktop */
40 PDESKTOP_OBJECT InputDesktop = NULL;
41 HDESK InputDesktopHandle = NULL;
42 HDC ScreenDeviceContext = NULL;
43
44 BOOL g_PaintDesktopVersion = FALSE;
45
46 /* INITALIZATION FUNCTIONS ****************************************************/
47
48 static GENERIC_MAPPING IntDesktopMapping =
49 {
50 STANDARD_RIGHTS_READ | DESKTOP_ENUMERATE | DESKTOP_READOBJECTS,
51 STANDARD_RIGHTS_WRITE | DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_HOOKCONTROL |
52 DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD | DESKTOP_WRITEOBJECTS,
53 STANDARD_RIGHTS_EXECUTE | DESKTOP_SWITCHDESKTOP,
54 STANDARD_RIGHTS_REQUIRED | DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE |
55 DESKTOP_HOOKCONTROL | DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD |
56 DESKTOP_READOBJECTS | DESKTOP_SWITCHDESKTOP | DESKTOP_WRITEOBJECTS
57 };
58
59 NTSTATUS FASTCALL
60 InitDesktopImpl(VOID)
61 {
62 /* Set Desktop Object Attributes */
63 ExDesktopObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(DESKTOP_OBJECT);
64 ExDesktopObjectType->TypeInfo.GenericMapping = IntDesktopMapping;
65
66 return STATUS_SUCCESS;
67 }
68
69 NTSTATUS FASTCALL
70 CleanupDesktopImpl(VOID)
71 {
72 return STATUS_SUCCESS;
73 }
74
75 /* OBJECT CALLBACKS **********************************************************/
76
77 NTSTATUS STDCALL
78 IntDesktopObjectCreate(PVOID ObjectBody,
79 PVOID Parent,
80 PWSTR RemainingPath,
81 struct _OBJECT_ATTRIBUTES* ObjectAttributes)
82 {
83 PDESKTOP_OBJECT Desktop = (PDESKTOP_OBJECT)ObjectBody;
84 UNICODE_STRING UnicodeString;
85
86 DPRINT("Creating desktop (0x%X) Name (%S)\n", Desktop, RemainingPath);
87 if (RemainingPath == NULL)
88 {
89 return STATUS_SUCCESS;
90 }
91
92 if (wcschr((RemainingPath + 1), '\\') != NULL)
93 {
94 return STATUS_UNSUCCESSFUL;
95 }
96
97 RtlInitUnicodeString(&UnicodeString, (RemainingPath + 1));
98
99
100
101 KeInitializeSpinLock(&Desktop->Lock);
102 InitializeListHead(&Desktop->ShellHookWindows);
103
104 Desktop->WindowStation = (PWINSTATION_OBJECT)Parent;
105
106 /* Put the desktop on the window station's list of associcated desktops */
107 ExInterlockedInsertTailList(
108 &Desktop->WindowStation->DesktopListHead,
109 &Desktop->ListEntry,
110 &Desktop->WindowStation->Lock);
111
112 return RtlCreateUnicodeString(&Desktop->Name, UnicodeString.Buffer);
113 }
114
115 VOID STDCALL
116 IntDesktopObjectDelete(PVOID DeletedObject)
117 {
118 PDESKTOP_OBJECT Desktop = (PDESKTOP_OBJECT)DeletedObject;
119 KIRQL OldIrql;
120
121 DPRINT("Deleting desktop (0x%X)\n", Desktop);
122
123 /* Remove the desktop from the window station's list of associcated desktops */
124 KeAcquireSpinLock(&Desktop->WindowStation->Lock, &OldIrql);
125 RemoveEntryList(&Desktop->ListEntry);
126 KeReleaseSpinLock(&Desktop->WindowStation->Lock, OldIrql);
127
128 RtlFreeUnicodeString(&Desktop->Name);
129 }
130
131 /* PRIVATE FUNCTIONS **********************************************************/
132
133 NTSTATUS FASTCALL
134 IntParseDesktopPath(PEPROCESS Process,
135 PUNICODE_STRING DesktopPath,
136 HWINSTA *hWinSta,
137 HDESK *hDesktop)
138 {
139 OBJECT_ATTRIBUTES ObjectAttributes;
140 UNICODE_STRING WinSta, Desktop, FullName;
141 BOOL DesktopPresent = FALSE;
142 BOOL WinStaPresent = FALSE;
143 NTSTATUS Status;
144
145 ASSERT(hWinSta);
146
147 *hWinSta = NULL;
148
149 if(hDesktop != NULL)
150 {
151 *hDesktop = NULL;
152 }
153
154 RtlInitUnicodeString(&WinSta, NULL);
155 RtlInitUnicodeString(&Desktop, NULL);
156
157 if(DesktopPath != NULL && DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
158 {
159 PWCHAR c = DesktopPath->Buffer;
160 USHORT wl = 0;
161 USHORT l = DesktopPath->Length;
162
163 /*
164 * Parse the desktop path string which can be in the form "WinSta\Desktop"
165 * or just "Desktop". In latter case WinSta0 will be used.
166 */
167
168 while(l > 0)
169 {
170 if(*c == L'\\')
171 {
172 wl = (ULONG_PTR)c - (ULONG_PTR)DesktopPath->Buffer;
173 break;
174 }
175 l -= sizeof(WCHAR);
176 c++;
177 }
178
179 if(wl > 0)
180 {
181 WinSta.Length = wl;
182 WinSta.MaximumLength = wl + sizeof(WCHAR);
183 WinSta.Buffer = DesktopPath->Buffer;
184
185 WinStaPresent = TRUE;
186 c++;
187 }
188
189 Desktop.Length = DesktopPath->Length - wl;
190 if(wl > 0)
191 {
192 Desktop.Length -= sizeof(WCHAR);
193 }
194 if(Desktop.Length > 0)
195 {
196 Desktop.MaximumLength = Desktop.Length + sizeof(WCHAR);
197 Desktop.Buffer = ((wl > 0) ? c : DesktopPath->Buffer);
198 DesktopPresent = TRUE;
199 }
200 }
201
202 if(!WinStaPresent)
203 {
204 /* search the process handle table for (inherited) window station
205 handles, use a more appropriate one than WinSta0 if possible. */
206 Status = ObFindHandleForObject(Process,
207 NULL,
208 ExWindowStationObjectType,
209 NULL,
210 (PHANDLE)hWinSta);
211 if(!NT_SUCCESS(Status))
212 {
213 /* we had no luck searching for opened handles, use WinSta0 now */
214 RtlInitUnicodeString(&WinSta, L"WinSta0");
215 }
216 }
217
218 if(!DesktopPresent && hDesktop != NULL)
219 {
220 /* search the process handle table for (inherited) desktop
221 handles, use a more appropriate one than Default if possible. */
222 Status = ObFindHandleForObject(Process,
223 NULL,
224 ExDesktopObjectType,
225 NULL,
226 (PHANDLE)hDesktop);
227 if(!NT_SUCCESS(Status))
228 {
229 /* we had no luck searching for opened handles, use Desktop now */
230 RtlInitUnicodeString(&Desktop, L"Default");
231 }
232 }
233
234 if(*hWinSta == NULL)
235 {
236 if(!IntGetFullWindowStationName(&FullName, &WinSta, NULL))
237 {
238 return STATUS_INSUFFICIENT_RESOURCES;
239 }
240
241 /* open the window station */
242 InitializeObjectAttributes(&ObjectAttributes,
243 &FullName,
244 OBJ_CASE_INSENSITIVE,
245 NULL,
246 NULL);
247
248 Status = ObOpenObjectByName(&ObjectAttributes,
249 ExWindowStationObjectType,
250 NULL,
251 KernelMode,
252 0,
253 NULL,
254 (HANDLE*)hWinSta);
255
256 RtlFreeUnicodeString(&FullName);
257
258 if(!NT_SUCCESS(Status))
259 {
260 SetLastNtError(Status);
261 DPRINT("Failed to reference window station %wZ PID: %d!\n", &WinSta, PsGetCurrentProcessId());
262 return Status;
263 }
264 }
265
266 if(hDesktop != NULL && *hDesktop == NULL)
267 {
268 if(!IntGetFullWindowStationName(&FullName, &WinSta, &Desktop))
269 {
270 NtClose(*hWinSta);
271 *hWinSta = NULL;
272 return STATUS_INSUFFICIENT_RESOURCES;
273 }
274
275 /* open the desktop object */
276 InitializeObjectAttributes(&ObjectAttributes,
277 &FullName,
278 OBJ_CASE_INSENSITIVE,
279 NULL,
280 NULL);
281
282 Status = ObOpenObjectByName(&ObjectAttributes,
283 ExDesktopObjectType,
284 NULL,
285 KernelMode,
286 0,
287 NULL,
288 (HANDLE*)hDesktop);
289
290 RtlFreeUnicodeString(&FullName);
291
292 if(!NT_SUCCESS(Status))
293 {
294 *hDesktop = NULL;
295 NtClose(*hWinSta);
296 *hWinSta = NULL;
297 SetLastNtError(Status);
298 DPRINT("Failed to reference desktop %wZ PID: %d!\n", &Desktop, PsGetCurrentProcessId());
299 return Status;
300 }
301 }
302
303 return STATUS_SUCCESS;
304 }
305
306 /*
307 * IntValidateDesktopHandle
308 *
309 * Validates the desktop handle.
310 *
311 * Remarks
312 * If the function succeeds, the handle remains referenced. If the
313 * fucntion fails, last error is set.
314 */
315
316 NTSTATUS FASTCALL
317 IntValidateDesktopHandle(
318 HDESK Desktop,
319 KPROCESSOR_MODE AccessMode,
320 ACCESS_MASK DesiredAccess,
321 PDESKTOP_OBJECT *Object)
322 {
323 NTSTATUS Status;
324
325 Status = ObReferenceObjectByHandle(
326 Desktop,
327 DesiredAccess,
328 ExDesktopObjectType,
329 AccessMode,
330 (PVOID*)Object,
331 NULL);
332
333 if (!NT_SUCCESS(Status))
334 SetLastNtError(Status);
335
336 return Status;
337 }
338
339 VOID FASTCALL
340 IntGetDesktopWorkArea(PDESKTOP_OBJECT Desktop, PRECT Rect)
341 {
342 PRECT Ret;
343
344 ASSERT(Desktop);
345
346 Ret = &Desktop->WorkArea;
347 if((Ret->right == -1) && ScreenDeviceContext)
348 {
349 PDC dc;
350 BITMAPOBJ *BitmapObj;
351 dc = DC_LockDc(ScreenDeviceContext);
352 /* FIXME - Handle dc == NULL!!!! */
353 BitmapObj = BITMAPOBJ_LockBitmap(dc->w.hBitmap);
354 if(BitmapObj)
355 {
356 Ret->right = BitmapObj->SurfObj.sizlBitmap.cx;
357 Ret->bottom = BitmapObj->SurfObj.sizlBitmap.cy;
358 BITMAPOBJ_UnlockBitmap(BitmapObj);
359 }
360 DC_UnlockDc(dc);
361 }
362
363 if(Rect)
364 {
365 *Rect = *Ret;
366 }
367 }
368
369 PDESKTOP_OBJECT FASTCALL
370 IntGetActiveDesktop(VOID)
371 {
372 return InputDesktop;
373 }
374
375 /*
376 * returns or creates a handle to the desktop object
377 */
378 HDESK FASTCALL
379 IntGetDesktopObjectHandle(PDESKTOP_OBJECT DesktopObject)
380 {
381 NTSTATUS Status;
382 HDESK Ret;
383
384 ASSERT(DesktopObject);
385
386 Status = ObFindHandleForObject(PsGetCurrentProcess(),
387 DesktopObject,
388 ExDesktopObjectType,
389 NULL,
390 (PHANDLE)&Ret);
391
392 if(!NT_SUCCESS(Status))
393 {
394 Status = ObOpenObjectByPointer(DesktopObject,
395 0,
396 NULL,
397 0,
398 ExDesktopObjectType,
399 UserMode,
400 (PHANDLE)&Ret);
401 if(!NT_SUCCESS(Status))
402 {
403 /* unable to create a handle */
404 DPRINT1("Unable to create a desktop handle\n");
405 return NULL;
406 }
407 }
408
409 return Ret;
410 }
411
412 PUSER_MESSAGE_QUEUE FASTCALL
413 IntGetFocusMessageQueue(VOID)
414 {
415 PDESKTOP_OBJECT pdo = IntGetActiveDesktop();
416 if (!pdo)
417 {
418 DPRINT("No active desktop\n");
419 return(NULL);
420 }
421 return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
422 }
423
424 VOID FASTCALL
425 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue)
426 {
427 PUSER_MESSAGE_QUEUE Old;
428 PDESKTOP_OBJECT pdo = IntGetActiveDesktop();
429 if (!pdo)
430 {
431 DPRINT("No active desktop\n");
432 return;
433 }
434 if(NewQueue != NULL)
435 {
436 if(NewQueue->Desktop != NULL)
437 {
438 DPRINT("Message Queue already attached to another desktop!\n");
439 return;
440 }
441 IntReferenceMessageQueue(NewQueue);
442 InterlockedExchange((LONG*)&NewQueue->Desktop, (LONG)pdo);
443 }
444 Old = (PUSER_MESSAGE_QUEUE)InterlockedExchange((LONG*)&pdo->ActiveMessageQueue, (LONG)NewQueue);
445 if(Old != NULL)
446 {
447 InterlockedExchange((LONG*)&Old->Desktop, 0);
448 IntDereferenceMessageQueue(Old);
449 }
450 }
451
452 HWND FASTCALL IntGetDesktopWindow(VOID)
453 {
454 PDESKTOP_OBJECT pdo = IntGetActiveDesktop();
455 if (!pdo)
456 {
457 DPRINT("No active desktop\n");
458 return NULL;
459 }
460 return pdo->DesktopWindow;
461 }
462
463 PWINDOW_OBJECT FASTCALL UserGetDesktopWindow(VOID)
464 {
465 PDESKTOP_OBJECT pdo = IntGetActiveDesktop();
466
467 if (!pdo)
468 {
469 DPRINT("No active desktop\n");
470 return NULL;
471 }
472
473 return UserGetWindowObject(pdo->DesktopWindow);
474 }
475
476
477 HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
478 {
479 PDESKTOP_OBJECT pdo = PsGetWin32Thread()->Desktop;
480 if (NULL == pdo)
481 {
482 DPRINT1("Thread doesn't have a desktop\n");
483 return NULL;
484 }
485 return pdo->DesktopWindow;
486 }
487
488 BOOL FASTCALL IntDesktopUpdatePerUserSettings(BOOL bEnable)
489 {
490 if (bEnable)
491 {
492 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
493 NTSTATUS Status;
494
495 RtlZeroMemory(QueryTable, sizeof(QueryTable));
496
497 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
498 QueryTable[0].Name = L"PaintDesktopVersion";
499 QueryTable[0].EntryContext = &g_PaintDesktopVersion;
500
501 /* Query the "PaintDesktopVersion" flag in the "Control Panel\Desktop" key */
502 Status = RtlQueryRegistryValues(RTL_REGISTRY_USER,
503 L"Control Panel\\Desktop",
504 QueryTable, NULL, NULL);
505 if (!NT_SUCCESS(Status))
506 {
507 DPRINT1("RtlQueryRegistryValues failed for PaintDesktopVersion (%x)\n",
508 Status);
509 g_PaintDesktopVersion = FALSE;
510 return FALSE;
511 }
512
513 DPRINT("PaintDesktopVersion = %d\n", g_PaintDesktopVersion);
514
515 return TRUE;
516 }
517 else
518 {
519 g_PaintDesktopVersion = FALSE;
520 return TRUE;
521 }
522 }
523
524 /* PUBLIC FUNCTIONS ***********************************************************/
525
526 NTSTATUS FASTCALL
527 co_IntShowDesktop(PDESKTOP_OBJECT Desktop, ULONG Width, ULONG Height)
528 {
529 CSR_API_MESSAGE Request;
530
531 Request.Type = MAKE_CSR_API(SHOW_DESKTOP, CSR_GUI);
532 Request.Data.ShowDesktopRequest.DesktopWindow = Desktop->DesktopWindow;
533 Request.Data.ShowDesktopRequest.Width = Width;
534 Request.Data.ShowDesktopRequest.Height = Height;
535
536 return co_CsrNotify(&Request);
537 }
538
539 NTSTATUS FASTCALL
540 IntHideDesktop(PDESKTOP_OBJECT Desktop)
541 {
542 #if 0
543 CSRSS_API_REQUEST Request;
544 CSRSS_API_REPLY Reply;
545
546 Request.Type = CSRSS_HIDE_DESKTOP;
547 Request.Data.HideDesktopRequest.DesktopWindow = Desktop->DesktopWindow;
548
549 return NotifyCsrss(&Request, &Reply);
550 #else
551
552 PWINDOW_OBJECT DesktopWindow;
553
554 DesktopWindow = IntGetWindowObject(Desktop->DesktopWindow);
555 if (! DesktopWindow)
556 {
557 return ERROR_INVALID_WINDOW_HANDLE;
558 }
559 DesktopWindow->Style &= ~WS_VISIBLE;
560
561 return STATUS_SUCCESS;
562 #endif
563 }
564
565 /*
566 * Send the Message to the windows registered for ShellHook
567 * notifications. The lParam contents depend on the Message. See
568 * MSDN for more details (RegisterShellHookWindow)
569 */
570 VOID co_IntShellHookNotify(WPARAM Message, LPARAM lParam)
571 {
572 PDESKTOP_OBJECT Desktop = IntGetActiveDesktop();
573 PLIST_ENTRY Entry, Entry2;
574 PSHELL_HOOK_WINDOW Current;
575 KIRQL OldLevel;
576
577 static UINT MsgType = 0;
578
579 if (!MsgType)
580 {
581
582 /* Too bad, this doesn't work.*/
583 #if 0
584 UNICODE_STRING Str;
585 RtlInitUnicodeString(&Str, L"SHELLHOOK");
586 MsgType = UserRegisterWindowMessage(&Str);
587 #endif
588
589 MsgType = IntAddAtom(L"SHELLHOOK");
590
591 DPRINT("MsgType = %x\n", MsgType);
592 if (!MsgType)
593 DPRINT1("LastError: %x\n", GetLastNtError());
594 }
595
596 if (!Desktop)
597 {
598 DPRINT1("IntShellHookNotify: No desktop!\n");
599 return;
600 }
601
602 /* We have to do some tricks because the list could change
603 * between calls, and we can't keep the lock during the call
604 */
605
606 KeAcquireSpinLock(&Desktop->Lock, &OldLevel);
607 Entry = Desktop->ShellHookWindows.Flink;
608 while (Entry != &Desktop->ShellHookWindows)
609 {
610 Current = CONTAINING_RECORD(Entry, SHELL_HOOK_WINDOW, ListEntry);
611 KeReleaseSpinLock(&Desktop->Lock, OldLevel);
612
613 DPRINT("Sending notify\n");
614 co_IntPostOrSendMessage(Current->hWnd,
615 MsgType,
616 Message,
617 lParam);
618
619 /* Loop again to find the window we were sending to. If it doesn't
620 * exist anymore, we just stop. This could leave an infinite loop
621 * if a window is removed and readded to the list. That's quite
622 * unlikely though.
623 */
624
625 KeAcquireSpinLock(&Desktop->Lock, &OldLevel);
626 Entry2 = Desktop->ShellHookWindows.Flink;
627 while (Entry2 != Entry &&
628 Entry2 != &Desktop->ShellHookWindows)
629 {
630 Entry2 = Entry2->Flink;
631 }
632
633 if (Entry2 == Entry)
634 Entry = Entry->Flink;
635 else
636 break;
637 }
638 KeReleaseSpinLock(&Desktop->Lock, OldLevel);
639 }
640
641 /*
642 * Add the window to the ShellHookWindows list. The windows
643 * on that list get notifications that are important to shell
644 * type applications.
645 *
646 * TODO: Validate the window? I'm not sure if sending these messages to
647 * an unsuspecting application that is not your own is a nice thing to do.
648 */
649 BOOL IntRegisterShellHookWindow(HWND hWnd)
650 {
651 PDESKTOP_OBJECT Desktop = PsGetWin32Thread()->Desktop;
652 PSHELL_HOOK_WINDOW Entry;
653 KIRQL OldLevel;
654
655 DPRINT("IntRegisterShellHookWindow\n");
656
657 /* First deregister the window, so we can be sure it's never twice in the
658 * list.
659 */
660 IntDeRegisterShellHookWindow(hWnd);
661
662 Entry = ExAllocatePoolWithTag(NonPagedPool,
663 sizeof(SHELL_HOOK_WINDOW),
664 TAG_WINSTA);
665 /* We have to walk this structure with while holding a spinlock, so we
666 * need NonPagedPool */
667
668 if (!Entry)
669 return FALSE;
670
671 Entry->hWnd = hWnd;
672
673 KeAcquireSpinLock(&Desktop->Lock, &OldLevel);
674 InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
675 KeReleaseSpinLock(&Desktop->Lock, OldLevel);
676
677 return TRUE;
678 }
679
680 /*
681 * Remove the window from the ShellHookWindows list. The windows
682 * on that list get notifications that are important to shell
683 * type applications.
684 */
685 BOOL IntDeRegisterShellHookWindow(HWND hWnd)
686 {
687 PDESKTOP_OBJECT Desktop = PsGetWin32Thread()->Desktop;
688 PLIST_ENTRY Entry;
689 PSHELL_HOOK_WINDOW Current;
690 KIRQL OldLevel;
691
692 KeAcquireSpinLock(&Desktop->Lock, &OldLevel);
693
694 Entry = Desktop->ShellHookWindows.Flink;
695 while (Entry != &Desktop->ShellHookWindows)
696 {
697 Current = CONTAINING_RECORD(Entry, SHELL_HOOK_WINDOW, ListEntry);
698 if (Current->hWnd == hWnd)
699 {
700 RemoveEntryList(Entry);
701 KeReleaseSpinLock(&Desktop->Lock, OldLevel);
702 ExFreePool(Entry);
703 return TRUE;
704 }
705 Entry = Entry->Flink;
706 }
707
708 KeReleaseSpinLock(&Desktop->Lock, OldLevel);
709
710 return FALSE;
711 }
712
713 /*
714 * NtUserCreateDesktop
715 *
716 * Creates a new desktop.
717 *
718 * Parameters
719 * lpszDesktopName
720 * Name of the new desktop.
721 *
722 * dwFlags
723 * Interaction flags.
724 *
725 * dwDesiredAccess
726 * Requested type of access.
727 *
728 * lpSecurity
729 * Security descriptor.
730 *
731 * hWindowStation
732 * Handle to window station on which to create the desktop.
733 *
734 * Return Value
735 * If the function succeeds, the return value is a handle to the newly
736 * created desktop. If the specified desktop already exists, the function
737 * succeeds and returns a handle to the existing desktop. When you are
738 * finished using the handle, call the CloseDesktop function to close it.
739 * If the function fails, the return value is NULL.
740 *
741 * Status
742 * @implemented
743 */
744
745 HDESK STDCALL
746 NtUserCreateDesktop(
747 PUNICODE_STRING lpszDesktopName,
748 DWORD dwFlags,
749 ACCESS_MASK dwDesiredAccess,
750 LPSECURITY_ATTRIBUTES lpSecurity,
751 HWINSTA hWindowStation)
752 {
753 OBJECT_ATTRIBUTES ObjectAttributes;
754 PWINSTATION_OBJECT WinStaObject;
755 PDESKTOP_OBJECT DesktopObject;
756 UNICODE_STRING DesktopName;
757 NTSTATUS Status;
758 HDESK Desktop;
759 CSR_API_MESSAGE Request;
760 DECLARE_RETURN(HDESK);
761
762 DPRINT("Enter CreateDesktop: %wZ\n", lpszDesktopName);
763 UserEnterExclusive();
764
765 Status = IntValidateWindowStationHandle(
766 hWindowStation,
767 KernelMode,
768 0, /* FIXME - WINSTA_CREATEDESKTOP */
769 &WinStaObject);
770
771 if (! NT_SUCCESS(Status))
772 {
773 DPRINT1("Failed validation of window station handle (0x%X), cannot create desktop %wZ\n",
774 hWindowStation, lpszDesktopName);
775 SetLastNtError(Status);
776 RETURN( NULL);
777 }
778
779 if (! IntGetFullWindowStationName(&DesktopName, &WinStaObject->Name,
780 lpszDesktopName))
781 {
782 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
783 ObDereferenceObject(WinStaObject);
784 RETURN( NULL);
785 }
786
787 ObDereferenceObject(WinStaObject);
788
789 /*
790 * Try to open already existing desktop
791 */
792
793 DPRINT1("Trying to open desktop (%wZ)\n", &DesktopName);
794
795 /* Initialize ObjectAttributes for the desktop object */
796 InitializeObjectAttributes(
797 &ObjectAttributes,
798 &DesktopName,
799 0,
800 NULL,
801 NULL);
802
803 Status = ObOpenObjectByName(
804 &ObjectAttributes,
805 ExDesktopObjectType,
806 NULL,
807 KernelMode,
808 dwDesiredAccess,
809 NULL,
810 (HANDLE*)&Desktop);
811
812 if (NT_SUCCESS(Status))
813 {
814 DPRINT("Successfully opened desktop (%wZ)\n", &DesktopName);
815 ExFreePool(DesktopName.Buffer);
816 RETURN( Desktop);
817 }
818
819 /*
820 * No existing desktop found, try to create new one
821 */
822
823 Status = ObCreateObject(
824 KernelMode,
825 ExDesktopObjectType,
826 &ObjectAttributes,
827 ExGetPreviousMode(),
828 NULL,
829 sizeof(DESKTOP_OBJECT),
830 0,
831 0,
832 (PVOID*)&DesktopObject);
833
834 if (! NT_SUCCESS(Status))
835 {
836 DPRINT1("Failed creating desktop (%wZ)\n", &DesktopName);
837 ExFreePool(DesktopName.Buffer);
838 SetLastNtError(STATUS_UNSUCCESSFUL);
839 RETURN( NULL);
840 }
841
842 // init desktop area
843 DesktopObject->WorkArea.left = 0;
844 DesktopObject->WorkArea.top = 0;
845 DesktopObject->WorkArea.right = -1;
846 DesktopObject->WorkArea.bottom = -1;
847 IntGetDesktopWorkArea(DesktopObject, NULL);
848
849 /* Initialize some local (to win32k) desktop state. */
850 DesktopObject->ActiveMessageQueue = NULL;
851
852 Status = ObInsertObject(
853 (PVOID)DesktopObject,
854 NULL,
855 STANDARD_RIGHTS_REQUIRED,
856 0,
857 NULL,
858 (HANDLE*)&Desktop);
859
860 ObDereferenceObject(DesktopObject);
861 ExFreePool(DesktopName.Buffer);
862
863 if (! NT_SUCCESS(Status))
864 {
865 DPRINT1("Failed to create desktop handle\n");
866 SetLastNtError(Status);
867 RETURN( NULL);
868 }
869
870 /*
871 * Create a handle for CSRSS and notify CSRSS
872 */
873 Request.Type = MAKE_CSR_API(CREATE_DESKTOP, CSR_GUI);
874 Status = CsrInsertObject(Desktop,
875 GENERIC_ALL,
876 (HANDLE*)&Request.Data.CreateDesktopRequest.DesktopHandle);
877 if (! NT_SUCCESS(Status))
878 {
879 DPRINT1("Failed to create desktop handle for CSRSS\n");
880 ZwClose(Desktop);
881 SetLastNtError(Status);
882 RETURN( NULL);
883 }
884
885 Status = co_CsrNotify(&Request);
886 if (! NT_SUCCESS(Status))
887 {
888 CsrCloseHandle(Request.Data.CreateDesktopRequest.DesktopHandle);
889 DPRINT1("Failed to notify CSRSS about new desktop\n");
890 ZwClose(Desktop);
891 SetLastNtError(Status);
892 RETURN( NULL);
893 }
894
895 RETURN( Desktop);
896
897 CLEANUP:
898 DPRINT("Leave NtUserCreateDesktop, ret=%i\n",_ret_);
899 UserLeave();
900 END_CLEANUP;
901 }
902
903 /*
904 * NtUserOpenDesktop
905 *
906 * Opens an existing desktop.
907 *
908 * Parameters
909 * lpszDesktopName
910 * Name of the existing desktop.
911 *
912 * dwFlags
913 * Interaction flags.
914 *
915 * dwDesiredAccess
916 * Requested type of access.
917 *
918 * Return Value
919 * Handle to the desktop or zero on failure.
920 *
921 * Status
922 * @implemented
923 */
924
925 HDESK STDCALL
926 NtUserOpenDesktop(
927 PUNICODE_STRING lpszDesktopName,
928 DWORD dwFlags,
929 ACCESS_MASK dwDesiredAccess)
930 {
931 OBJECT_ATTRIBUTES ObjectAttributes;
932 PWINSTATION_OBJECT WinStaObject;
933 UNICODE_STRING DesktopName;
934 NTSTATUS Status;
935 HDESK Desktop;
936 DECLARE_RETURN(HDESK);
937
938 DPRINT("Enter NtUserOpenDesktop: %wZ\n", lpszDesktopName);
939 UserEnterExclusive();
940
941 /*
942 * Validate the window station handle and compose the fully
943 * qualified desktop name
944 */
945
946 Status = IntValidateWindowStationHandle(
947 PsGetCurrentProcess()->Win32WindowStation,
948 KernelMode,
949 0,
950 &WinStaObject);
951
952 if (!NT_SUCCESS(Status))
953 {
954 DPRINT1("Failed validation of window station handle (0x%X)\n",
955 PsGetCurrentProcess()->Win32WindowStation);
956 SetLastNtError(Status);
957 RETURN( 0);
958 }
959
960 if (!IntGetFullWindowStationName(&DesktopName, &WinStaObject->Name,
961 lpszDesktopName))
962 {
963 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
964 ObDereferenceObject(WinStaObject);
965 RETURN( 0);
966 }
967
968 ObDereferenceObject(WinStaObject);
969
970 DPRINT1("Trying to open desktop (%wZ)\n", &DesktopName);
971
972 /* Initialize ObjectAttributes for the desktop object */
973 InitializeObjectAttributes(
974 &ObjectAttributes,
975 &DesktopName,
976 0,
977 NULL,
978 NULL);
979
980 Status = ObOpenObjectByName(
981 &ObjectAttributes,
982 ExDesktopObjectType,
983 NULL,
984 UserMode,
985 dwDesiredAccess,
986 NULL,
987 (HANDLE*)&Desktop);
988
989 if (!NT_SUCCESS(Status))
990 {
991 SetLastNtError(Status);
992 ExFreePool(DesktopName.Buffer);
993 RETURN( 0);
994 }
995
996 DPRINT("Successfully opened desktop (%wZ)\n", &DesktopName);
997 ExFreePool(DesktopName.Buffer);
998
999 RETURN( Desktop);
1000
1001 CLEANUP:
1002 DPRINT("Leave NtUserOpenDesktop, ret=%i\n",_ret_);
1003 UserLeave();
1004 END_CLEANUP;
1005 }
1006
1007 /*
1008 * NtUserOpenInputDesktop
1009 *
1010 * Opens the input (interactive) desktop.
1011 *
1012 * Parameters
1013 * dwFlags
1014 * Interaction flags.
1015 *
1016 * fInherit
1017 * Inheritance option.
1018 *
1019 * dwDesiredAccess
1020 * Requested type of access.
1021 *
1022 * Return Value
1023 * Handle to the input desktop or zero on failure.
1024 *
1025 * Status
1026 * @implemented
1027 */
1028
1029 HDESK STDCALL
1030 NtUserOpenInputDesktop(
1031 DWORD dwFlags,
1032 BOOL fInherit,
1033 ACCESS_MASK dwDesiredAccess)
1034 {
1035 PDESKTOP_OBJECT Object;
1036 NTSTATUS Status;
1037 HDESK Desktop;
1038
1039 DPRINT("About to open input desktop\n");
1040
1041 /* Get a pointer to the desktop object */
1042
1043 Status = IntValidateDesktopHandle(
1044 InputDesktopHandle,
1045 UserMode,
1046 0,
1047 &Object);
1048
1049 if (!NT_SUCCESS(Status))
1050 {
1051 DPRINT("Validation of input desktop handle (0x%X) failed\n", InputDesktop);
1052 return (HDESK)0;
1053 }
1054
1055 /* Create a new handle to the object */
1056
1057 Status = ObOpenObjectByPointer(
1058 Object,
1059 0,
1060 NULL,
1061 dwDesiredAccess,
1062 ExDesktopObjectType,
1063 UserMode,
1064 (HANDLE*)&Desktop);
1065
1066 ObDereferenceObject(Object);
1067
1068 if (NT_SUCCESS(Status))
1069 {
1070 DPRINT("Successfully opened input desktop\n");
1071 return (HDESK)Desktop;
1072 }
1073
1074 SetLastNtError(Status);
1075 return (HDESK)0;
1076 }
1077
1078 /*
1079 * NtUserCloseDesktop
1080 *
1081 * Closes a desktop handle.
1082 *
1083 * Parameters
1084 * hDesktop
1085 * Handle to the desktop.
1086 *
1087 * Return Value
1088 * Status
1089 *
1090 * Remarks
1091 * The desktop handle can be created with NtUserCreateDesktop or
1092 * NtUserOpenDesktop. This function will fail if any thread in the calling
1093 * process is using the specified desktop handle or if the handle refers
1094 * to the initial desktop of the calling process.
1095 *
1096 * Status
1097 * @implemented
1098 */
1099
1100 BOOL STDCALL
1101 NtUserCloseDesktop(HDESK hDesktop)
1102 {
1103 PDESKTOP_OBJECT Object;
1104 NTSTATUS Status;
1105
1106 DPRINT("About to close desktop handle (0x%X)\n", hDesktop);
1107
1108 Status = IntValidateDesktopHandle(
1109 hDesktop,
1110 UserMode,
1111 0,
1112 &Object);
1113
1114 if (!NT_SUCCESS(Status))
1115 {
1116 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
1117 return FALSE;
1118 }
1119
1120 ObDereferenceObject(Object);
1121
1122 DPRINT("Closing desktop handle (0x%X)\n", hDesktop);
1123
1124 Status = ZwClose(hDesktop);
1125 if (!NT_SUCCESS(Status))
1126 {
1127 SetLastNtError(Status);
1128 return FALSE;
1129 }
1130
1131 return TRUE;
1132 }
1133
1134
1135 static int GetSystemVersionString(LPWSTR buffer)
1136 {
1137 RTL_OSVERSIONINFOEXW versionInfo;
1138 int len;
1139
1140 versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
1141
1142 if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo)))
1143 return 0;
1144
1145 if (versionInfo.dwMajorVersion <= 4)
1146 len = swprintf(buffer,
1147 L"ReactOS Version %d.%d %s Build %d",
1148 versionInfo.dwMajorVersion, versionInfo.dwMinorVersion,
1149 versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
1150 else
1151 len = swprintf(buffer,
1152 L"ReactOS %s (Build %d)",
1153 versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
1154
1155 return len;
1156 }
1157
1158 /*
1159 * NtUserPaintDesktop
1160 *
1161 * The NtUserPaintDesktop function fills the clipping region in the
1162 * specified device context with the desktop pattern or wallpaper. The
1163 * function is provided primarily for shell desktops.
1164 *
1165 * Parameters
1166 * hDC
1167 * Handle to the device context.
1168 *
1169 * Status
1170 * @implemented
1171 */
1172
1173 BOOL STDCALL
1174 NtUserPaintDesktop(HDC hDC)
1175 {
1176 RECT Rect;
1177 HBRUSH DesktopBrush, PreviousBrush;
1178 HWND hWndDesktop;
1179 BOOL doPatBlt = TRUE;
1180 PWINDOW_OBJECT WndDesktop;
1181 int len;
1182
1183 PWINSTATION_OBJECT WinSta = PsGetWin32Thread()->Desktop->WindowStation;
1184
1185 IntGdiGetClipBox(hDC, &Rect);
1186
1187 hWndDesktop = IntGetDesktopWindow();
1188 if (!(WndDesktop = UserGetWindowObject(hWndDesktop)))
1189 return FALSE;
1190
1191 DesktopBrush = (HBRUSH)IntGetClassLong(WndDesktop, GCL_HBRBACKGROUND, FALSE); //fixme: verify retval
1192
1193
1194 /*
1195 * Paint desktop background
1196 */
1197
1198 if(WinSta->hbmWallpaper != NULL)
1199 {
1200 PWINDOW_OBJECT DeskWin;
1201
1202 if((DeskWin = UserGetWindowObject(hWndDesktop)))
1203 {
1204 SIZE sz;
1205 int x, y;
1206 HDC hWallpaperDC;
1207
1208 sz.cx = DeskWin->WindowRect.right - DeskWin->WindowRect.left;
1209 sz.cy = DeskWin->WindowRect.bottom - DeskWin->WindowRect.top;
1210
1211
1212 x = (sz.cx / 2) - (WinSta->cxWallpaper / 2);
1213 y = (sz.cy / 2) - (WinSta->cyWallpaper / 2);
1214
1215 hWallpaperDC = NtGdiCreateCompatableDC(hDC);
1216 if(hWallpaperDC != NULL)
1217 {
1218 HBITMAP hOldBitmap;
1219
1220 if(x > 0 || y > 0)
1221 {
1222 /* FIXME - clip out the bitmap */
1223 PreviousBrush = NtGdiSelectObject(hDC, DesktopBrush);
1224 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1225 NtGdiSelectObject(hDC, PreviousBrush);
1226 }
1227 else
1228 doPatBlt = FALSE;
1229
1230 hOldBitmap = NtGdiSelectObject(hWallpaperDC, WinSta->hbmWallpaper);
1231 NtGdiBitBlt(hDC, x, y, WinSta->cxWallpaper, WinSta->cyWallpaper, hWallpaperDC, 0, 0, SRCCOPY);
1232 NtGdiSelectObject(hWallpaperDC, hOldBitmap);
1233
1234 NtGdiDeleteDC(hWallpaperDC);
1235 }
1236 }
1237 }
1238
1239 if (doPatBlt)
1240 {
1241 PreviousBrush = NtGdiSelectObject(hDC, DesktopBrush);
1242 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1243 NtGdiSelectObject(hDC, PreviousBrush);
1244 }
1245
1246 /*
1247 * Display system version on the desktop background
1248 */
1249
1250 if (g_PaintDesktopVersion)
1251 {
1252 static WCHAR s_wszVersion[256] = {0};
1253 RECT rect;
1254
1255 if (*s_wszVersion)
1256 len = wcslen(s_wszVersion);
1257 else
1258 len = GetSystemVersionString(s_wszVersion);
1259
1260 if (len)
1261 {
1262 if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0))
1263 {
1264 rect.right = UserGetSystemMetrics(SM_CXSCREEN);
1265 rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
1266 }
1267
1268 COLORREF color_old = NtGdiSetTextColor(hDC, RGB(255,255,255));
1269 UINT align_old = NtGdiSetTextAlign(hDC, TA_RIGHT);
1270 int mode_old = NtGdiSetBkMode(hDC, TRANSPARENT);
1271
1272 NtGdiTextOut(hDC, rect.right-16, rect.bottom-48, s_wszVersion, len);
1273
1274 NtGdiSetBkMode(hDC, mode_old);
1275 NtGdiSetTextAlign(hDC, align_old);
1276 NtGdiSetTextColor(hDC, color_old);
1277 }
1278 }
1279
1280 return TRUE;
1281 }
1282
1283
1284 /*
1285 * NtUserSwitchDesktop
1286 *
1287 * Sets the current input (interactive) desktop.
1288 *
1289 * Parameters
1290 * hDesktop
1291 * Handle to desktop.
1292 *
1293 * Return Value
1294 * Status
1295 *
1296 * Status
1297 * @unimplemented
1298 */
1299
1300 BOOL STDCALL
1301 NtUserSwitchDesktop(HDESK hDesktop)
1302 {
1303 PDESKTOP_OBJECT DesktopObject;
1304 NTSTATUS Status;
1305
1306 DPRINT("About to switch desktop (0x%X)\n", hDesktop);
1307
1308 Status = IntValidateDesktopHandle(
1309 hDesktop,
1310 UserMode,
1311 0,
1312 &DesktopObject);
1313
1314 if (!NT_SUCCESS(Status))
1315 {
1316 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
1317 return FALSE;
1318 }
1319
1320 /*
1321 * Don't allow applications switch the desktop if it's locked, unless the caller
1322 * is the logon application itself
1323 */
1324 if((DesktopObject->WindowStation->Flags & WSS_LOCKED) &&
1325 LogonProcess != NULL && LogonProcess != PsGetWin32Process())
1326 {
1327 ObDereferenceObject(DesktopObject);
1328 DPRINT1("Switching desktop 0x%x denied because the work station is locked!\n", hDesktop);
1329 return FALSE;
1330 }
1331
1332 /* FIXME: Fail if the desktop belong to an invisible window station */
1333 /* FIXME: Fail if the process is associated with a secured
1334 desktop such as Winlogon or Screen-Saver */
1335 /* FIXME: Connect to input device */
1336
1337 /* Set the active desktop in the desktop's window station. */
1338 DesktopObject->WindowStation->ActiveDesktop = DesktopObject;
1339
1340 /* Set the global state. */
1341 InputDesktop = DesktopObject;
1342 InputDesktopHandle = hDesktop;
1343 InputWindowStation = DesktopObject->WindowStation;
1344
1345 ObDereferenceObject(DesktopObject);
1346
1347 return TRUE;
1348 }
1349
1350 /*
1351 * NtUserResolveDesktopForWOW
1352 *
1353 * Status
1354 * @unimplemented
1355 */
1356
1357 DWORD STDCALL
1358 NtUserResolveDesktopForWOW(DWORD Unknown0)
1359 {
1360 UNIMPLEMENTED
1361 return 0;
1362 }
1363
1364 /*
1365 * NtUserGetThreadDesktop
1366 *
1367 * Status
1368 * @implemented
1369 */
1370
1371 HDESK STDCALL
1372 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
1373 {
1374 NTSTATUS Status;
1375 PETHREAD Thread;
1376 PDESKTOP_OBJECT DesktopObject;
1377 HDESK Ret, hThreadDesktop;
1378 OBJECT_HANDLE_INFORMATION HandleInformation;
1379
1380 if(!dwThreadId)
1381 {
1382 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1383 return 0;
1384 }
1385
1386 Status = PsLookupThreadByThreadId((HANDLE)dwThreadId, &Thread);
1387 if(!NT_SUCCESS(Status))
1388 {
1389 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1390 return 0;
1391 }
1392
1393 if(Thread->ThreadsProcess == PsGetCurrentProcess())
1394 {
1395 /* just return the handle, we queried the desktop handle of a thread running
1396 in the same context */
1397 Ret = Thread->Tcb.Win32Thread->hDesktop;
1398 ObDereferenceObject(Thread);
1399 return Ret;
1400 }
1401
1402 /* get the desktop handle and the desktop of the thread */
1403 if(!(hThreadDesktop = Thread->Tcb.Win32Thread->hDesktop) ||
1404 !(DesktopObject = Thread->Tcb.Win32Thread->Desktop))
1405 {
1406 ObDereferenceObject(Thread);
1407 DPRINT1("Desktop information of thread 0x%x broken!?\n", dwThreadId);
1408 return NULL;
1409 }
1410
1411 /* we could just use DesktopObject instead of looking up the handle, but latter
1412 may be a bit safer (e.g. when the desktop is being destroyed */
1413 /* switch into the context of the thread we're trying to get the desktop from,
1414 so we can use the handle */
1415 KeAttachProcess(&Thread->ThreadsProcess->Pcb);
1416 Status = ObReferenceObjectByHandle(hThreadDesktop,
1417 GENERIC_ALL,
1418 ExDesktopObjectType,
1419 UserMode,
1420 (PVOID*)&DesktopObject,
1421 &HandleInformation);
1422 KeDetachProcess();
1423
1424 /* the handle couldn't be found, there's nothing to get... */
1425 if(!NT_SUCCESS(Status))
1426 {
1427 ObDereferenceObject(Thread);
1428 return NULL;
1429 }
1430
1431 /* lookup our handle table if we can find a handle to the desktop object,
1432 if not, create one */
1433 Ret = IntGetDesktopObjectHandle(DesktopObject);
1434
1435 /* all done, we got a valid handle to the desktop */
1436 ObDereferenceObject(DesktopObject);
1437 ObDereferenceObject(Thread);
1438 return Ret;
1439 }
1440
1441 /*
1442 * NtUserSetThreadDesktop
1443 *
1444 * Status
1445 * @implemented
1446 */
1447
1448 BOOL STDCALL
1449 NtUserSetThreadDesktop(HDESK hDesktop)
1450 {
1451 PW32THREAD W32Thread;
1452 PDESKTOP_OBJECT DesktopObject;
1453 NTSTATUS Status;
1454
1455 /* Validate the new desktop. */
1456 Status = IntValidateDesktopHandle(
1457 hDesktop,
1458 UserMode,
1459 0,
1460 &DesktopObject);
1461
1462 if (!NT_SUCCESS(Status))
1463 {
1464 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
1465 return FALSE;
1466 }
1467
1468 W32Thread = PsGetWin32Thread();
1469
1470 /* FIXME: Should check here to see if the thread has any windows. */
1471
1472 if (W32Thread->Desktop != NULL)
1473 {
1474 ObDereferenceObject(W32Thread->Desktop);
1475 }
1476
1477 W32Thread->Desktop = DesktopObject;
1478 W32Thread->hDesktop = hDesktop;
1479
1480 return TRUE;
1481 }
1482
1483 /* EOF */