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