From 5911951d0832a19250142a42c3e7770c54d5e57b Mon Sep 17 00:00:00 2001 From: narzoul Date: Sun, 27 Dec 2020 17:08:44 +0100 Subject: [PATCH] Use window region to emulate pre-DWM system region --- DDrawCompat/DDraw/DirectDrawClipper.cpp | 18 +- DDrawCompat/DDraw/DirectDrawClipper.h | 1 + DDrawCompat/DDraw/RealPrimarySurface.cpp | 142 +--- DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp | 12 +- DDrawCompat/Gdi/Dc.cpp | 18 +- DDrawCompat/Gdi/DcFunctions.cpp | 75 -- DDrawCompat/Gdi/DcFunctions.h | 3 - DDrawCompat/Gdi/Gdi.cpp | 5 - DDrawCompat/Gdi/Gdi.h | 1 - DDrawCompat/Gdi/PaintHandlers.cpp | 84 ++- DDrawCompat/Gdi/Region.cpp | 30 +- DDrawCompat/Gdi/Region.h | 6 + DDrawCompat/Gdi/TitleBar.cpp | 21 +- DDrawCompat/Gdi/TitleBar.h | 2 + DDrawCompat/Gdi/WinProc.cpp | 175 +++-- DDrawCompat/Gdi/WinProc.h | 3 +- DDrawCompat/Gdi/Window.cpp | 835 ++++++++++++--------- DDrawCompat/Gdi/Window.h | 64 +- DDrawCompat/Win32/Log.cpp | 2 +- DDrawCompat/Win32/Log.h | 2 +- 20 files changed, 791 insertions(+), 708 deletions(-) diff --git a/DDrawCompat/DDraw/DirectDrawClipper.cpp b/DDrawCompat/DDraw/DirectDrawClipper.cpp index a8cd068..e7a6c2f 100644 --- a/DDrawCompat/DDraw/DirectDrawClipper.cpp +++ b/DDrawCompat/DDraw/DirectDrawClipper.cpp @@ -16,15 +16,13 @@ namespace }; std::map g_clipperData; + bool g_isInvalidated = false; void updateWindowClipList(CompatRef clipper, ClipperData& data); void onWindowPosChange() { - for (auto& clipperData : g_clipperData) - { - updateWindowClipList(*clipperData.first, clipperData.second); - } + g_isInvalidated = true; } void updateWindowClipList(CompatRef clipper, ClipperData& data) @@ -146,4 +144,16 @@ namespace DDraw vtable.SetClipList = &SetClipList; vtable.SetHWnd = &SetHWnd; } + + void DirectDrawClipper::update() + { + if (g_isInvalidated) + { + g_isInvalidated = false; + for (auto& clipperData : g_clipperData) + { + updateWindowClipList(*clipperData.first, clipperData.second); + } + } + } } diff --git a/DDrawCompat/DDraw/DirectDrawClipper.h b/DDrawCompat/DDraw/DirectDrawClipper.h index 5430647..6ce4ae4 100644 --- a/DDrawCompat/DDraw/DirectDrawClipper.h +++ b/DDrawCompat/DDraw/DirectDrawClipper.h @@ -11,6 +11,7 @@ namespace DDraw public: static HRGN getClipRgn(CompatRef clipper); static HRESULT setClipRgn(CompatRef clipper, HRGN rgn); + static void update(); static void setCompatVtable(IDirectDrawClipperVtbl& vtable); }; diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp index 665d133..9f62410 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.cpp +++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp @@ -47,137 +47,11 @@ namespace CompatPtr getBackBuffer(); CompatPtr getLastSurface(); - void bltToWindow(CompatRef src) - { - for (auto windowPair : Gdi::Window::getWindows()) - { - 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); - } - } - } - - void bltToWindowViaGdi(Gdi::Region* primaryRegion) - { - D3dDdi::ScopedCriticalSection lock; - std::unique_ptr virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc); - RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds(); - - for (auto windowPair : Gdi::Window::getWindows()) - { - HWND presentationWindow = windowPair.second->getPresentationWindow(); - if (!presentationWindow) - { - continue; - } - - Gdi::Region visibleRegion = windowPair.second->getVisibleRegion(); - if (visibleRegion.isEmpty()) - { - continue; - } - - if (primaryRegion) - { - visibleRegion -= *primaryRegion; - if (visibleRegion.isEmpty()) - { - continue; - } - } - - if (!virtualScreenDc) - { - virtualScreenDc.reset(Gdi::VirtualScreen::createDc()); - if (!virtualScreenDc) - { - return; - } - } - - Gdi::AccessGuard accessGuard(Gdi::ACCESS_READ, !primaryRegion); - HDC dc = GetWindowDC(presentationWindow); - RECT rect = windowPair.second->getWindowRect(); - visibleRegion.offset(-rect.left, -rect.top); - SelectClipRgn(dc, visibleRegion); - CALL_ORIG_FUNC(BitBlt)(dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, virtualScreenDc.get(), - rect.left - virtualScreenBounds.left, rect.top - virtualScreenBounds.top, SRCCOPY); - CALL_ORIG_FUNC(ReleaseDC)(presentationWindow, dc); - } - } - - void bltVisibleLayeredWindowsToBackBuffer() - { - auto backBuffer(getBackBuffer()); - if (!backBuffer) - { - return; - } - - HDC backBufferDc = nullptr; - RECT ddrawMonitorRect = D3dDdi::KernelModeThunks::getMonitorRect(); - - for (auto windowPair : Gdi::Window::getWindows()) - { - if (!windowPair.second->isLayered()) - { - continue; - } - - if (!backBufferDc) - { - D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_X8R8G8B8); - backBuffer->GetDC(backBuffer, &backBufferDc); - D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_UNKNOWN); - 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); - rgn.offset(-ddrawMonitorRect.left, -ddrawMonitorRect.top); - } - - SelectClipRgn(backBufferDc, rgn); - - 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); - } - - if (backBufferDc) - { - SelectClipRgn(backBufferDc, nullptr); - backBuffer->ReleaseDC(backBuffer, backBufferDc); - } - } - void bltToPrimaryChain(CompatRef src) { if (!g_isFullScreen) { - bltToWindow(src); + Gdi::Window::present(*g_frontBuffer, src, *g_clipper); return; } @@ -324,12 +198,13 @@ namespace if (!g_frontBuffer || !src || DDraw::RealPrimarySurface::isLost()) { - bltToWindowViaGdi(nullptr); + Gdi::Window::present(nullptr); return; } - Gdi::Region primaryRegion(D3dDdi::KernelModeThunks::getMonitorRect()); - bltToWindowViaGdi(&primaryRegion); + RECT monitorRect = D3dDdi::KernelModeThunks::getMonitorRect(); + Gdi::Region excludeRegion(monitorRect); + Gdi::Window::present(excludeRegion); if (Win32::DisplayMode::getBpp() <= 8) { @@ -360,7 +235,12 @@ namespace if (g_isFullScreen && src == DDraw::PrimarySurface::getGdiSurface()) { - bltVisibleLayeredWindowsToBackBuffer(); + auto backBuffer(getBackBuffer()); + if (backBuffer) + { + POINT offset = { -monitorRect.left, -monitorRect.top }; + Gdi::Window::presentLayered(*backBuffer, offset); + } } } diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp index 493aed5..66a82d1 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp @@ -1,10 +1,11 @@ #include -#include "DDraw/DirectDrawSurface.h" -#include "DDraw/RealPrimarySurface.h" -#include "DDraw/Surfaces/PrimarySurface.h" -#include "DDraw/Surfaces/Surface.h" -#include "DDraw/Surfaces/SurfaceImpl.h" +#include +#include +#include +#include +#include +#include namespace DDraw { @@ -28,6 +29,7 @@ namespace DDraw { return DDERR_WASSTILLDRAWING; } + DirectDrawClipper::update(); return s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } diff --git a/DDrawCompat/Gdi/Dc.cpp b/DDrawCompat/Gdi/Dc.cpp index c5aee12..1787424 100644 --- a/DDrawCompat/Gdi/Dc.cpp +++ b/DDrawCompat/Gdi/Dc.cpp @@ -113,14 +113,12 @@ namespace } } - void setClippingRegion(const CompatDc& compatDc, std::shared_ptr rootWindow, - const POINT& origin, const RECT& virtualScreenBounds) + void setClippingRegion(const CompatDc& compatDc, HWND hwnd, const POINT& origin, const RECT& virtualScreenBounds) { - if (rootWindow) + if (hwnd) { Gdi::Region sysRgn; CALL_ORIG_FUNC(GetRandomRgn)(compatDc.origDc, sysRgn, SYSRGN); - sysRgn &= rootWindow->getVisibleRegion(); OffsetRgn(sysRgn, -virtualScreenBounds.left, -virtualScreenBounds.top); SelectClipRgn(compatDc.dc, sysRgn); } @@ -196,13 +194,6 @@ namespace Gdi return it->second.dc; } - const HWND wnd = CALL_ORIG_FUNC(WindowFromDC)(origDc); - auto rootWindow(wnd ? Gdi::Window::get(GetAncestor(wnd, GA_ROOT)) : nullptr); - if (rootWindow) - { - rootWindow->updateWindow(); - } - CompatDc compatDc; compatDc.dc = Gdi::DcCache::getDc(); if (!compatDc.dc) @@ -212,7 +203,8 @@ namespace Gdi POINT origin = {}; GetDCOrgEx(origDc, &origin); - if (wnd && GetDesktopWindow() != wnd) + const HWND hwnd = CALL_ORIG_FUNC(WindowFromDC)(origDc); + if (hwnd && GetDesktopWindow() != hwnd) { origin.x -= virtualScreenBounds.left; origin.y -= virtualScreenBounds.top; @@ -223,7 +215,7 @@ namespace Gdi compatDc.threadId = GetCurrentThreadId(); compatDc.savedState = useMetaRgn ? SaveDC(compatDc.dc) : 0; copyDcAttributes(compatDc, origDc, origin); - setClippingRegion(compatDc, rootWindow, origin, virtualScreenBounds); + setClippingRegion(compatDc, hwnd, origin, virtualScreenBounds); g_origDcToCompatDc.insert(CompatDcMap::value_type(origDc, compatDc)); diff --git a/DDrawCompat/Gdi/DcFunctions.cpp b/DDrawCompat/Gdi/DcFunctions.cpp index 72da28e..1cdad50 100644 --- a/DDrawCompat/Gdi/DcFunctions.cpp +++ b/DDrawCompat/Gdi/DcFunctions.cpp @@ -47,17 +47,9 @@ namespace HDC m_compatDc; }; - struct ExcludeRgnForOverlappingWindowArgs - { - HRGN rgn; - HWND rootWnd; - }; - std::unordered_map g_funcNames; thread_local bool g_redirectToDib = true; - HRGN getWindowRegion(HWND hwnd); - #define CREATE_DC_FUNC_ATTRIBUTE(attribute) \ template \ bool attribute() \ @@ -266,30 +258,6 @@ namespace return LOG_RESULT(CALL_ORIG_FUNC(DrawCaption)(hwnd, hdc, lprect, flags)); } - BOOL CALLBACK excludeRgnForOverlappingWindow(HWND hwnd, LPARAM lParam) - { - auto& args = *reinterpret_cast(lParam); - if (hwnd == args.rootWnd) - { - return FALSE; - } - - DWORD windowPid = 0; - GetWindowThreadProcessId(hwnd, &windowPid); - if (GetCurrentProcessId() != windowPid || - !IsWindowVisible(hwnd) || - (CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE) & (WS_EX_LAYERED | WS_EX_TRANSPARENT))) - { - return TRUE; - } - - HRGN windowRgn = getWindowRegion(hwnd); - CombineRgn(args.rgn, args.rgn, windowRgn, RGN_DIFF); - DeleteObject(windowRgn); - - return TRUE; - } - template OrigFuncPtr getCompatGdiDcFuncPtr(FuncPtr) { @@ -302,18 +270,6 @@ namespace return &compatGdiTextDcFunc; } - HRGN getWindowRegion(HWND hwnd) - { - RECT wr = {}; - GetWindowRect(hwnd, &wr); - HRGN windowRgn = CreateRectRgnIndirect(&wr); - if (ERROR != GetWindowRgn(hwnd, windowRgn)) - { - OffsetRgn(windowRgn, wr.left, wr.top); - } - return windowRgn; - } - template void hookGdiDcFunction(const char* moduleName, const char* funcName) { @@ -336,26 +292,6 @@ namespace moduleName, funcName, getCompatGdiTextDcFuncPtr(origFunc)); } - int WINAPI getRandomRgn(HDC hdc, HRGN hrgn, INT iNum) - { - int result = CALL_ORIG_FUNC(GetRandomRgn)(hdc, hrgn, iNum); - if (1 != result) - { - return result; - } - - HWND hwnd = WindowFromDC(hdc); - if (!hwnd || hwnd == GetDesktopWindow() || (CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED)) - { - return 1; - } - - ExcludeRgnForOverlappingWindowArgs args = { hrgn, GetAncestor(hwnd, GA_ROOT) }; - EnumWindows(excludeRgnForOverlappingWindow, reinterpret_cast(&args)); - - return 1; - } - template ATOM WINAPI registerClass(const WndClass* lpWndClass, ATOM(WINAPI* origRegisterClass)(const WndClass*), ATOM(WINAPI* registerClassEx)(const WndClassEx*)) @@ -481,14 +417,6 @@ namespace Gdi { namespace DcFunctions { - HRGN getVisibleWindowRgn(HWND hwnd) - { - HRGN rgn = getWindowRegion(hwnd); - ExcludeRgnForOverlappingWindowArgs args = { rgn, hwnd }; - EnumWindows(excludeRgnForOverlappingWindow, reinterpret_cast(&args)); - return rgn; - } - void installHooks() { // Bitmap functions @@ -517,9 +445,6 @@ namespace Gdi // Brush functions HOOK_GDI_DC_FUNCTION(gdi32, PatBlt); - // Clipping functions - HOOK_FUNCTION(gdi32, GetRandomRgn, getRandomRgn); - // Device context functions HOOK_GDI_DC_FUNCTION(gdi32, DrawEscape); HOOK_FUNCTION(user32, WindowFromDC, windowFromDc); diff --git a/DDrawCompat/Gdi/DcFunctions.h b/DDrawCompat/Gdi/DcFunctions.h index 2d5623e..3ae80af 100644 --- a/DDrawCompat/Gdi/DcFunctions.h +++ b/DDrawCompat/Gdi/DcFunctions.h @@ -1,12 +1,9 @@ #pragma once -#include - namespace Gdi { namespace DcFunctions { - HRGN getVisibleWindowRgn(HWND hwnd); void installHooks(); } } diff --git a/DDrawCompat/Gdi/Gdi.cpp b/DDrawCompat/Gdi/Gdi.cpp index cabbbd9..7a0e644 100644 --- a/DDrawCompat/Gdi/Gdi.cpp +++ b/DDrawCompat/Gdi/Gdi.cpp @@ -34,11 +34,6 @@ namespace Gdi DcCache::dllThreadDetach(); } - HRGN getVisibleWindowRgn(HWND hwnd) - { - return DcFunctions::getVisibleWindowRgn(hwnd); - } - void installHooks() { DcFunctions::installHooks(); diff --git a/DDrawCompat/Gdi/Gdi.h b/DDrawCompat/Gdi/Gdi.h index da0691f..ece2645 100644 --- a/DDrawCompat/Gdi/Gdi.h +++ b/DDrawCompat/Gdi/Gdi.h @@ -9,7 +9,6 @@ namespace Gdi typedef void(*WindowPosChangeNotifyFunc)(); void dllThreadDetach(); - HRGN getVisibleWindowRgn(HWND hwnd); void installHooks(); bool isDisplayDc(HDC dc); void redraw(HRGN rgn); diff --git a/DDrawCompat/Gdi/PaintHandlers.cpp b/DDrawCompat/Gdi/PaintHandlers.cpp index 1ab437e..c9e5034 100644 --- a/DDrawCompat/Gdi/PaintHandlers.cpp +++ b/DDrawCompat/Gdi/PaintHandlers.cpp @@ -7,6 +7,7 @@ #include #include #include +#include std::ostream& operator<<(std::ostream& os, const MENUITEMINFOW& val) { @@ -43,7 +44,8 @@ namespace LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc, const char* origWndProcName); LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc); - LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc); + LRESULT onNcActivate(HWND hwnd, WPARAM wParam, LPARAM lParam); + LRESULT onNcPaint(HWND hwnd, WNDPROC origWndProc); LRESULT onPaint(HWND hwnd, WNDPROC origWndProc); LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc); LRESULT onSetText(HWND hwnd, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc); @@ -143,7 +145,7 @@ namespace return onEraseBackground(hwnd, reinterpret_cast(wParam), origWndProc); case WM_NCPAINT: - return onNcPaint(hwnd, wParam, origWndProc); + return onNcPaint(hwnd, origWndProc); case WM_PRINT: case WM_PRINTCLIENT: @@ -164,14 +166,38 @@ namespace return LOG_RESULT(defPaintProc(hwnd, msg, wParam, lParam, origWndProc)); } + LRESULT defWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origDefWindowProc) + { + switch (msg) + { + case WM_NCACTIVATE: + return onNcActivate(hwnd, wParam, lParam); + + case WM_NCCREATE: + { + char className[64] = {}; + GetClassName(hwnd, className, sizeof(className)); + if (std::string(className) == "CompatWindowDesktopReplacement") + { + // Disable VirtualizeDesktopPainting shim + return 0; + } + } + } + + return defPaintProc(hwnd, msg, wParam, lParam, origDefWindowProc); + } + LRESULT WINAPI defWindowProcA(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - return defPaintProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcA), "defWindowProcA"); + LOG_FUNC("DefWindowProcA", Compat::WindowMessageStruct(hwnd, msg, wParam, lParam)); + return LOG_RESULT(defWindowProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcA))); } LRESULT WINAPI defWindowProcW(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - return defPaintProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcW), "defWindowProcW"); + LOG_FUNC("DefWindowProcW", Compat::WindowMessageStruct(hwnd, msg, wParam, lParam)); + return LOG_RESULT(defWindowProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcW))); } LRESULT editWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc) @@ -285,7 +311,7 @@ namespace { case WM_NCPAINT: CallWindowProc(origWndProc, hwnd, msg, wParam, lParam); - return onNcPaint(hwnd, wParam, origWndProc); + return onNcPaint(hwnd, origWndProc); case WM_PAINT: { @@ -310,6 +336,18 @@ namespace return result; } + case WM_WINDOWPOSCHANGED: + { + LRESULT result = CallWindowProc(origWndProc, hwnd, msg, wParam, lParam); + auto exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE); + if (exStyle & WS_EX_LAYERED) + { + CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED); + RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW); + } + return result; + } + case 0x1e5: if (-1 == wParam) { @@ -341,11 +379,11 @@ namespace return CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast(dc), 0); } - LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc) + LRESULT onNcActivate(HWND hwnd, WPARAM wParam, LPARAM lParam) { - if (!hwnd) + if (-1 == lParam) { - return CallWindowProc(origWndProc, hwnd, WM_NCPAINT, wParam, 0); + return TRUE; } HDC windowDc = GetWindowDC(hwnd); @@ -353,6 +391,26 @@ namespace if (compatDc) { + D3dDdi::ScopedCriticalSection lock; + Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE); + Gdi::TitleBar titleBar(hwnd, compatDc); + titleBar.setActive(wParam); + titleBar.drawAll(); + Gdi::Dc::releaseDc(windowDc); + } + + CALL_ORIG_FUNC(ReleaseDC)(hwnd, windowDc); + return TRUE; + } + + LRESULT onNcPaint(HWND hwnd, WNDPROC origWndProc) + { + HDC windowDc = GetWindowDC(hwnd); + HDC compatDc = Gdi::Dc::getDc(windowDc); + + if (compatDc) + { + D3dDdi::ScopedCriticalSection lock; Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE); Gdi::TitleBar titleBar(hwnd, compatDc); titleBar.drawAll(); @@ -430,9 +488,10 @@ namespace if (compatDc) { + D3dDdi::ScopedCriticalSection lock; Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE); Gdi::TitleBar titleBar(hwnd, compatDc); - titleBar.drawCaption(); + titleBar.drawAll(); Gdi::Dc::releaseDc(windowDc); } @@ -479,7 +538,12 @@ namespace [[maybe_unused]] const std::string& procName, WndProcHook wndProcHook, WNDPROC oldWndProcTrampoline) { LOG_FUNC(procName.c_str(), Compat::WindowMessageStruct(hwnd, uMsg, wParam, lParam)); - return LOG_RESULT(wndProcHook(hwnd, uMsg, wParam, lParam, oldWndProcTrampoline)); + LRESULT result = wndProcHook(hwnd, uMsg, wParam, lParam, oldWndProcTrampoline); + if (WM_CREATE == uMsg && -1 != result) + { + Gdi::WinProc::onCreateWindow(hwnd); + } + return LOG_RESULT(result); } template diff --git a/DDrawCompat/Gdi/Region.cpp b/DDrawCompat/Gdi/Region.cpp index 5593ec9..a65e666 100644 --- a/DDrawCompat/Gdi/Region.cpp +++ b/DDrawCompat/Gdi/Region.cpp @@ -15,6 +15,11 @@ namespace namespace Gdi { + Region::Region(std::nullptr_t) + : m_region(nullptr) + { + } + Region::Region(HRGN rgn) : m_region(rgn) { @@ -42,9 +47,13 @@ namespace Gdi } Region::Region(const Region& other) - : Region() + : Region(nullptr) { - CombineRgn(m_region, other, nullptr, RGN_COPY); + if (other.m_region) + { + m_region = CreateRectRgn(0, 0, 0, 0); + CombineRgn(m_region, other, nullptr, RGN_COPY); + } } Region::Region(Region&& other) @@ -81,6 +90,23 @@ namespace Gdi return m_region; } + bool Region::operator==(const Region& other) const + { + if (m_region) + { + return other.m_region && EqualRgn(m_region, other.m_region); + } + else + { + return !other.m_region; + } + } + + bool Region::operator!=(const Region& other) const + { + return !(*this == other); + } + Region Region::operator&(const Region& other) const { return combineRegions(*this, other, RGN_AND); diff --git a/DDrawCompat/Gdi/Region.h b/DDrawCompat/Gdi/Region.h index 413fd7a..27392b1 100644 --- a/DDrawCompat/Gdi/Region.h +++ b/DDrawCompat/Gdi/Region.h @@ -1,5 +1,7 @@ #pragma once +#include + #include namespace Gdi @@ -7,6 +9,7 @@ namespace Gdi class Region { public: + Region(std::nullptr_t); Region(HRGN rgn); Region(const RECT& rect = RECT{ 0, 0, 0, 0 }); Region(HWND hwnd); @@ -21,6 +24,9 @@ namespace Gdi operator HRGN() const; + bool operator==(const Region& other) const; + bool operator!=(const Region& other) const; + Region operator&(const Region& other) const; Region operator|(const Region& other) const; Region operator-(const Region& other) const; diff --git a/DDrawCompat/Gdi/TitleBar.cpp b/DDrawCompat/Gdi/TitleBar.cpp index 14d0cf3..c355a4a 100644 --- a/DDrawCompat/Gdi/TitleBar.cpp +++ b/DDrawCompat/Gdi/TitleBar.cpp @@ -19,9 +19,16 @@ namespace namespace Gdi { - TitleBar::TitleBar(HWND hwnd, HDC compatDc) : - m_hwnd(hwnd), m_compatDc(compatDc), m_buttonWidth(0), m_buttonHeight(0), m_tbi(), - m_windowRect(), m_hasIcon(false), m_hasTitleBar(false) + TitleBar::TitleBar(HWND hwnd, HDC compatDc) + : m_hwnd(hwnd) + , m_compatDc(compatDc) + , m_buttonWidth(0) + , m_buttonHeight(0) + , m_tbi{} + , m_windowRect{} + , m_hasIcon(false) + , m_hasTitleBar(false) + , m_isActive(false) { m_hasTitleBar = 0 != (CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_STYLE) & WS_CAPTION); if (!m_hasTitleBar) @@ -46,6 +53,7 @@ namespace Gdi GetWindowRect(hwnd, &m_windowRect); OffsetRect(&m_tbi.rcTitleBar, -m_windowRect.left, -m_windowRect.top); + m_isActive = GetActiveWindow() == hwnd; m_buttonWidth = GetSystemMetrics(SM_CXSIZE) - 2; m_buttonHeight = GetSystemMetrics(SM_CYSIZE) - 4; @@ -94,7 +102,7 @@ namespace Gdi } UINT flags = 0; - if (GetActiveWindow() == m_hwnd) + if (m_isActive) { flags |= DC_ACTIVE; } @@ -152,6 +160,11 @@ namespace Gdi } } + void TitleBar::setActive(bool isActive) + { + m_isActive = isActive; + } + bool TitleBar::isVisible(std::size_t tbiIndex) const { return !(m_tbi.rgstate[tbiIndex] & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN)); diff --git a/DDrawCompat/Gdi/TitleBar.h b/DDrawCompat/Gdi/TitleBar.h index d2a333b..225ba4f 100644 --- a/DDrawCompat/Gdi/TitleBar.h +++ b/DDrawCompat/Gdi/TitleBar.h @@ -15,6 +15,7 @@ namespace Gdi void drawButtons() const; void drawCaption() const; void excludeFromClipRegion() const; + void setActive(bool isActive); private: void adjustButtonSize(RECT& rect) const; @@ -29,5 +30,6 @@ namespace Gdi RECT m_windowRect; bool m_hasIcon; bool m_hasTitleBar; + bool m_isActive; }; } diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index 4c4fdac..0c79867 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include #include @@ -38,15 +38,15 @@ namespace HWINEVENTHOOK g_objectStateChangeEventHook = nullptr; std::set g_windowPosChangeNotifyFuncs; - Compat::CriticalSection g_windowProcCs; + Compat::SrwLock g_windowProcSrwLock; std::map g_windowProc; WindowProc getWindowProc(HWND hwnd); - void onActivate(HWND hwnd); + bool isTopLevelWindow(HWND hwnd); void onCreateWindow(HWND hwnd); void onDestroyWindow(HWND hwnd); void onWindowPosChanged(HWND hwnd); - void onWindowPosChanging(HWND hwnd, const WINDOWPOS& wp); + void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp); void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW); LRESULT CALLBACK ddcWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, @@ -56,6 +56,14 @@ namespace switch (uMsg) { + case WM_SYNCPAINT: + if (isTopLevelWindow(hwnd)) + { + Gdi::Window::onSyncPaint(hwnd); + return 0; + } + break; + case WM_WINDOWPOSCHANGED: onWindowPosChanged(hwnd); break; @@ -65,18 +73,14 @@ namespace switch (uMsg) { - case WM_ACTIVATE: - onActivate(hwnd); - break; - case WM_NCDESTROY: onDestroyWindow(hwnd); break; case WM_STYLECHANGED: - if (GWL_EXSTYLE == wParam) + if (isTopLevelWindow(hwnd)) { - onWindowPosChanged(hwnd); + Gdi::Window::onStyleChanged(hwnd, wParam); } break; @@ -103,7 +107,7 @@ namespace { if (GWL_WNDPROC == nIndex) { - Compat::ScopedCriticalSection lock(g_windowProcCs); + Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock); auto it = g_windowProc.find(hWnd); if (it != g_windowProc.end()) { @@ -127,7 +131,7 @@ namespace WindowProc getWindowProc(HWND hwnd) { - Compat::ScopedCriticalSection lock(g_windowProcCs); + Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock); return g_windowProc[hwnd]; } @@ -153,6 +157,11 @@ namespace return TRUE; } + bool isTopLevelWindow(HWND hwnd) + { + return GetDesktopWindow() == GetAncestor(hwnd, GA_PARENT); + } + void CALLBACK objectCreateEvent( HWINEVENTHOOK /*hWinEventHook*/, DWORD /*event*/, @@ -207,26 +216,28 @@ namespace } } - void onActivate(HWND hwnd) - { - RECT windowRect = {}; - GetWindowRect(hwnd, &windowRect); - RECT clientRect = {}; - GetClientRect(hwnd, &clientRect); - POINT clientOrigin = {}; - ClientToScreen(hwnd, &clientOrigin); - OffsetRect(&windowRect, -clientOrigin.x, -clientOrigin.y); - - HRGN ncRgn = CreateRectRgnIndirect(&windowRect); - HRGN clientRgn = CreateRectRgnIndirect(&clientRect); - CombineRgn(ncRgn, ncRgn, clientRgn, RGN_DIFF); - RedrawWindow(hwnd, nullptr, ncRgn, RDW_FRAME | RDW_INVALIDATE); - DeleteObject(clientRgn); - DeleteObject(ncRgn); - } - void onCreateWindow(HWND hwnd) { + LOG_FUNC("onCreateWindow", hwnd); + + { + Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock); + if (g_windowProc.find(hwnd) != g_windowProc.end()) + { + return; + } + + auto wndProcA = reinterpret_cast(CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC)); + auto wndProcW = reinterpret_cast(CALL_ORIG_FUNC(GetWindowLongW)(hwnd, GWL_WNDPROC)); + g_windowProc[hwnd] = { wndProcA, wndProcW }; + setWindowProc(hwnd, ddcWindowProcA, ddcWindowProcW); + } + + if (!isTopLevelWindow(hwnd)) + { + return; + } + char className[64] = {}; GetClassName(hwnd, className, sizeof(className)); if (std::string(className) == "CompatWindowDesktopReplacement") @@ -236,27 +247,19 @@ namespace return; } - if (!Gdi::Window::isPresentationWindow(hwnd)) - { - Compat::ScopedCriticalSection lock(g_windowProcCs); - if (g_windowProc.find(hwnd) == g_windowProc.end()) - { - auto wndProcA = reinterpret_cast(CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC)); - auto wndProcW = reinterpret_cast(CALL_ORIG_FUNC(GetWindowLongW)(hwnd, GWL_WNDPROC)); - g_windowProc[hwnd] = { wndProcA, wndProcW }; - setWindowProc(hwnd, ddcWindowProcA, ddcWindowProcW); - } - } - - Gdi::Window::add(hwnd); + Gdi::Window::updateAll(); } void onDestroyWindow(HWND hwnd) { - Gdi::Window::remove(hwnd); - delete reinterpret_cast(RemoveProp(hwnd, PROP_DDRAWCOMPAT)); + if (isTopLevelWindow(hwnd)) + { + Gdi::Window::updateAll(); + return; + } - Compat::ScopedCriticalSection lock(g_windowProcCs); + delete reinterpret_cast(RemoveProp(hwnd, PROP_DDRAWCOMPAT)); + Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock); auto it = g_windowProc.find(hwnd); if (it != g_windowProc.end()) { @@ -267,24 +270,13 @@ namespace void onWindowPosChanged(HWND hwnd) { - if (Gdi::MENU_ATOM == GetClassLongPtr(hwnd, GCW_ATOM)) - { - auto exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE); - if (exStyle & WS_EX_LAYERED) - { - CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED); - return; - } - } - for (auto notifyFunc : g_windowPosChangeNotifyFuncs) { notifyFunc(); } - if (Gdi::Window::isTopLevelWindow(hwnd)) + if (isTopLevelWindow(hwnd)) { - Gdi::Window::add(hwnd); Gdi::Window::updateAll(); } else @@ -321,9 +313,13 @@ namespace } } - void onWindowPosChanging(HWND hwnd, const WINDOWPOS& wp) + void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp) { - if (!Gdi::Window::isTopLevelWindow(hwnd)) + if (isTopLevelWindow(hwnd)) + { + wp.flags |= SWP_NOREDRAW; + } + else { std::unique_ptr cwi(reinterpret_cast(RemoveProp(hwnd, PROP_DDRAWCOMPAT))); if (!(wp.flags & SWP_NOMOVE) && IsWindowVisible(hwnd) && !IsIconic(GetAncestor(hwnd, GA_ROOT))) @@ -339,12 +335,25 @@ namespace } } + 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 (result) + { + Gdi::Window::updateLayeredWindowInfo(hwnd, + (dwFlags & LWA_COLORKEY) ? crKey : CLR_INVALID, + (dwFlags & LWA_ALPHA) ? bAlpha : 255); + } + return LOG_RESULT(result); + } + LONG setWindowLong(HWND hWnd, int nIndex, LONG dwNewLong, decltype(&SetWindowLongA) origSetWindowLong, WNDPROC(WindowProc::* wndProc)) { if (GWL_WNDPROC == nIndex) { - Compat::ScopedCriticalSection lock(g_windowProcCs); + Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock); auto it = g_windowProc.find(hWnd); if (it != g_windowProc.end() && 0 != origSetWindowLong(hWnd, nIndex, dwNewLong)) { @@ -385,6 +394,7 @@ namespace wp.cy = cy; wp.flags = uFlags; onWindowPosChanging(hWnd, wp); + uFlags = wp.flags; } BOOL result = CALL_ORIG_FUNC(SetWindowPos)(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags); delete reinterpret_cast(RemoveProp(hWnd, PROP_DDRAWCOMPAT)); @@ -402,6 +412,34 @@ namespace CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast(wndProcA)); } } + + 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 (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 (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 @@ -411,7 +449,7 @@ namespace Gdi void dllThreadDetach() { auto threadId = GetCurrentThreadId(); - Compat::ScopedCriticalSection lock(g_windowProcCs); + Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock); auto it = g_windowProc.begin(); while (it != g_windowProc.end()) { @@ -430,9 +468,12 @@ namespace Gdi { HOOK_FUNCTION(user32, GetWindowLongA, getWindowLongA); HOOK_FUNCTION(user32, GetWindowLongW, getWindowLongW); + HOOK_FUNCTION(user32, SetLayeredWindowAttributes, setLayeredWindowAttributes); HOOK_FUNCTION(user32, SetWindowLongA, setWindowLongA); HOOK_FUNCTION(user32, SetWindowLongW, setWindowLongW); HOOK_FUNCTION(user32, SetWindowPos, setWindowPos); + HOOK_FUNCTION(user32, UpdateLayeredWindow, updateLayeredWindow); + HOOK_FUNCTION(user32, UpdateLayeredWindowIndirect, updateLayeredWindowIndirect); g_objectCreateEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, Dll::g_currentModule, &objectCreateEvent, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); @@ -443,6 +484,14 @@ namespace Gdi Gdi::Window::updateAll(); } + void onCreateWindow(HWND hwnd) + { + if (g_objectCreateEventHook) + { + ::onCreateWindow(hwnd); + } + } + void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc) { g_windowPosChangeNotifyFuncs.insert(notifyFunc); @@ -453,7 +502,7 @@ namespace Gdi UnhookWinEvent(g_objectStateChangeEventHook); UnhookWinEvent(g_objectCreateEventHook); - Compat::ScopedCriticalSection lock(g_windowProcCs); + Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock); for (const auto& windowProc : g_windowProc) { setWindowProc(windowProc.first, windowProc.second.wndProcA, windowProc.second.wndProcW); diff --git a/DDrawCompat/Gdi/WinProc.h b/DDrawCompat/Gdi/WinProc.h index 33acfdf..3843ce4 100644 --- a/DDrawCompat/Gdi/WinProc.h +++ b/DDrawCompat/Gdi/WinProc.h @@ -1,6 +1,6 @@ #pragma once -#include "Gdi.h" +#include namespace Gdi { @@ -8,6 +8,7 @@ namespace Gdi { void dllThreadDetach(); void installHooks(); + void onCreateWindow(HWND hwnd); void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc); void uninstallHooks(); } diff --git a/DDrawCompat/Gdi/Window.cpp b/DDrawCompat/Gdi/Window.cpp index b8b8802..f8799d3 100644 --- a/DDrawCompat/Gdi/Window.cpp +++ b/DDrawCompat/Gdi/Window.cpp @@ -1,22 +1,90 @@ +#include +#include +#include + #include #include #include +#include #include #include #include -#include +#include #include namespace { + struct UpdateWindowContext + { + Gdi::Region obscuredRegion; + Gdi::Region virtualScreenRegion; + DWORD processId; + }; + + struct Window + { + HWND hwnd; + HWND presentationWindow; + RECT windowRect; + Gdi::Region windowRegion; + Gdi::Region visibleRegion; + Gdi::Region invalidatedRegion; + COLORREF colorKey; + BYTE alpha; + bool isLayered; + bool isRgnChangePending; + + Window(HWND hwnd) + : hwnd(hwnd) + , presentationWindow(nullptr) + , windowRect{} + , windowRegion(nullptr) + , colorKey(CLR_INVALID) + , alpha(255) + , isLayered(true) + , isRgnChangePending(false) + { + } + }; + const UINT WM_CREATEPRESENTATIONWINDOW = WM_USER; - const UINT WM_DESTROYPRESENTATIONWINDOW = WM_USER + 1; - const UINT WM_SETPRESENTATIONWINDOWPOS = WM_USER + 2; + const UINT WM_SETPRESENTATIONWINDOWPOS = WM_USER + 1; + const UINT WM_SETPRESENTATIONWINDOWRGN = WM_USER + 2; + const RECT REGION_OVERRIDE_MARKER_RECT = { 32000, 32000, 32001, 32001 }; HANDLE g_presentationWindowThread = nullptr; DWORD g_presentationWindowThreadId = 0; HWND g_messageWindow = nullptr; + std::map g_windows; + std::vector g_windowZOrder; + + std::map::iterator addWindow(HWND hwnd) + { + DWMNCRENDERINGPOLICY ncRenderingPolicy = DWMNCRP_DISABLED; + DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncRenderingPolicy, sizeof(ncRenderingPolicy)); + + BOOL disableTransitions = TRUE; + DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disableTransitions, sizeof(disableTransitions)); + + const auto style = GetClassLongPtr(hwnd, GCL_STYLE); + if (style & CS_DROPSHADOW) + { + SetClassLongPtr(hwnd, GCL_STYLE, style & ~CS_DROPSHADOW); + } + + return g_windows.emplace(hwnd, Window(hwnd)).first; + } + + Gdi::Region getWindowRegion(HWND hwnd) + { + Gdi::Region rgn; + if (ERROR == CALL_ORIG_FUNC(GetWindowRgn)(hwnd, rgn)) + { + return nullptr; + } + return rgn; + } LRESULT CALLBACK messageWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { @@ -25,25 +93,18 @@ namespace { case WM_CREATEPRESENTATIONWINDOW: { - D3dDdi::ScopedCriticalSection lock; - HWND origWindow = reinterpret_cast(lParam); - auto window = Gdi::Window::get(origWindow); - if (!window) - { - return 0; - } - // Workaround for ForceSimpleWindow shim static auto origCreateWindowExA = reinterpret_cast( Compat::getProcAddress(GetModuleHandle("user32"), "CreateWindowExA")); + HWND owner = reinterpret_cast(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, - origWindow, + owner, nullptr, nullptr, nullptr); @@ -51,11 +112,9 @@ namespace if (presentationWindow) { CALL_ORIG_FUNC(SetLayeredWindowAttributes)(presentationWindow, 0, 255, LWA_ALPHA); - SendMessage(presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast(origWindow)); - window->setPresentationWindow(presentationWindow); } - return 0; + return reinterpret_cast(presentationWindow); } case WM_DESTROY: @@ -73,62 +132,21 @@ namespace switch (uMsg) { - case WM_DESTROYPRESENTATIONWINDOW: - DestroyWindow(hwnd); - return 0; - case WM_SETPRESENTATIONWINDOWPOS: { - HWND owner = reinterpret_cast(lParam); - if (IsWindowVisible(owner) && !IsIconic(owner)) + const auto& wp = *reinterpret_cast(lParam); + return SetWindowPos(hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags); + } + + case WM_SETPRESENTATIONWINDOWRGN: + { + HRGN rgn = nullptr; + if (wParam) { - DWORD flags = SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW; - - HWND insertAfter = HWND_TOP; - HWND prev = GetWindow(owner, GW_HWNDPREV); - if (prev) - { - if (hwnd == prev) - { - flags = flags | SWP_NOZORDER; - } - else - { - insertAfter = prev; - } - } - - RECT wr = {}; - GetWindowRect(owner, &wr); - RECT wrPres = {}; - GetWindowRect(hwnd, &wrPres); - if (!(flags & SWP_NOZORDER) || !EqualRect(&wr, &wrPres) || !IsWindowVisible(hwnd)) - { - CALL_ORIG_FUNC(SetWindowPos)( - hwnd, insertAfter, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, flags); - } - - Gdi::Region rgn; - int rgnResult = GetWindowRgn(owner, rgn); - Gdi::Region rgnPres; - int rgnPresResult = GetWindowRgn(hwnd, rgnPres); - if (rgnResult != rgnPresResult || !EqualRgn(rgn, rgnPres)) - { - if (ERROR != rgnResult) - { - SetWindowRgn(hwnd, rgn.release(), FALSE); - } - else - { - SetWindowRgn(hwnd, nullptr, FALSE); - } - } + rgn = CreateRectRgn(0, 0, 0, 0); + CombineRgn(rgn, reinterpret_cast(wParam), nullptr, RGN_COPY); } - else if (IsWindowVisible(hwnd)) - { - ShowWindow(hwnd, SW_HIDE); - } - return 0; + return SetWindowRgn(hwnd, rgn, FALSE); } } @@ -162,334 +180,469 @@ namespace return 0; } - BOOL WINAPI setLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags) + LRESULT sendMessageBlocking(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - LOG_FUNC("SetLayeredWindowAttributes", hwnd, crKey, bAlpha, dwFlags); - BOOL result = CALL_ORIG_FUNC(SetLayeredWindowAttributes)(hwnd, crKey, bAlpha, dwFlags); - if (result) - { - Gdi::Window::updateLayeredWindowInfo(hwnd, - (dwFlags & LWA_COLORKEY) ? crKey : CLR_INVALID, - (dwFlags & LWA_ALPHA) ? bAlpha : 255); - } - return LOG_RESULT(result); + DWORD_PTR result = 0; + SendMessageTimeout(hwnd, msg, wParam, lParam, SMTO_BLOCK | SMTO_NOTIMEOUTIFNOTHUNG, 0, &result); + return result; } - BOOL WINAPI updateLayeredWindow(HWND hWnd, HDC hdcDst, POINT* pptDst, SIZE* psize, - HDC hdcSrc, POINT* pptSrc, COLORREF crKey, BLENDFUNCTION* pblend, DWORD dwFlags) + void updatePosition(Window& window, const RECT& oldWindowRect, const Gdi::Region& oldVisibleRegion) { - 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 (result && hdcSrc) + Gdi::Region preservedRegion(oldVisibleRegion); + preservedRegion.offset(window.windowRect.left - oldWindowRect.left, window.windowRect.top - oldWindowRect.top); + preservedRegion &= window.visibleRegion; + + POINT clientPos = {}; + ClientToScreen(window.hwnd, &clientPos); + if (!preservedRegion.isEmpty()) { - Gdi::Window::updateLayeredWindowInfo(hWnd, - (dwFlags & ULW_COLORKEY) ? crKey : CLR_INVALID, - ((dwFlags & LWA_ALPHA) && pblend) ? pblend->SourceConstantAlpha : 255); + Gdi::Region updateRegion; + GetUpdateRgn(window.hwnd, updateRegion, FALSE); + OffsetRgn(updateRegion, clientPos.x, clientPos.y); + preservedRegion -= updateRegion; + + if (!preservedRegion.isEmpty() && + (window.windowRect.left != oldWindowRect.left || window.windowRect.top != oldWindowRect.top)) + { + HDC screenDc = GetDC(nullptr); + SelectClipRgn(screenDc, preservedRegion); + BitBlt(screenDc, window.windowRect.left, window.windowRect.top, + oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top, + screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY); + SelectClipRgn(screenDc, nullptr); + CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc); + } } - return LOG_RESULT(result); + + window.invalidatedRegion = window.visibleRegion - preservedRegion; + OffsetRgn(window.invalidatedRegion, -clientPos.x, -clientPos.y); } - BOOL WINAPI updateLayeredWindowIndirect(HWND hwnd, const UPDATELAYEREDWINDOWINFO* pULWInfo) + BOOL CALLBACK updateWindow(HWND hwnd, LPARAM lParam) { - LOG_FUNC("UpdateLayeredWindowIndirect", hwnd, pULWInfo); - BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindowIndirect)(hwnd, pULWInfo); - if (result && pULWInfo) + auto& context = *reinterpret_cast(lParam); + + DWORD processId = 0; + GetWindowThreadProcessId(hwnd, &processId); + if (GetWindowThreadProcessId(hwnd, &processId) == g_presentationWindowThreadId || + processId != context.processId) { - Gdi::Window::updateLayeredWindowInfo(hwnd, - (pULWInfo->dwFlags & ULW_COLORKEY) ? pULWInfo->crKey : CLR_INVALID, - ((pULWInfo->dwFlags & LWA_ALPHA) && pULWInfo->pblend) ? pULWInfo->pblend->SourceConstantAlpha : 255); + return TRUE; } - return LOG_RESULT(result); + + auto it = g_windows.find(hwnd); + if (it == g_windows.end()) + { + it = addWindow(hwnd); + } + g_windowZOrder.push_back(&it->second); + + const LONG exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE); + const bool isLayered = exStyle & WS_EX_LAYERED; + const bool isVisible = IsWindowVisible(hwnd) && !IsIconic(hwnd); + bool setPresentationWindowRgn = false; + + if (isLayered != it->second.isLayered) + { + it->second.isLayered = isLayered; + it->second.isRgnChangePending = isVisible; + if (!isLayered) + { + it->second.presentationWindow = reinterpret_cast( + sendMessageBlocking(g_messageWindow, WM_CREATEPRESENTATIONWINDOW, reinterpret_cast(hwnd), 0)); + setPresentationWindowRgn = true; + } + else if (it->second.presentationWindow) + { + sendMessageBlocking(it->second.presentationWindow, WM_CLOSE, 0, 0); + it->second.presentationWindow = nullptr; + } + } + + Gdi::Region windowRegion(getWindowRegion(hwnd)); + if (windowRegion && !PtInRegion(windowRegion, REGION_OVERRIDE_MARKER_RECT.left, REGION_OVERRIDE_MARKER_RECT.top) || + !windowRegion && it->second.windowRegion) + { + swap(it->second.windowRegion, windowRegion); + setPresentationWindowRgn = true; + } + + RECT windowRect = {}; + Gdi::Region visibleRegion; + + if (isVisible) + { + GetWindowRect(hwnd, &windowRect); + if (!IsRectEmpty(&windowRect)) + { + if (it->second.windowRegion) + { + visibleRegion = it->second.windowRegion; + OffsetRgn(visibleRegion, windowRect.left, windowRect.top); + } + else + { + visibleRegion = windowRect; + } + visibleRegion &= context.virtualScreenRegion; + visibleRegion -= context.obscuredRegion; + + if (!isLayered && !(exStyle & WS_EX_TRANSPARENT)) + { + context.obscuredRegion |= visibleRegion; + } + } + } + + std::swap(it->second.windowRect, windowRect); + swap(it->second.visibleRegion, visibleRegion); + + if (!isLayered) + { + if (!it->second.visibleRegion.isEmpty()) + { + updatePosition(it->second, windowRect, visibleRegion); + } + + if (isVisible && !it->second.isRgnChangePending) + { + OffsetRgn(visibleRegion, it->second.windowRect.left - windowRect.left, it->second.windowRect.top - windowRect.top); + if (it->second.visibleRegion != visibleRegion) + { + it->second.isRgnChangePending = true; + } + } + + if (it->second.presentationWindow) + { + if (setPresentationWindowRgn) + { + HRGN rgn = it->second.windowRegion; + sendMessageBlocking(it->second.presentationWindow, WM_SETPRESENTATIONWINDOWRGN, + reinterpret_cast(rgn), 0); + } + + WINDOWPOS wp = {}; + wp.flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOSENDCHANGING; + if (isVisible) + { + wp.hwndInsertAfter = GetWindow(hwnd, GW_HWNDPREV); + if (!wp.hwndInsertAfter) + { + wp.hwndInsertAfter = (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_TOP; + } + else if (wp.hwndInsertAfter == it->second.presentationWindow) + { + wp.flags |= SWP_NOZORDER; + } + + wp.x = it->second.windowRect.left; + wp.y = it->second.windowRect.top; + wp.cx = it->second.windowRect.right - it->second.windowRect.left; + wp.cy = it->second.windowRect.bottom - it->second.windowRect.top; + wp.flags |= SWP_SHOWWINDOW; + } + else + { + wp.flags |= SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER; + } + + sendMessageBlocking(it->second.presentationWindow, WM_SETPRESENTATIONWINDOWPOS, + 0, reinterpret_cast(&wp)); + } + } + return TRUE; } } namespace Gdi { - Window::Window(HWND hwnd) - : m_hwnd(hwnd) - , m_presentationWindow(nullptr) - , m_windowRect{ 0, 0, 0, 0 } - , m_colorKey(CLR_INVALID) - , m_alpha(255) - , m_isLayered(CALL_ORIG_FUNC(GetWindowLongA)(m_hwnd, GWL_EXSTYLE)& WS_EX_LAYERED) + namespace Window { - DWMNCRENDERINGPOLICY ncRenderingPolicy = DWMNCRP_DISABLED; - DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncRenderingPolicy, sizeof(ncRenderingPolicy)); - - BOOL disableTransitions = TRUE; - DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disableTransitions, sizeof(disableTransitions)); - - const auto style = GetClassLongPtr(hwnd, GCL_STYLE); - if (style & CS_DROPSHADOW) + void installHooks() { - SetClassLongPtr(hwnd, GCL_STYLE, style & ~CS_DROPSHADOW); + WNDCLASS wc = {}; + wc.lpfnWndProc = &presentationWindowProc; + wc.hInstance = Dll::g_currentModule; + wc.lpszClassName = "DDrawCompatPresentationWindow"; + RegisterClass(&wc); + + g_presentationWindowThread = CreateThread( + nullptr, 0, &presentationWindowThreadProc, nullptr, 0, &g_presentationWindowThreadId); + + int i = 0; + while (!g_messageWindow && i < 1000) + { + Sleep(1); + ++i; + } + + if (!g_messageWindow) + { + Compat::Log() << "ERROR: Failed to create a message-only window"; + } } - if (!m_isLayered) + bool isPresentationWindow(HWND hwnd) { - SendNotifyMessage(g_messageWindow, WM_CREATEPRESENTATIONWINDOW, 0, reinterpret_cast(hwnd)); + return g_presentationWindowThreadId == GetWindowThreadProcessId(hwnd, nullptr); } - update(); - } - - Window::~Window() - { - if (m_presentationWindow) + void onStyleChanged(HWND hwnd, WPARAM wParam) { - SendNotifyMessage(m_presentationWindow, WM_DESTROYPRESENTATIONWINDOW, 0, 0); + if (GWL_EXSTYLE == wParam) + { + D3dDdi::ScopedCriticalSection lock; + auto it = g_windows.find(hwnd); + if (it != g_windows.end()) + { + const bool isLayered = GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED; + if (isLayered != it->second.isLayered) + { + updateAll(); + } + } + } } - } - bool Window::add(HWND hwnd) - { - if (isTopLevelWindow(hwnd) && !get(hwnd)) + void onSyncPaint(HWND hwnd) + { + LOG_FUNC("Window::onSyncPaint", hwnd); + bool isInvalidated = false; + + { + D3dDdi::ScopedCriticalSection lock; + auto it = g_windows.find(hwnd); + if (it == g_windows.end()) + { + return; + } + + if (it->second.isRgnChangePending) + { + it->second.isRgnChangePending = false; + const LONG origWndProc = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC); + CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast(CALL_ORIG_FUNC(DefWindowProcA))); + if (it->second.isLayered) + { + SetWindowRgn(hwnd, Gdi::Region(it->second.windowRegion).release(), FALSE); + } + else + { + Gdi::Region rgn(it->second.visibleRegion); + OffsetRgn(rgn, -it->second.windowRect.left, -it->second.windowRect.top); + rgn |= REGION_OVERRIDE_MARKER_RECT; + SetWindowRgn(hwnd, rgn, FALSE); + } + CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, origWndProc); + } + + isInvalidated = !it->second.invalidatedRegion.isEmpty(); + if (isInvalidated) + { + RedrawWindow(hwnd, nullptr, it->second.invalidatedRegion, + RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN); + it->second.invalidatedRegion = nullptr; + } + } + + if (isInvalidated) + { + RECT emptyRect = {}; + RedrawWindow(hwnd, &emptyRect, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ERASENOW); + } + } + + void present(CompatRef dst, CompatRef src, + CompatRef clipper) { D3dDdi::ScopedCriticalSection lock; - s_windows.emplace(hwnd, std::make_shared(hwnd)); - return true; - } - - return false; - } - - void Window::calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion) - { - if (IsRectEmpty(&m_windowRect) || m_visibleRegion.isEmpty()) - { - m_invalidatedRegion = Region(); - return; - } - - m_invalidatedRegion = m_visibleRegion; - - if (m_windowRect.right - m_windowRect.left == oldWindowRect.right - oldWindowRect.left && - m_windowRect.bottom - m_windowRect.top == oldWindowRect.bottom - oldWindowRect.top) - { - Region preservedRegion(oldVisibleRegion); - preservedRegion.offset(m_windowRect.left - oldWindowRect.left, m_windowRect.top - oldWindowRect.top); - preservedRegion &= m_visibleRegion; - - if (!preservedRegion.isEmpty()) + for (auto window : g_windowZOrder) { - if (m_windowRect.left != oldWindowRect.left || m_windowRect.top != oldWindowRect.top) + if (window->presentationWindow && !window->visibleRegion.isEmpty()) { - HDC screenDc = GetDC(nullptr); - SelectClipRgn(screenDc, preservedRegion); - BitBlt(screenDc, m_windowRect.left, m_windowRect.top, - oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top, - screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY); - SelectClipRgn(screenDc, nullptr); - CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc); + clipper->SetHWnd(&clipper, 0, window->presentationWindow); + dst->Blt(&dst, nullptr, &src, nullptr, DDBLT_WAIT, nullptr); } - m_invalidatedRegion -= preservedRegion; } } - } - std::shared_ptr Window::get(HWND hwnd) - { - D3dDdi::ScopedCriticalSection lock; - auto it = s_windows.find(hwnd); - return it != s_windows.end() ? it->second : nullptr; - } - - BYTE Window::getAlpha() const - { - D3dDdi::ScopedCriticalSection lock; - return m_alpha; - } - - COLORREF Window::getColorKey() const - { - D3dDdi::ScopedCriticalSection lock; - return m_colorKey; - } - - HWND Window::getPresentationWindow() const - { - return m_presentationWindow ? m_presentationWindow : m_hwnd; - } - - Region Window::getVisibleRegion() const - { - D3dDdi::ScopedCriticalSection lock; - return m_visibleRegion; - } - - RECT Window::getWindowRect() const - { - D3dDdi::ScopedCriticalSection lock; - return m_windowRect; - } - - std::map> Window::getWindows() - { - D3dDdi::ScopedCriticalSection lock; - return s_windows; - } - - 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 = Dll::g_currentModule; - wc.lpszClassName = "DDrawCompatPresentationWindow"; - RegisterClass(&wc); - - g_presentationWindowThread = CreateThread( - nullptr, 0, &presentationWindowThreadProc, nullptr, 0, &g_presentationWindowThreadId); - - int i = 0; - while (!g_messageWindow && i < 1000) + void present(Gdi::Region excludeRegion) { - Sleep(1); - ++i; - } + D3dDdi::ScopedCriticalSection lock; + std::unique_ptr virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc); + RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds(); - if (!g_messageWindow) - { - Compat::Log() << "ERROR: Failed to create a message-only window"; - } - } - - bool Window::isLayered() const - { - return m_isLayered; - } - - bool Window::isPresentationWindow(HWND hwnd) - { - return IsWindow(hwnd) && g_presentationWindowThreadId == GetWindowThreadProcessId(hwnd, nullptr); - } - - bool Window::isTopLevelWindow(HWND hwnd) - { - return GetDesktopWindow() == GetAncestor(hwnd, GA_PARENT); - } - - void Window::remove(HWND hwnd) - { - D3dDdi::ScopedCriticalSection lock; - s_windows.erase(hwnd); - } - - void Window::uninstallHooks() - { - if (g_presentationWindowThread) - { - SendMessage(g_messageWindow, WM_CLOSE, 0, 0); - if (WAIT_OBJECT_0 != WaitForSingleObject(g_presentationWindowThread, 1000)) + for (auto window : g_windowZOrder) { - TerminateThread(g_presentationWindowThread, 0); - Compat::Log() << "The presentation window thread was terminated forcefully"; + if (!window->presentationWindow) + { + continue; + } + + Gdi::Region visibleRegion(window->visibleRegion); + if (excludeRegion) + { + visibleRegion -= excludeRegion; + } + if (visibleRegion.isEmpty()) + { + continue; + } + + if (!virtualScreenDc) + { + virtualScreenDc.reset(Gdi::VirtualScreen::createDc()); + if (!virtualScreenDc) + { + return; + } + } + + HDC dc = GetWindowDC(window->presentationWindow); + RECT rect = window->windowRect; + visibleRegion.offset(-rect.left, -rect.top); + SelectClipRgn(dc, visibleRegion); + CALL_ORIG_FUNC(BitBlt)(dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, virtualScreenDc.get(), + rect.left - virtualScreenBounds.left, rect.top - virtualScreenBounds.top, SRCCOPY); + CALL_ORIG_FUNC(ReleaseDC)(window->presentationWindow, dc); } } - } - void Window::setPresentationWindow(HWND hwnd) - { - D3dDdi::ScopedCriticalSection lock; - if (m_isLayered) + void presentLayered(CompatRef dst, POINT offset) { - SendNotifyMessage(hwnd, WM_DESTROYPRESENTATIONWINDOW, 0, 0); - } - else - { - m_presentationWindow = hwnd; - SendNotifyMessage(m_presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast(m_hwnd)); - DDraw::RealPrimarySurface::scheduleUpdate(); - } - } + D3dDdi::ScopedCriticalSection lock; - void Window::update() - { - D3dDdi::ScopedCriticalSection lock; - const bool isLayered = CALL_ORIG_FUNC(GetWindowLongA)(m_hwnd, GWL_EXSTYLE) & WS_EX_LAYERED; - if (isLayered != m_isLayered) - { - if (!isLayered) + HDC dstDc = nullptr; + for (auto it = g_windowZOrder.rbegin(); it != g_windowZOrder.rend(); ++it) { - SendNotifyMessage(g_messageWindow, WM_CREATEPRESENTATIONWINDOW, 0, reinterpret_cast(m_hwnd)); + auto& window = **it; + if (!window.isLayered) + { + continue; + } + + if (!dstDc) + { + const UINT D3DDDIFMT_UNKNOWN = 0; + const UINT D3DDDIFMT_X8R8G8B8 = 22; + D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_X8R8G8B8); + dst->GetDC(&dst, &dstDc); + D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_UNKNOWN); + if (!dstDc) + { + return; + } + } + + HDC windowDc = GetWindowDC(window.hwnd); + Gdi::Region rgn(window.visibleRegion); + RECT wr = window.windowRect; + + if (0 != offset.x || 0 != offset.y) + { + OffsetRect(&wr, offset.x, offset.y); + rgn.offset(offset.x, offset.y); + } + + SelectClipRgn(dstDc, rgn); + + auto colorKey = window.colorKey; + if (CLR_INVALID != colorKey) + { + CALL_ORIG_FUNC(TransparentBlt)(dstDc, 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 = window.alpha; + CALL_ORIG_FUNC(AlphaBlend)(dstDc, 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)(window.hwnd, windowDc); } - else if (m_presentationWindow) + + if (dstDc) { - SendNotifyMessage(m_presentationWindow, WM_DESTROYPRESENTATIONWINDOW, 0, 0); - m_presentationWindow = nullptr; + SelectClipRgn(dstDc, nullptr); + dst->ReleaseDC(&dst, dstDc); } } - m_isLayered = isLayered; - RECT newWindowRect = {}; - Region newVisibleRegion; - - if (IsWindowVisible(m_hwnd) && !IsIconic(m_hwnd)) + void updateAll() { - GetWindowRect(m_hwnd, &newWindowRect); - if (!IsRectEmpty(&newWindowRect) && !m_isLayered) + LOG_FUNC("Window::updateAll"); + UpdateWindowContext context; + context.processId = GetCurrentProcessId(); + context.virtualScreenRegion = VirtualScreen::getRegion(); + std::vector invalidatedWindows; + { - newVisibleRegion = m_hwnd; + D3dDdi::ScopedCriticalSection lock; + g_windowZOrder.clear(); + EnumWindows(updateWindow, reinterpret_cast(&context)); + + for (auto it = g_windows.begin(); it != g_windows.end();) + { + if (g_windowZOrder.end() == std::find(g_windowZOrder.begin(), g_windowZOrder.end(), &it->second)) + { + if (it->second.presentationWindow) + { + sendMessageBlocking(it->second.presentationWindow, WM_CLOSE, 0, 0); + } + it = g_windows.erase(it); + } + else + { + ++it; + } + } + + for (auto it = g_windowZOrder.rbegin(); it != g_windowZOrder.rend(); ++it) + { + auto& window = **it; + if (window.isRgnChangePending || !window.invalidatedRegion.isEmpty()) + { + invalidatedWindows.push_back(window.hwnd); + } + } } - } - if (m_presentationWindow) - { - SendNotifyMessage(m_presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast(m_hwnd)); - } - - std::swap(m_windowRect, newWindowRect); - swap(m_visibleRegion, newVisibleRegion); - - calcInvalidatedRegion(newWindowRect, newVisibleRegion); - } - - void Window::updateAll() - { - auto windows(getWindows()); - for (auto& windowPair : windows) - { - windowPair.second->update(); - } - - for (auto& windowPair : windows) - { - if (!windowPair.second->m_invalidatedRegion.isEmpty()) + for (auto hwnd : invalidatedWindows) { - POINT clientOrigin = {}; - ClientToScreen(windowPair.first, &clientOrigin); - windowPair.second->m_invalidatedRegion.offset(-clientOrigin.x, -clientOrigin.y); - RedrawWindow(windowPair.first, nullptr, windowPair.second->m_invalidatedRegion, - RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASENOW); + SendNotifyMessage(hwnd, WM_SYNCPAINT, 0, 0); + } + } + + void updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha) + { + D3dDdi::ScopedCriticalSection lock; + auto it = g_windows.find(hwnd); + if (it != g_windows.end()) + { + it->second.colorKey = colorKey; + it->second.alpha = alpha; + if (!it->second.visibleRegion.isEmpty()) + { + DDraw::RealPrimarySurface::scheduleUpdate(); + } + } + } + + 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"; + } } } } - - void Window::updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha) - { - D3dDdi::ScopedCriticalSection lock; - auto window(get(hwnd)); - if (window) - { - window->m_colorKey = colorKey; - window->m_alpha = alpha; - DDraw::RealPrimarySurface::scheduleUpdate(); - } - } - - void Window::updateWindow() - { - RECT windowRect = {}; - GetWindowRect(m_hwnd, &windowRect); - if (!EqualRect(&windowRect, &m_windowRect)) - { - updateAll(); - } - } - - std::map> Window::s_windows; } diff --git a/DDrawCompat/Gdi/Window.h b/DDrawCompat/Gdi/Window.h index 0bc69a1..654febd 100644 --- a/DDrawCompat/Gdi/Window.h +++ b/DDrawCompat/Gdi/Window.h @@ -1,57 +1,25 @@ #pragma once -#include -#include +#include -#include - -#include "Gdi/Region.h" +#include +#include namespace Gdi { - class Window + namespace Window { - public: - Window(HWND hwnd); - Window(const Window&) = delete; - Window& operator=(const Window&) = delete; - ~Window(); + bool isPresentationWindow(HWND hwnd); + void onStyleChanged(HWND hwnd, WPARAM wParam); + void onSyncPaint(HWND hwnd); + void present(CompatRef dst, CompatRef src, + CompatRef clipper); + void present(Gdi::Region excludeRegion); + void presentLayered(CompatRef dst, POINT offset); + void updateAll(); + void updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha); - BYTE getAlpha() const; - COLORREF getColorKey() const; - HWND getPresentationWindow() const; - Region getVisibleRegion() const; - RECT getWindowRect() const; - bool isLayered() const; - void setPresentationWindow(HWND hwnd); - void updateWindow(); - - static bool add(HWND hwnd); - static std::shared_ptr get(HWND hwnd); - static void remove(HWND hwnd); - - static std::map> getWindows(); - static bool isPresentationWindow(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(); - - private: - void calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion); - void update(); - - HWND m_hwnd; - HWND m_presentationWindow; - RECT m_windowRect; - Region m_visibleRegion; - Region m_invalidatedRegion; - COLORREF m_colorKey; - BYTE m_alpha; - bool m_isLayered; - - static std::map> s_windows; - }; + void installHooks(); + void uninstallHooks(); + } } diff --git a/DDrawCompat/Win32/Log.cpp b/DDrawCompat/Win32/Log.cpp index aa598e6..e553bec 100644 --- a/DDrawCompat/Win32/Log.cpp +++ b/DDrawCompat/Win32/Log.cpp @@ -339,7 +339,7 @@ std::ostream& operator<<(std::ostream& os, const NMHDR& nm) << Compat::hex(nm.code); } -std::ostream& operator<<(std::ostream& os, const POINTS& p) +std::ostream& operator<<(std::ostream& os, const POINT& p) { return Compat::LogStruct(os) << p.x diff --git a/DDrawCompat/Win32/Log.h b/DDrawCompat/Win32/Log.h index 90b8f75..bb2a8e2 100644 --- a/DDrawCompat/Win32/Log.h +++ b/DDrawCompat/Win32/Log.h @@ -32,7 +32,7 @@ std::ostream& operator<<(std::ostream& os, const MINMAXINFO& mmi); std::ostream& operator<<(std::ostream& os, const MSG& msg); std::ostream& operator<<(std::ostream& os, const NCCALCSIZE_PARAMS& nccs); std::ostream& operator<<(std::ostream& os, const NMHDR& nm); -std::ostream& operator<<(std::ostream& os, const POINTS& p); +std::ostream& operator<<(std::ostream& os, const POINT& p); std::ostream& operator<<(std::ostream& os, const RECT& rect); std::ostream& operator<<(std::ostream& os, const SIZE& size); std::ostream& operator<<(std::ostream& os, const STYLESTRUCT& ss);