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 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 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 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 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 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 150 HWND FASTCALL UserHungWindowFromGhostWindow(HWND hwndGhost) 151 { 152 PWND pGhostWnd = ValidateHwndNoErr(hwndGhost); 153 return IntHungWindowFromGhostWindow(pGhostWnd); 154 } 155 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