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