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