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