2018-12-01 11:59:07 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS user32.dll
|
|
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
2018-12-11 03:30:59 +00:00
|
|
|
* PURPOSE: Window ghosting feature
|
2018-12-01 11:59:07 +00:00
|
|
|
* COPYRIGHT: Copyright 2018 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <win32k.h>
|
2018-12-11 03:30:59 +00:00
|
|
|
#include "ghostwnd.h"
|
2018-12-01 11:59:07 +00:00
|
|
|
|
2018-12-11 03:30:59 +00:00
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
2018-12-16 11:16:29 +00:00
|
|
|
DBG_DEFAULT_CHANNEL(UserInput);
|
|
|
|
|
2018-12-11 03:30:59 +00:00
|
|
|
static UNICODE_STRING GhostClass = RTL_CONSTANT_STRING(GHOSTCLASSNAME);
|
|
|
|
static UNICODE_STRING GhostProp = RTL_CONSTANT_STRING(GHOST_PROP);
|
|
|
|
|
2018-12-16 11:16:29 +00:00
|
|
|
PTHREADINFO gptiGhostThread = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GhostThreadMain
|
|
|
|
*
|
|
|
|
* Creates ghost windows and exits when no non-responsive window remains.
|
|
|
|
*/
|
|
|
|
VOID NTAPI
|
|
|
|
UserGhostThreadEntry(VOID)
|
|
|
|
{
|
|
|
|
TRACE("Ghost thread started\n");
|
|
|
|
|
|
|
|
UserEnterExclusive();
|
|
|
|
|
|
|
|
gptiGhostThread = PsGetCurrentThreadWin32Thread();
|
|
|
|
|
|
|
|
//TODO: Implement. This thread should handle all ghost windows and exit when no ghost window is needed.
|
|
|
|
|
|
|
|
gptiGhostThread = NULL;
|
|
|
|
|
|
|
|
UserLeave();
|
|
|
|
}
|
|
|
|
|
2018-12-11 03:30:59 +00:00
|
|
|
BOOL FASTCALL IntIsGhostWindow(PWND Window)
|
|
|
|
{
|
|
|
|
BOOLEAN Ret = FALSE;
|
|
|
|
UNICODE_STRING ClassName;
|
|
|
|
INT iCls, Len;
|
|
|
|
RTL_ATOM Atom = 0;
|
|
|
|
|
|
|
|
if (!Window)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (Window->fnid && !(Window->fnid & FNID_DESTROY))
|
|
|
|
{
|
|
|
|
if (LookupFnIdToiCls(Window->fnid, &iCls))
|
|
|
|
{
|
|
|
|
Atom = gpsi->atomSysClass[iCls];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check class name
|
|
|
|
RtlInitUnicodeString(&ClassName, NULL);
|
|
|
|
Len = UserGetClassName(Window->pcls, &ClassName, Atom, FALSE);
|
|
|
|
if (Len > 0)
|
|
|
|
{
|
|
|
|
Ret = RtlEqualUnicodeString(&ClassName, &GhostClass, TRUE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINT1("Unable to get class name\n");
|
|
|
|
}
|
|
|
|
RtlFreeUnicodeString(&ClassName);
|
|
|
|
|
|
|
|
return Ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
HWND FASTCALL IntGhostWindowFromHungWindow(PWND pHungWnd)
|
|
|
|
{
|
|
|
|
RTL_ATOM Atom;
|
|
|
|
HWND hwndGhost;
|
|
|
|
|
|
|
|
if (!IntGetAtomFromStringOrAtom(&GhostProp, &Atom))
|
2018-12-11 23:03:49 +00:00
|
|
|
return NULL;
|
2018-12-11 03:30:59 +00:00
|
|
|
|
|
|
|
hwndGhost = UserGetProp(pHungWnd, Atom, TRUE);
|
|
|
|
if (hwndGhost)
|
|
|
|
{
|
|
|
|
if (ValidateHwndNoErr(hwndGhost))
|
|
|
|
return hwndGhost;
|
|
|
|
|
|
|
|
DPRINT("Not a window\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HWND FASTCALL UserGhostWindowFromHungWindow(HWND hwndHung)
|
2018-12-01 11:59:07 +00:00
|
|
|
{
|
2018-12-11 03:30:59 +00:00
|
|
|
PWND pHungWnd = ValidateHwndNoErr(hwndHung);
|
|
|
|
if (!pHungWnd)
|
|
|
|
{
|
|
|
|
DPRINT("Not a window\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return IntGhostWindowFromHungWindow(pHungWnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
HWND FASTCALL IntHungWindowFromGhostWindow(PWND pGhostWnd)
|
|
|
|
{
|
|
|
|
const GHOST_DATA *UserData;
|
|
|
|
HWND hwndTarget;
|
|
|
|
|
|
|
|
if (!IntIsGhostWindow(pGhostWnd))
|
|
|
|
{
|
|
|
|
DPRINT("Not a ghost window\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
UserData = (const GHOST_DATA *)pGhostWnd->dwUserData;
|
|
|
|
if (UserData)
|
|
|
|
{
|
|
|
|
_SEH2_TRY
|
|
|
|
{
|
|
|
|
ProbeForRead(UserData, sizeof(GHOST_DATA), 1);
|
|
|
|
hwndTarget = UserData->hwndTarget;
|
|
|
|
}
|
|
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
{
|
|
|
|
DPRINT1("Exception!\n");
|
|
|
|
hwndTarget = NULL;
|
|
|
|
}
|
|
|
|
_SEH2_END;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINT("No user data\n");
|
|
|
|
hwndTarget = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hwndTarget)
|
|
|
|
{
|
|
|
|
if (ValidateHwndNoErr(hwndTarget))
|
|
|
|
return hwndTarget;
|
|
|
|
|
|
|
|
DPRINT1("Not a window\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HWND FASTCALL UserHungWindowFromGhostWindow(HWND hwndGhost)
|
|
|
|
{
|
|
|
|
PWND pGhostWnd = ValidateHwndNoErr(hwndGhost);
|
|
|
|
return IntHungWindowFromGhostWindow(pGhostWnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL FASTCALL IntMakeHungWindowGhosted(HWND hwndHung)
|
|
|
|
{
|
|
|
|
PWND pHungWnd = ValidateHwndNoErr(hwndHung);
|
|
|
|
if (!pHungWnd)
|
|
|
|
{
|
|
|
|
DPRINT1("Not a window\n");
|
|
|
|
return FALSE; // not a window
|
|
|
|
}
|
|
|
|
|
2019-12-29 14:10:19 +00:00
|
|
|
if (!MsqIsHung(pHungWnd->head.pti, MSQ_HUNG))
|
2018-12-11 03:30:59 +00:00
|
|
|
{
|
|
|
|
DPRINT1("Not hung window\n");
|
|
|
|
return FALSE; // not hung window
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(pHungWnd->style & WS_VISIBLE))
|
|
|
|
return FALSE; // invisible
|
|
|
|
|
|
|
|
if (pHungWnd->style & WS_CHILD)
|
|
|
|
return FALSE; // child
|
|
|
|
|
|
|
|
if (IntIsGhostWindow(pHungWnd))
|
|
|
|
{
|
|
|
|
DPRINT1("IntIsGhostWindow\n");
|
|
|
|
return FALSE; // ghost window cannot be ghosted
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IntGhostWindowFromHungWindow(pHungWnd))
|
|
|
|
{
|
|
|
|
DPRINT("Already ghosting\n");
|
|
|
|
return FALSE; // already ghosting
|
|
|
|
}
|
|
|
|
|
2018-12-16 11:16:29 +00:00
|
|
|
// TODO: Find a way to pass the hwnd of pHungWnd to the ghost thread as we can't pass parameters directly
|
|
|
|
|
|
|
|
if (!gptiGhostThread)
|
|
|
|
UserCreateSystemThread(ST_GHOST_THREAD);
|
2018-12-11 03:30:59 +00:00
|
|
|
|
2018-12-16 11:16:29 +00:00
|
|
|
return TRUE;
|
2018-12-01 11:59:07 +00:00
|
|
|
}
|