Large change to modify NTDLL'S CSR Functions to be compatible with NT. They are exter...
[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 CSR_API_MESSAGE Request;
510
511 Request.Type = MAKE_CSR_API(SHOW_DESKTOP, CSR_GUI);
512 Request.Data.ShowDesktopRequest.DesktopWindow = Desktop->DesktopWindow;
513 Request.Data.ShowDesktopRequest.Width = Width;
514 Request.Data.ShowDesktopRequest.Height = Height;
515
516 return CsrNotify(&Request);
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 CSR_API_MESSAGE Request;
732
733 DPRINT("CreateDesktop: %wZ\n", lpszDesktopName);
734
735 Status = IntValidateWindowStationHandle(
736 hWindowStation,
737 KernelMode,
738 0, /* FIXME - WINSTA_CREATEDESKTOP */
739 &WinStaObject);
740
741 if (! NT_SUCCESS(Status))
742 {
743 DPRINT1("Failed validation of window station handle (0x%X), cannot create desktop %wZ\n",
744 hWindowStation, lpszDesktopName);
745 SetLastNtError(Status);
746 return NULL;
747 }
748
749 if (! IntGetFullWindowStationName(&DesktopName, &WinStaObject->Name,
750 lpszDesktopName))
751 {
752 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
753 ObDereferenceObject(WinStaObject);
754 return NULL;
755 }
756
757 ObDereferenceObject(WinStaObject);
758
759 /*
760 * Try to open already existing desktop
761 */
762
763 DPRINT("Trying to open desktop (%wZ)\n", &DesktopName);
764
765 /* Initialize ObjectAttributes for the desktop object */
766 InitializeObjectAttributes(
767 &ObjectAttributes,
768 &DesktopName,
769 0,
770 NULL,
771 NULL);
772
773 Status = ObOpenObjectByName(
774 &ObjectAttributes,
775 ExDesktopObjectType,
776 NULL,
777 UserMode,
778 dwDesiredAccess,
779 NULL,
780 (HANDLE*)&Desktop);
781
782 if (NT_SUCCESS(Status))
783 {
784 DPRINT("Successfully opened desktop (%wZ)\n", &DesktopName);
785 ExFreePool(DesktopName.Buffer);
786 return Desktop;
787 }
788
789 /*
790 * No existing desktop found, try to create new one
791 */
792
793 Status = ObCreateObject(
794 KernelMode,
795 ExDesktopObjectType,
796 &ObjectAttributes,
797 ExGetPreviousMode(),
798 NULL,
799 sizeof(DESKTOP_OBJECT),
800 0,
801 0,
802 (PVOID*)&DesktopObject);
803
804 if (! NT_SUCCESS(Status))
805 {
806 DPRINT1("Failed creating desktop (%wZ)\n", &DesktopName);
807 ExFreePool(DesktopName.Buffer);
808 SetLastNtError(STATUS_UNSUCCESSFUL);
809 return NULL;
810 }
811
812 // init desktop area
813 DesktopObject->WorkArea.left = 0;
814 DesktopObject->WorkArea.top = 0;
815 DesktopObject->WorkArea.right = -1;
816 DesktopObject->WorkArea.bottom = -1;
817 IntGetDesktopWorkArea(DesktopObject, NULL);
818
819 /* Initialize some local (to win32k) desktop state. */
820 DesktopObject->ActiveMessageQueue = NULL;
821
822 Status = ObInsertObject(
823 (PVOID)DesktopObject,
824 NULL,
825 STANDARD_RIGHTS_REQUIRED,
826 0,
827 NULL,
828 (HANDLE*)&Desktop);
829
830 ObDereferenceObject(DesktopObject);
831 ExFreePool(DesktopName.Buffer);
832
833 if (! NT_SUCCESS(Status))
834 {
835 DPRINT1("Failed to create desktop handle\n");
836 SetLastNtError(Status);
837 return NULL;
838 }
839
840 /*
841 * Create a handle for CSRSS and notify CSRSS
842 */
843 Request.Type = MAKE_CSR_API(CREATE_DESKTOP, CSR_GUI);
844 Status = CsrInsertObject(Desktop,
845 GENERIC_ALL,
846 (HANDLE*)&Request.Data.CreateDesktopRequest.DesktopHandle);
847 if (! NT_SUCCESS(Status))
848 {
849 DPRINT1("Failed to create desktop handle for CSRSS\n");
850 ZwClose(Desktop);
851 SetLastNtError(Status);
852 return NULL;
853 }
854
855 Status = CsrNotify(&Request);
856 if (! NT_SUCCESS(Status))
857 {
858 CsrCloseHandle(Request.Data.CreateDesktopRequest.DesktopHandle);
859 DPRINT1("Failed to notify CSRSS about new desktop\n");
860 ZwClose(Desktop);
861 SetLastNtError(Status);
862 return NULL;
863 }
864
865 return Desktop;
866 }
867
868 /*
869 * NtUserOpenDesktop
870 *
871 * Opens an existing desktop.
872 *
873 * Parameters
874 * lpszDesktopName
875 * Name of the existing desktop.
876 *
877 * dwFlags
878 * Interaction flags.
879 *
880 * dwDesiredAccess
881 * Requested type of access.
882 *
883 * Return Value
884 * Handle to the desktop or zero on failure.
885 *
886 * Status
887 * @implemented
888 */
889
890 HDESK STDCALL
891 NtUserOpenDesktop(
892 PUNICODE_STRING lpszDesktopName,
893 DWORD dwFlags,
894 ACCESS_MASK dwDesiredAccess)
895 {
896 OBJECT_ATTRIBUTES ObjectAttributes;
897 PWINSTATION_OBJECT WinStaObject;
898 UNICODE_STRING DesktopName;
899 NTSTATUS Status;
900 HDESK Desktop;
901
902 /*
903 * Validate the window station handle and compose the fully
904 * qualified desktop name
905 */
906
907 Status = IntValidateWindowStationHandle(
908 PsGetCurrentProcess()->Win32WindowStation,
909 KernelMode,
910 0,
911 &WinStaObject);
912
913 if (!NT_SUCCESS(Status))
914 {
915 DPRINT1("Failed validation of window station handle (0x%X)\n",
916 PsGetCurrentProcess()->Win32WindowStation);
917 SetLastNtError(Status);
918 return 0;
919 }
920
921 if (!IntGetFullWindowStationName(&DesktopName, &WinStaObject->Name,
922 lpszDesktopName))
923 {
924 SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
925 ObDereferenceObject(WinStaObject);
926 return 0;
927 }
928
929 ObDereferenceObject(WinStaObject);
930
931 DPRINT1("Trying to open desktop (%wZ)\n", &DesktopName);
932
933 /* Initialize ObjectAttributes for the desktop object */
934 InitializeObjectAttributes(
935 &ObjectAttributes,
936 &DesktopName,
937 0,
938 NULL,
939 NULL);
940
941 Status = ObOpenObjectByName(
942 &ObjectAttributes,
943 ExDesktopObjectType,
944 NULL,
945 UserMode,
946 dwDesiredAccess,
947 NULL,
948 (HANDLE*)&Desktop);
949
950 if (!NT_SUCCESS(Status))
951 {
952 SetLastNtError(Status);
953 ExFreePool(DesktopName.Buffer);
954 return 0;
955 }
956
957 DPRINT("Successfully opened desktop (%wZ)\n", &DesktopName);
958 ExFreePool(DesktopName.Buffer);
959
960 return Desktop;
961 }
962
963 /*
964 * NtUserOpenInputDesktop
965 *
966 * Opens the input (interactive) desktop.
967 *
968 * Parameters
969 * dwFlags
970 * Interaction flags.
971 *
972 * fInherit
973 * Inheritance option.
974 *
975 * dwDesiredAccess
976 * Requested type of access.
977 *
978 * Return Value
979 * Handle to the input desktop or zero on failure.
980 *
981 * Status
982 * @implemented
983 */
984
985 HDESK STDCALL
986 NtUserOpenInputDesktop(
987 DWORD dwFlags,
988 BOOL fInherit,
989 ACCESS_MASK dwDesiredAccess)
990 {
991 PDESKTOP_OBJECT Object;
992 NTSTATUS Status;
993 HDESK Desktop;
994
995 DPRINT("About to open input desktop\n");
996
997 /* Get a pointer to the desktop object */
998
999 Status = IntValidateDesktopHandle(
1000 InputDesktopHandle,
1001 UserMode,
1002 0,
1003 &Object);
1004
1005 if (!NT_SUCCESS(Status))
1006 {
1007 DPRINT("Validation of input desktop handle (0x%X) failed\n", InputDesktop);
1008 return (HDESK)0;
1009 }
1010
1011 /* Create a new handle to the object */
1012
1013 Status = ObOpenObjectByPointer(
1014 Object,
1015 0,
1016 NULL,
1017 dwDesiredAccess,
1018 ExDesktopObjectType,
1019 UserMode,
1020 (HANDLE*)&Desktop);
1021
1022 ObDereferenceObject(Object);
1023
1024 if (NT_SUCCESS(Status))
1025 {
1026 DPRINT("Successfully opened input desktop\n");
1027 return (HDESK)Desktop;
1028 }
1029
1030 SetLastNtError(Status);
1031 return (HDESK)0;
1032 }
1033
1034 /*
1035 * NtUserCloseDesktop
1036 *
1037 * Closes a desktop handle.
1038 *
1039 * Parameters
1040 * hDesktop
1041 * Handle to the desktop.
1042 *
1043 * Return Value
1044 * Status
1045 *
1046 * Remarks
1047 * The desktop handle can be created with NtUserCreateDesktop or
1048 * NtUserOpenDesktop. This function will fail if any thread in the calling
1049 * process is using the specified desktop handle or if the handle refers
1050 * to the initial desktop of the calling process.
1051 *
1052 * Status
1053 * @implemented
1054 */
1055
1056 BOOL STDCALL
1057 NtUserCloseDesktop(HDESK hDesktop)
1058 {
1059 PDESKTOP_OBJECT Object;
1060 NTSTATUS Status;
1061
1062 DPRINT("About to close desktop handle (0x%X)\n", hDesktop);
1063
1064 Status = IntValidateDesktopHandle(
1065 hDesktop,
1066 UserMode,
1067 0,
1068 &Object);
1069
1070 if (!NT_SUCCESS(Status))
1071 {
1072 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
1073 return FALSE;
1074 }
1075
1076 ObDereferenceObject(Object);
1077
1078 DPRINT("Closing desktop handle (0x%X)\n", hDesktop);
1079
1080 Status = ZwClose(hDesktop);
1081 if (!NT_SUCCESS(Status))
1082 {
1083 SetLastNtError(Status);
1084 return FALSE;
1085 }
1086
1087 return TRUE;
1088 }
1089
1090
1091 static int GetSystemVersionString(LPWSTR buffer)
1092 {
1093 RTL_OSVERSIONINFOEXW versionInfo;
1094 int len;
1095
1096 versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
1097
1098 if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo)))
1099 return 0;
1100
1101 if (versionInfo.dwMajorVersion <= 4)
1102 len = swprintf(buffer,
1103 L"ReactOS Version %d.%d %s Build %d",
1104 versionInfo.dwMajorVersion, versionInfo.dwMinorVersion,
1105 versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
1106 else
1107 len = swprintf(buffer,
1108 L"ReactOS %s (Build %d)",
1109 versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
1110
1111 return len;
1112 }
1113
1114 /*
1115 * NtUserPaintDesktop
1116 *
1117 * The NtUserPaintDesktop function fills the clipping region in the
1118 * specified device context with the desktop pattern or wallpaper. The
1119 * function is provided primarily for shell desktops.
1120 *
1121 * Parameters
1122 * hDC
1123 * Handle to the device context.
1124 *
1125 * Status
1126 * @implemented
1127 */
1128
1129 BOOL STDCALL
1130 NtUserPaintDesktop(HDC hDC)
1131 {
1132 RECT Rect;
1133 HBRUSH DesktopBrush, PreviousBrush;
1134 HWND hWndDesktop;
1135 BOOL doPatBlt = TRUE;
1136 int len;
1137
1138 PWINSTATION_OBJECT WinSta = PsGetWin32Thread()->Desktop->WindowStation;
1139
1140 IntGdiGetClipBox(hDC, &Rect);
1141
1142 hWndDesktop = IntGetDesktopWindow();
1143 DesktopBrush = (HBRUSH)NtUserGetClassLong(hWndDesktop, GCL_HBRBACKGROUND, FALSE);
1144
1145 /*
1146 * Paint desktop background
1147 */
1148
1149 if(WinSta->hbmWallpaper != NULL)
1150 {
1151 PWINDOW_OBJECT DeskWin;
1152
1153 if((DeskWin = IntGetWindowObject(hWndDesktop)))
1154 {
1155 SIZE sz;
1156 int x, y;
1157 HDC hWallpaperDC;
1158
1159 sz.cx = DeskWin->WindowRect.right - DeskWin->WindowRect.left;
1160 sz.cy = DeskWin->WindowRect.bottom - DeskWin->WindowRect.top;
1161 IntReleaseWindowObject(DeskWin);
1162
1163 x = (sz.cx / 2) - (WinSta->cxWallpaper / 2);
1164 y = (sz.cy / 2) - (WinSta->cyWallpaper / 2);
1165
1166 hWallpaperDC = NtGdiCreateCompatableDC(hDC);
1167 if(hWallpaperDC != NULL)
1168 {
1169 HBITMAP hOldBitmap;
1170
1171 if(x > 0 || y > 0)
1172 {
1173 /* FIXME - clip out the bitmap */
1174 PreviousBrush = NtGdiSelectObject(hDC, DesktopBrush);
1175 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1176 NtGdiSelectObject(hDC, PreviousBrush);
1177 }
1178 else
1179 doPatBlt = FALSE;
1180
1181 hOldBitmap = NtGdiSelectObject(hWallpaperDC, WinSta->hbmWallpaper);
1182 NtGdiBitBlt(hDC, x, y, WinSta->cxWallpaper, WinSta->cyWallpaper, hWallpaperDC, 0, 0, SRCCOPY);
1183 NtGdiSelectObject(hWallpaperDC, hOldBitmap);
1184
1185 NtGdiDeleteDC(hWallpaperDC);
1186 }
1187 }
1188 }
1189
1190 if (doPatBlt) {
1191 PreviousBrush = NtGdiSelectObject(hDC, DesktopBrush);
1192 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1193 NtGdiSelectObject(hDC, PreviousBrush);
1194 }
1195
1196 /*
1197 * Display system version on the desktop background
1198 */
1199
1200 if (g_PaintDesktopVersion) {
1201 static WCHAR s_wszVersion[256] = {0};
1202 RECT rect;
1203
1204 if (*s_wszVersion)
1205 len = wcslen(s_wszVersion);
1206 else
1207 len = GetSystemVersionString(s_wszVersion);
1208
1209 if (len) {
1210 if (!NtUserSystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0)) {
1211 rect.right = NtUserGetSystemMetrics(SM_CXSCREEN);
1212 rect.bottom = NtUserGetSystemMetrics(SM_CYSCREEN);
1213 }
1214
1215 COLORREF color_old = NtGdiSetTextColor(hDC, RGB(255,255,255));
1216 UINT align_old = NtGdiSetTextAlign(hDC, TA_RIGHT);
1217 int mode_old = NtGdiSetBkMode(hDC, TRANSPARENT);
1218
1219 NtGdiTextOut(hDC, rect.right-16, rect.bottom-48, s_wszVersion, len);
1220
1221 NtGdiSetBkMode(hDC, mode_old);
1222 NtGdiSetTextAlign(hDC, align_old);
1223 NtGdiSetTextColor(hDC, color_old);
1224 }
1225 }
1226
1227 return TRUE;
1228 }
1229
1230
1231 /*
1232 * NtUserSwitchDesktop
1233 *
1234 * Sets the current input (interactive) desktop.
1235 *
1236 * Parameters
1237 * hDesktop
1238 * Handle to desktop.
1239 *
1240 * Return Value
1241 * Status
1242 *
1243 * Status
1244 * @unimplemented
1245 */
1246
1247 BOOL STDCALL
1248 NtUserSwitchDesktop(HDESK hDesktop)
1249 {
1250 PDESKTOP_OBJECT DesktopObject;
1251 NTSTATUS Status;
1252
1253 DPRINT("About to switch desktop (0x%X)\n", hDesktop);
1254
1255 Status = IntValidateDesktopHandle(
1256 hDesktop,
1257 UserMode,
1258 0,
1259 &DesktopObject);
1260
1261 if (!NT_SUCCESS(Status))
1262 {
1263 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
1264 return FALSE;
1265 }
1266
1267 /*
1268 * Don't allow applications switch the desktop if it's locked, unless the caller
1269 * is the logon application itself
1270 */
1271 if((DesktopObject->WindowStation->Flags & WSS_LOCKED) &&
1272 LogonProcess != NULL && LogonProcess != PsGetWin32Process())
1273 {
1274 ObDereferenceObject(DesktopObject);
1275 DPRINT1("Switching desktop 0x%x denied because the work station is locked!\n", hDesktop);
1276 return FALSE;
1277 }
1278
1279 /* FIXME: Fail if the desktop belong to an invisible window station */
1280 /* FIXME: Fail if the process is associated with a secured
1281 desktop such as Winlogon or Screen-Saver */
1282 /* FIXME: Connect to input device */
1283
1284 /* Set the active desktop in the desktop's window station. */
1285 DesktopObject->WindowStation->ActiveDesktop = DesktopObject;
1286
1287 /* Set the global state. */
1288 InputDesktop = DesktopObject;
1289 InputDesktopHandle = hDesktop;
1290 InputWindowStation = DesktopObject->WindowStation;
1291
1292 ObDereferenceObject(DesktopObject);
1293
1294 return TRUE;
1295 }
1296
1297 /*
1298 * NtUserResolveDesktopForWOW
1299 *
1300 * Status
1301 * @unimplemented
1302 */
1303
1304 DWORD STDCALL
1305 NtUserResolveDesktopForWOW(DWORD Unknown0)
1306 {
1307 UNIMPLEMENTED
1308 return 0;
1309 }
1310
1311 /*
1312 * NtUserGetThreadDesktop
1313 *
1314 * Status
1315 * @implemented
1316 */
1317
1318 HDESK STDCALL
1319 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
1320 {
1321 NTSTATUS Status;
1322 PETHREAD Thread;
1323 PDESKTOP_OBJECT DesktopObject;
1324 HDESK Ret, hThreadDesktop;
1325 OBJECT_HANDLE_INFORMATION HandleInformation;
1326
1327 if(!dwThreadId)
1328 {
1329 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1330 return 0;
1331 }
1332
1333 Status = PsLookupThreadByThreadId((HANDLE)dwThreadId, &Thread);
1334 if(!NT_SUCCESS(Status))
1335 {
1336 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1337 return 0;
1338 }
1339
1340 if(Thread->ThreadsProcess == PsGetCurrentProcess())
1341 {
1342 /* just return the handle, we queried the desktop handle of a thread running
1343 in the same context */
1344 Ret = Thread->Tcb.Win32Thread->hDesktop;
1345 ObDereferenceObject(Thread);
1346 return Ret;
1347 }
1348
1349 /* get the desktop handle and the desktop of the thread */
1350 if(!(hThreadDesktop = Thread->Tcb.Win32Thread->hDesktop) ||
1351 !(DesktopObject = Thread->Tcb.Win32Thread->Desktop))
1352 {
1353 ObDereferenceObject(Thread);
1354 DPRINT1("Desktop information of thread 0x%x broken!?\n", dwThreadId);
1355 return NULL;
1356 }
1357
1358 /* we could just use DesktopObject instead of looking up the handle, but latter
1359 may be a bit safer (e.g. when the desktop is being destroyed */
1360 /* switch into the context of the thread we're trying to get the desktop from,
1361 so we can use the handle */
1362 KeAttachProcess(EPROCESS_TO_KPROCESS(Thread->ThreadsProcess));
1363 Status = ObReferenceObjectByHandle(hThreadDesktop,
1364 GENERIC_ALL,
1365 ExDesktopObjectType,
1366 UserMode,
1367 (PVOID*)&DesktopObject,
1368 &HandleInformation);
1369 KeDetachProcess();
1370
1371 /* the handle couldn't be found, there's nothing to get... */
1372 if(!NT_SUCCESS(Status))
1373 {
1374 ObDereferenceObject(Thread);
1375 return NULL;
1376 }
1377
1378 /* lookup our handle table if we can find a handle to the desktop object,
1379 if not, create one */
1380 Ret = IntGetDesktopObjectHandle(DesktopObject);
1381
1382 /* all done, we got a valid handle to the desktop */
1383 ObDereferenceObject(DesktopObject);
1384 ObDereferenceObject(Thread);
1385 return Ret;
1386 }
1387
1388 /*
1389 * NtUserSetThreadDesktop
1390 *
1391 * Status
1392 * @implemented
1393 */
1394
1395 BOOL STDCALL
1396 NtUserSetThreadDesktop(HDESK hDesktop)
1397 {
1398 PW32THREAD W32Thread;
1399 PDESKTOP_OBJECT DesktopObject;
1400 NTSTATUS Status;
1401
1402 /* Validate the new desktop. */
1403 Status = IntValidateDesktopHandle(
1404 hDesktop,
1405 UserMode,
1406 0,
1407 &DesktopObject);
1408
1409 if (!NT_SUCCESS(Status))
1410 {
1411 DPRINT("Validation of desktop handle (0x%X) failed\n", hDesktop);
1412 return FALSE;
1413 }
1414
1415 W32Thread = PsGetWin32Thread();
1416
1417 /* FIXME: Should check here to see if the thread has any windows. */
1418
1419 if (W32Thread->Desktop != NULL)
1420 {
1421 ObDereferenceObject(W32Thread->Desktop);
1422 }
1423
1424 W32Thread->Desktop = DesktopObject;
1425 W32Thread->hDesktop = hDesktop;
1426
1427 return TRUE;
1428 }
1429
1430 /* EOF */