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