1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Track layered window transparency and position changes

This commit is contained in:
narzoul 2018-12-10 18:32:39 +01:00
parent 291a9c2f9a
commit ec61179b84
4 changed files with 135 additions and 56 deletions

View File

@ -48,26 +48,11 @@ namespace
CompatPtr<IDirectDrawSurface7> getBackBuffer();
CompatPtr<IDirectDrawSurface7> getLastSurface();
BOOL CALLBACK addVisibleLayeredWindowToVector(HWND hwnd, LPARAM lParam)
{
DWORD windowPid = 0;
GetWindowThreadProcessId(hwnd, &windowPid);
if (GetCurrentProcessId() == windowPid &&
IsWindowVisible(hwnd) &&
(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) &&
!Gdi::Window::isPresentationWindow(hwnd))
{
auto& visibleLayeredWindows = *reinterpret_cast<std::vector<HWND>*>(lParam);
visibleLayeredWindows.push_back(hwnd);
}
return TRUE;
}
void bltToWindow(CompatRef<IDirectDrawSurface7> src)
{
for (auto windowPair : Gdi::Window::getWindows())
{
if (IsWindowVisible(windowPair.first))
if (!windowPair.second->isLayered() && !windowPair.second->getVisibleRegion().isEmpty())
{
g_clipper->SetHWnd(g_clipper, 0, windowPair.second->getPresentationWindow());
g_frontBuffer->Blt(g_frontBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr);
@ -82,11 +67,6 @@ namespace
for (auto windowPair : Gdi::Window::getWindows())
{
if (!IsWindowVisible(windowPair.first))
{
continue;
}
Gdi::Region visibleRegion = windowPair.second->getVisibleRegion();
if (visibleRegion.isEmpty())
{
@ -125,14 +105,6 @@ namespace
void bltVisibleLayeredWindowsToBackBuffer()
{
std::vector<HWND> visibleLayeredWindows;
EnumWindows(addVisibleLayeredWindowToVector, reinterpret_cast<LPARAM>(&visibleLayeredWindows));
if (visibleLayeredWindows.empty())
{
return;
}
auto backBuffer(getBackBuffer());
if (!backBuffer)
{
@ -140,32 +112,58 @@ namespace
}
HDC backBufferDc = nullptr;
backBuffer->GetDC(backBuffer, &backBufferDc);
RECT ddrawMonitorRect = D3dDdi::KernelModeThunks::getMonitorRect();
for (auto it = visibleLayeredWindows.rbegin(); it != visibleLayeredWindows.rend(); ++it)
for (auto windowPair : Gdi::Window::getWindows())
{
HDC windowDc = GetWindowDC(*it);
HRGN rgn = Gdi::getVisibleWindowRgn(*it);
RECT wr = {};
GetWindowRect(*it, &wr);
if (!windowPair.second->isLayered())
{
continue;
}
if (!backBufferDc)
{
backBuffer->GetDC(backBuffer, &backBufferDc);
if (!backBufferDc)
{
return;
}
}
HDC windowDc = GetWindowDC(windowPair.first);
Gdi::Region rgn(Gdi::getVisibleWindowRgn(windowPair.first));
RECT wr = windowPair.second->getWindowRect();
if (0 != ddrawMonitorRect.left || 0 != ddrawMonitorRect.top)
{
OffsetRect(&wr, -ddrawMonitorRect.left, -ddrawMonitorRect.top);
OffsetRgn(rgn, -ddrawMonitorRect.left, -ddrawMonitorRect.top);
rgn.offset(-ddrawMonitorRect.left, -ddrawMonitorRect.top);
}
SelectClipRgn(backBufferDc, rgn);
CALL_ORIG_FUNC(BitBlt)(backBufferDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, SRCCOPY);
SelectClipRgn(backBufferDc, nullptr);
DeleteObject(rgn);
CALL_ORIG_FUNC(ReleaseDC)(*it, windowDc);
auto colorKey = windowPair.second->getColorKey();
if (CLR_INVALID != colorKey)
{
CALL_ORIG_FUNC(TransparentBlt)(backBufferDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, colorKey);
}
else
{
BLENDFUNCTION blend = {};
blend.SourceConstantAlpha = windowPair.second->getAlpha();
CALL_ORIG_FUNC(AlphaBlend)(backBufferDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, blend);
}
CALL_ORIG_FUNC(ReleaseDC)(windowPair.first, windowDc);
}
backBuffer->ReleaseDC(backBuffer, backBufferDc);
if (backBufferDc)
{
SelectClipRgn(backBufferDc, nullptr);
backBuffer->ReleaseDC(backBuffer, backBufferDc);
}
}
void bltToPrimaryChain(CompatRef<IDirectDrawSurface7> src)

View File

@ -182,10 +182,7 @@ namespace
disableDwmAttributes(hwnd);
removeDropShadow(hwnd);
Gdi::PaintHandlers::onCreateWindow(hwnd);
if (Gdi::Window::isTopLevelNonLayeredWindow(hwnd))
{
Gdi::Window::add(hwnd);
}
Gdi::Window::add(hwnd);
}
void onDestroyWindow(HWND hwnd)

View File

@ -1,5 +1,6 @@
#include "Common/Hook.h"
#include "Common/Log.h"
#include "DDraw/RealPrimarySurface.h"
#include "DDraw/ScopedThreadLock.h"
#include "Gdi/Gdi.h"
#include "Gdi/Window.h"
@ -38,7 +39,7 @@ namespace
nullptr,
nullptr,
nullptr);
SetLayeredWindowAttributes(presentationWindow, 0, 255, LWA_ALPHA);
CALL_ORIG_FUNC(SetLayeredWindowAttributes)(presentationWindow, 0, 255, LWA_ALPHA);
return reinterpret_cast<LRESULT>(presentationWindow);
}
@ -81,6 +82,47 @@ namespace
return 0;
}
BOOL WINAPI setLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags)
{
LOG_FUNC("SetLayeredWindowAttributes", hwnd, crKey, bAlpha, dwFlags);
BOOL result = CALL_ORIG_FUNC(SetLayeredWindowAttributes)(hwnd, crKey, bAlpha, dwFlags);
if (SUCCEEDED(result))
{
Gdi::Window::updateLayeredWindowInfo(hwnd,
(dwFlags & LWA_COLORKEY) ? crKey : CLR_INVALID,
(dwFlags & LWA_ALPHA) ? bAlpha : 255);
}
return LOG_RESULT(result);
}
BOOL WINAPI updateLayeredWindow(HWND hWnd, HDC hdcDst, POINT* pptDst, SIZE* psize,
HDC hdcSrc, POINT* pptSrc, COLORREF crKey, BLENDFUNCTION* pblend, DWORD dwFlags)
{
LOG_FUNC("UpdateLayeredWindow", hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags);
BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindow)(
hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags);
if (SUCCEEDED(result) && hdcSrc)
{
Gdi::Window::updateLayeredWindowInfo(hWnd,
(dwFlags & ULW_COLORKEY) ? crKey : CLR_INVALID,
((dwFlags & LWA_ALPHA) && pblend) ? pblend->SourceConstantAlpha : 255);
}
return LOG_RESULT(result);
}
BOOL WINAPI updateLayeredWindowIndirect(HWND hwnd, const UPDATELAYEREDWINDOWINFO* pULWInfo)
{
LOG_FUNC("UpdateLayeredWindowIndirect", hwnd, pULWInfo);
BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindowIndirect)(hwnd, pULWInfo);
if (SUCCEEDED(result) && pULWInfo)
{
Gdi::Window::updateLayeredWindowInfo(hwnd,
(pULWInfo->dwFlags & ULW_COLORKEY) ? pULWInfo->crKey : CLR_INVALID,
((pULWInfo->dwFlags & LWA_ALPHA) && pULWInfo->pblend) ? pULWInfo->pblend->SourceConstantAlpha : 255);
}
return LOG_RESULT(result);
}
}
namespace Gdi
@ -89,10 +131,13 @@ namespace Gdi
: m_hwnd(hwnd)
, m_presentationWindow(nullptr)
, m_windowRect{ 0, 0, 0, 0 }
, m_colorKey(CLR_INVALID)
, m_alpha(255)
, m_isLayered(GetWindowLong(m_hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)
, m_isUpdating(false)
{
const ATOM atom = static_cast<ATOM>(GetClassLong(hwnd, GCW_ATOM));
if (MENU_ATOM != atom && getComboLBoxAtom() != atom)
if (!m_isLayered && MENU_ATOM != atom && getComboLBoxAtom() != atom)
{
m_presentationWindow = reinterpret_cast<HWND>(SendMessage(
g_messageWindow, WM_CREATEPRESENTATIONWINDOW, reinterpret_cast<WPARAM>(hwnd), 0));
@ -103,7 +148,7 @@ namespace Gdi
bool Window::add(HWND hwnd)
{
if (isTopLevelNonLayeredWindow(hwnd) && !get(hwnd))
if (isTopLevelWindow(hwnd) && !get(hwnd))
{
DDraw::ScopedThreadLock lock;
s_windows.emplace(hwnd, std::make_shared<Window>(hwnd));
@ -151,6 +196,18 @@ namespace Gdi
return it != s_windows.end() ? it->second : nullptr;
}
BYTE Window::getAlpha() const
{
DDraw::ScopedThreadLock lock;
return m_alpha;
}
COLORREF Window::getColorKey() const
{
DDraw::ScopedThreadLock lock;
return m_colorKey;
}
HWND Window::getPresentationWindow() const
{
return m_presentationWindow ? m_presentationWindow : m_hwnd;
@ -176,6 +233,10 @@ namespace Gdi
void Window::installHooks()
{
HOOK_FUNCTION(user32, SetLayeredWindowAttributes, setLayeredWindowAttributes);
HOOK_FUNCTION(user32, UpdateLayeredWindow, updateLayeredWindow);
HOOK_FUNCTION(user32, UpdateLayeredWindowIndirect, updateLayeredWindowIndirect);
WNDCLASS wc = {};
wc.lpfnWndProc = &presentationWindowProc;
wc.hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
@ -198,16 +259,20 @@ namespace Gdi
}
}
bool Window::isLayered() const
{
return m_isLayered;
}
bool Window::isPresentationWindow(HWND hwnd)
{
return IsWindow(hwnd) && g_presentationWindowThreadId == GetWindowThreadProcessId(hwnd, nullptr);
}
bool Window::isTopLevelNonLayeredWindow(HWND hwnd)
bool Window::isTopLevelWindow(HWND hwnd)
{
return !(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) &&
(!(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || GetParent(hwnd) == GetDesktopWindow() ||
getComboLBoxAtom() == GetClassLong(hwnd, GCW_ATOM));
return !(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || GetParent(hwnd) == GetDesktopWindow() ||
getComboLBoxAtom() == GetClassLong(hwnd, GCW_ATOM);
}
void Window::remove(HWND hwnd)
@ -244,14 +309,14 @@ namespace Gdi
if (IsWindowVisible(m_hwnd) && !IsIconic(m_hwnd))
{
GetWindowRect(m_hwnd, &newWindowRect);
if (!IsRectEmpty(&newWindowRect))
if (!IsRectEmpty(&newWindowRect) && !m_isLayered)
{
HDC windowDc = GetWindowDC(m_hwnd);
GetRandomRgn(windowDc, newVisibleRegion, SYSRGN);
CALL_ORIG_FUNC(ReleaseDC)(m_hwnd, windowDc);
}
if (m_presentationWindow && GetCurrentThreadId() == GetWindowThreadProcessId(m_hwnd, nullptr))
if (m_presentationWindow)
{
SetWindowPos(m_presentationWindow, nullptr, newWindowRect.left, newWindowRect.top,
newWindowRect.right - newWindowRect.left, newWindowRect.bottom - newWindowRect.top,
@ -292,6 +357,18 @@ namespace Gdi
}
}
void Window::updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha)
{
DDraw::ScopedThreadLock lock;
auto window(get(hwnd));
if (window)
{
window->m_colorKey = colorKey;
window->m_alpha = alpha;
DDraw::RealPrimarySurface::update();
}
}
void Window::updateWindow()
{
RECT windowRect = {};

View File

@ -18,9 +18,12 @@ namespace Gdi
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
BYTE getAlpha() const;
COLORREF getColorKey() const;
HWND getPresentationWindow() const;
Region getVisibleRegion() const;
RECT getWindowRect() const;
bool isLayered() const;
void updateWindow();
static bool add(HWND hwnd);
@ -29,8 +32,9 @@ namespace Gdi
static std::map<HWND, std::shared_ptr<Window>> getWindows();
static bool isPresentationWindow(HWND hwnd);
static bool isTopLevelNonLayeredWindow(HWND hwnd);
static bool isTopLevelWindow(HWND hwnd);
static void updateAll();
static void updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha);
static void installHooks();
static void uninstallHooks();
@ -44,6 +48,9 @@ namespace Gdi
RECT m_windowRect;
Region m_visibleRegion;
Region m_invalidatedRegion;
COLORREF m_colorKey;
BYTE m_alpha;
bool m_isLayered;
bool m_isUpdating;
static std::map<HWND, std::shared_ptr<Window>> s_windows;