xref: /reactos/win32ss/user/ntuser/ghost.c (revision a8e7defb)
1 /*
2  * PROJECT:     ReactOS user32.dll
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Window ghosting feature
5  * COPYRIGHT:   Copyright 2018 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6  */
7 
8 #include <win32k.h>
9 #include "ghostwnd.h"
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 DBG_DEFAULT_CHANNEL(UserInput);
15 
16 static UNICODE_STRING GhostClass = RTL_CONSTANT_STRING(GHOSTCLASSNAME);
17 static UNICODE_STRING GhostProp = RTL_CONSTANT_STRING(GHOST_PROP);
18 
19 PTHREADINFO gptiGhostThread = NULL;
20 
21 /*
22  * GhostThreadMain
23  *
24  * Creates ghost windows and exits when no non-responsive window remains.
25  */
26 VOID NTAPI
UserGhostThreadEntry(VOID)27 UserGhostThreadEntry(VOID)
28 {
29     TRACE("Ghost thread started\n");
30 
31     UserEnterExclusive();
32 
33     gptiGhostThread = PsGetCurrentThreadWin32Thread();
34 
35     //TODO: Implement. This thread should handle all ghost windows and exit when no ghost window is needed.
36 
37     gptiGhostThread = NULL;
38 
39     UserLeave();
40 }
41 
IntIsGhostWindow(PWND Window)42 BOOL FASTCALL IntIsGhostWindow(PWND Window)
43 {
44     BOOLEAN Ret = FALSE;
45     UNICODE_STRING ClassName;
46     INT iCls, Len;
47     RTL_ATOM Atom = 0;
48 
49     if (!Window)
50         return FALSE;
51 
52     if (Window->fnid && !(Window->fnid & FNID_DESTROY))
53     {
54         if (LookupFnIdToiCls(Window->fnid, &iCls))
55         {
56             Atom = gpsi->atomSysClass[iCls];
57         }
58     }
59 
60     // check class name
61     RtlInitUnicodeString(&ClassName, NULL);
62     Len = UserGetClassName(Window->pcls, &ClassName, Atom, FALSE);
63     if (Len > 0)
64     {
65         Ret = RtlEqualUnicodeString(&ClassName, &GhostClass, TRUE);
66     }
67     else
68     {
69         DPRINT1("Unable to get class name\n");
70     }
71     RtlFreeUnicodeString(&ClassName);
72 
73     return Ret;
74 }
75 
IntGhostWindowFromHungWindow(PWND pHungWnd)76 HWND FASTCALL IntGhostWindowFromHungWindow(PWND pHungWnd)
77 {
78     RTL_ATOM Atom;
79     HWND hwndGhost;
80 
81     if (!IntGetAtomFromStringOrAtom(&GhostProp, &Atom))
82         return NULL;
83 
84     hwndGhost = UserGetProp(pHungWnd, Atom, TRUE);
85     if (hwndGhost)
86     {
87         if (ValidateHwndNoErr(hwndGhost))
88             return hwndGhost;
89 
90         DPRINT("Not a window\n");
91     }
92 
93     return NULL;
94 }
95 
UserGhostWindowFromHungWindow(HWND hwndHung)96 HWND FASTCALL UserGhostWindowFromHungWindow(HWND hwndHung)
97 {
98     PWND pHungWnd = ValidateHwndNoErr(hwndHung);
99     if (!pHungWnd)
100     {
101         DPRINT("Not a window\n");
102         return NULL;
103     }
104     return IntGhostWindowFromHungWindow(pHungWnd);
105 }
106 
IntHungWindowFromGhostWindow(PWND pGhostWnd)107 HWND FASTCALL IntHungWindowFromGhostWindow(PWND pGhostWnd)
108 {
109     const GHOST_DATA *UserData;
110     HWND hwndTarget;
111 
112     if (!IntIsGhostWindow(pGhostWnd))
113     {
114         DPRINT("Not a ghost window\n");
115         return NULL;
116     }
117 
118     UserData = (const GHOST_DATA *)pGhostWnd->dwUserData;
119     if (UserData)
120     {
121         _SEH2_TRY
122         {
123             ProbeForRead(UserData, sizeof(GHOST_DATA), 1);
124             hwndTarget = UserData->hwndTarget;
125         }
126         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
127         {
128             DPRINT1("Exception!\n");
129             hwndTarget = NULL;
130         }
131         _SEH2_END;
132     }
133     else
134     {
135         DPRINT("No user data\n");
136         hwndTarget = NULL;
137     }
138 
139     if (hwndTarget)
140     {
141         if (ValidateHwndNoErr(hwndTarget))
142             return hwndTarget;
143 
144         DPRINT1("Not a window\n");
145     }
146 
147     return NULL;
148 }
149 
UserHungWindowFromGhostWindow(HWND hwndGhost)150 HWND FASTCALL UserHungWindowFromGhostWindow(HWND hwndGhost)
151 {
152     PWND pGhostWnd = ValidateHwndNoErr(hwndGhost);
153     return IntHungWindowFromGhostWindow(pGhostWnd);
154 }
155 
IntMakeHungWindowGhosted(HWND hwndHung)156 BOOL FASTCALL IntMakeHungWindowGhosted(HWND hwndHung)
157 {
158     PWND pHungWnd = ValidateHwndNoErr(hwndHung);
159     if (!pHungWnd)
160     {
161         DPRINT1("Not a window\n");
162         return FALSE;   // not a window
163     }
164 
165     if (!MsqIsHung(pHungWnd->head.pti, MSQ_HUNG))
166     {
167         DPRINT1("Not hung window\n");
168         return FALSE;   // not hung window
169     }
170 
171     if (!(pHungWnd->style & WS_VISIBLE))
172         return FALSE;   // invisible
173 
174     if (pHungWnd->style & WS_CHILD)
175         return FALSE;   // child
176 
177     if (IntIsGhostWindow(pHungWnd))
178     {
179         DPRINT1("IntIsGhostWindow\n");
180         return FALSE;   // ghost window cannot be ghosted
181     }
182 
183     if (IntGhostWindowFromHungWindow(pHungWnd))
184     {
185         DPRINT("Already ghosting\n");
186         return FALSE;   // already ghosting
187     }
188 
189     // TODO: Find a way to pass the hwnd of pHungWnd to the ghost thread as we can't pass parameters directly
190 
191     if (!gptiGhostThread)
192         UserCreateSystemThread(ST_GHOST_THREAD);
193 
194     return TRUE;
195 }
196