1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00
DDrawCompat/DDrawCompat/Gdi/PresentationWindow.cpp
2021-03-01 22:02:31 +01:00

183 lines
4.6 KiB
C++

#include <Common/Hook.h>
#include <Common/Log.h>
#include <Dll/Dll.h>
#include <Gdi/PresentationWindow.h>
namespace
{
const UINT WM_CREATEPRESENTATIONWINDOW = WM_USER;
const UINT WM_SETPRESENTATIONWINDOWPOS = WM_USER + 1;
const UINT WM_SETPRESENTATIONWINDOWRGN = WM_USER + 2;
HANDLE g_presentationWindowThread = nullptr;
unsigned g_presentationWindowThreadId = 0;
HWND g_messageWindow = nullptr;
LRESULT CALLBACK messageWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LOG_FUNC("messageWindowProc", Compat::WindowMessageStruct(hwnd, uMsg, wParam, lParam));
switch (uMsg)
{
case WM_CREATEPRESENTATIONWINDOW:
{
// Workaround for ForceSimpleWindow shim
static auto origCreateWindowExA = reinterpret_cast<decltype(&CreateWindowExA)>(
Compat::getProcAddress(GetModuleHandle("user32"), "CreateWindowExA"));
HWND owner = reinterpret_cast<HWND>(wParam);
HWND presentationWindow = origCreateWindowExA(
WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOPARENTNOTIFY | WS_EX_TOOLWINDOW,
"DDrawCompatPresentationWindow",
nullptr,
WS_DISABLED | WS_POPUP,
0, 0, 1, 1,
owner,
nullptr,
nullptr,
nullptr);
if (presentationWindow)
{
CALL_ORIG_FUNC(SetLayeredWindowAttributes)(presentationWindow, 0, 255, LWA_ALPHA);
}
return LOG_RESULT(reinterpret_cast<LRESULT>(presentationWindow));
}
case WM_DESTROY:
PostQuitMessage(0);
return LOG_RESULT(0);
}
return LOG_RESULT(CALL_ORIG_FUNC(DefWindowProc)(hwnd, uMsg, wParam, lParam));
}
LRESULT CALLBACK presentationWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LOG_FUNC("presentationWindowProc", Compat::WindowMessageStruct(hwnd, uMsg, wParam, lParam));
switch (uMsg)
{
case WM_SETPRESENTATIONWINDOWPOS:
{
const auto& wp = *reinterpret_cast<WINDOWPOS*>(lParam);
return SetWindowPos(hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
}
case WM_SETPRESENTATIONWINDOWRGN:
{
HRGN rgn = nullptr;
if (wParam)
{
rgn = CreateRectRgn(0, 0, 0, 0);
CombineRgn(rgn, reinterpret_cast<HRGN>(wParam), nullptr, RGN_COPY);
}
return SetWindowRgn(hwnd, rgn, FALSE);
}
}
return CALL_ORIG_FUNC(DefWindowProc)(hwnd, uMsg, wParam, lParam);
}
unsigned WINAPI presentationWindowThreadProc(LPVOID /*lpParameter*/)
{
WNDCLASS wc = {};
wc.lpfnWndProc = &messageWindowProc;
wc.hInstance = Dll::g_currentModule;
wc.lpszClassName = "DDrawCompatMessageWindow";
CALL_ORIG_FUNC(RegisterClassA)(&wc);
g_messageWindow = CreateWindow(
"DDrawCompatMessageWindow", nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);
if (!g_messageWindow)
{
return 0;
}
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT sendMessageBlocking(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
DWORD_PTR result = 0;
SendMessageTimeout(hwnd, msg, wParam, lParam, SMTO_BLOCK | SMTO_NOTIMEOUTIFNOTHUNG, 0, &result);
return result;
}
}
namespace Gdi
{
namespace PresentationWindow
{
HWND create(HWND owner)
{
return reinterpret_cast<HWND>(sendMessageBlocking(
g_messageWindow, WM_CREATEPRESENTATIONWINDOW, reinterpret_cast<WPARAM>(owner), 0));
}
void destroy(HWND hwnd)
{
sendMessageBlocking(hwnd, WM_CLOSE, 0, 0);
}
void installHooks()
{
WNDCLASS wc = {};
wc.lpfnWndProc = &presentationWindowProc;
wc.hInstance = Dll::g_currentModule;
wc.lpszClassName = "DDrawCompatPresentationWindow";
CALL_ORIG_FUNC(RegisterClassA)(&wc);
Dll::createThread(presentationWindowThreadProc, &g_presentationWindowThreadId, THREAD_PRIORITY_TIME_CRITICAL);
int i = 0;
while (!g_messageWindow && i < 1000)
{
Sleep(1);
++i;
}
if (!g_messageWindow)
{
Compat::Log() << "ERROR: Failed to create a message-only window";
}
}
bool isPresentationWindow(HWND hwnd)
{
return GetWindowThreadProcessId(hwnd, nullptr) == g_presentationWindowThreadId;
}
void setWindowPos(HWND hwnd, const WINDOWPOS& wp)
{
sendMessageBlocking(hwnd, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast<WPARAM>(&wp));
}
void setWindowRgn(HWND hwnd, HRGN rgn)
{
sendMessageBlocking(hwnd, WM_SETPRESENTATIONWINDOWRGN, reinterpret_cast<WPARAM>(rgn), 0);
}
void uninstallHooks()
{
if (g_presentationWindowThread)
{
sendMessageBlocking(g_messageWindow, WM_CLOSE, 0, 0);
if (WAIT_OBJECT_0 != WaitForSingleObject(g_presentationWindowThread, 1000))
{
TerminateThread(g_presentationWindowThread, 0);
Compat::Log() << "The presentation window thread was terminated forcefully";
}
}
}
}
}