diff --git a/DDrawCompat/DDraw/DirectDrawClipper.cpp b/DDrawCompat/DDraw/DirectDrawClipper.cpp index d0c7daf..c240329 100644 --- a/DDrawCompat/DDraw/DirectDrawClipper.cpp +++ b/DDrawCompat/DDraw/DirectDrawClipper.cpp @@ -1,8 +1,128 @@ +#include +#include + +#include "Common/CompatRef.h" #include "DDraw/DirectDrawClipper.h" +#include "Gdi/Gdi.h" + +namespace +{ + struct ClipperData + { + HWND hwnd; + std::vector oldClipList; + }; + + std::map g_clipperData; + + void updateWindowClipList(CompatRef clipper, ClipperData& data); + + void onWindowPosChange(HWND /*hwnd*/, const RECT& oldWindowRect, const RECT& newWindowRect) + { + for (auto& clipperData : g_clipperData) + { + if (!IsRectEmpty(&oldWindowRect) || !IsRectEmpty(&newWindowRect)) + { + updateWindowClipList(*clipperData.first, clipperData.second); + } + } + } + + void updateWindowClipList(CompatRef clipper, ClipperData& data) + { + HDC dc = GetDC(data.hwnd); + HRGN rgn = CreateRectRgn(0, 0, 0, 0); + + GetRandomRgn(dc, rgn, SYSRGN); + DWORD rgnSize = GetRegionData(rgn, 0, nullptr); + std::vector rgnData(rgnSize); + GetRegionData(rgn, rgnSize, reinterpret_cast(rgnData.data())); + + clipper->SetHWnd(&clipper, 0, nullptr); + if (FAILED(clipper->SetClipList(&clipper, reinterpret_cast(rgnData.data()), 0))) + { + clipper->SetHWnd(&clipper, 0, data.hwnd); + } + + DeleteObject(rgn); + ReleaseDC(data.hwnd, dc); + } + + HRESULT STDMETHODCALLTYPE GetHWnd(IDirectDrawClipper* This, HWND* lphWnd) + { + if (lphWnd) + { + auto it = g_clipperData.find(This); + if (it != g_clipperData.end()) + { + *lphWnd = it->second.hwnd; + return DD_OK; + } + } + return DDraw::DirectDrawClipper::s_origVtable.GetHWnd(This, lphWnd); + } + + ULONG STDMETHODCALLTYPE Release(IDirectDrawClipper* This) + { + ULONG result = DDraw::DirectDrawClipper::s_origVtable.Release(This); + if (0 == result) + { + g_clipperData.erase(This); + } + return result; + } + + HRESULT STDMETHODCALLTYPE SetClipList(IDirectDrawClipper* This, LPRGNDATA lpClipList, DWORD dwFlags) + { + if (g_clipperData.find(This) != g_clipperData.end()) + { + return DDERR_CLIPPERISUSINGHWND; + } + return DDraw::DirectDrawClipper::s_origVtable.SetClipList(This, lpClipList, dwFlags); + } + + HRESULT STDMETHODCALLTYPE SetHWnd(IDirectDrawClipper* This, DWORD dwFlags, HWND hWnd) + { + const auto& origVtable = DDraw::DirectDrawClipper::s_origVtable; + + HRESULT result = origVtable.SetHWnd(This, dwFlags, hWnd); + if (SUCCEEDED(result)) + { + auto it = g_clipperData.find(This); + if (hWnd) + { + if (it == g_clipperData.end()) + { + it = g_clipperData.insert({ This, ClipperData() }).first; + it->second.hwnd = hWnd; + + DWORD size = 0; + origVtable.GetClipList(This, nullptr, nullptr, &size); + it->second.oldClipList.resize(size); + origVtable.GetClipList(This, nullptr, + reinterpret_cast(it->second.oldClipList.data()), &size); + } + updateWindowClipList(*This, it->second); + Gdi::watchWindowPosChanges(&onWindowPosChange); + } + else if (it != g_clipperData.end()) + { + origVtable.SetClipList(This, it->second.oldClipList.empty() ? nullptr : + reinterpret_cast(it->second.oldClipList.data()), 0); + g_clipperData.erase(it); + } + } + return result; + } +} namespace DDraw { - void DirectDrawClipper::setCompatVtable(IDirectDrawClipperVtbl& /*vtable*/) + void DirectDrawClipper::setCompatVtable(IDirectDrawClipperVtbl& vtable) { + vtable.GetHWnd = &GetHWnd; + vtable.Release = &Release; + vtable.SetClipList = &SetClipList; + vtable.SetHWnd = &SetHWnd; } } diff --git a/DDrawCompat/DDraw/RealPrimarySurface.cpp b/DDrawCompat/DDraw/RealPrimarySurface.cpp index ee8d310..cbb5966 100644 --- a/DDrawCompat/DDraw/RealPrimarySurface.cpp +++ b/DDrawCompat/DDraw/RealPrimarySurface.cpp @@ -378,6 +378,11 @@ namespace DDraw void RealPrimarySurface::setClipper(CompatWeakPtr clipper) { + if (g_backBuffer) + { + return; + } + HRESULT result = g_frontBuffer->SetClipper(g_frontBuffer, clipper); if (FAILED(result)) { diff --git a/DDrawCompat/Gdi/Gdi.cpp b/DDrawCompat/Gdi/Gdi.cpp index 27977c2..4ab538d 100644 --- a/DDrawCompat/Gdi/Gdi.cpp +++ b/DDrawCompat/Gdi/Gdi.cpp @@ -245,4 +245,9 @@ namespace Gdi Gdi::DcCache::updatePalette(startingEntry, count); } } + + void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc) + { + WinProc::watchWindowPosChanges(notifyFunc); + } } diff --git a/DDrawCompat/Gdi/Gdi.h b/DDrawCompat/Gdi/Gdi.h index 6ba16cb..a042d20 100644 --- a/DDrawCompat/Gdi/Gdi.h +++ b/DDrawCompat/Gdi/Gdi.h @@ -6,6 +6,8 @@ namespace Gdi { + typedef void(*WindowPosChangeNotifyFunc)(HWND, const RECT&, const RECT&); + bool beginGdiRendering(DWORD lockFlags = 0); void endGdiRendering(); @@ -20,6 +22,7 @@ namespace Gdi void unhookWndProc(LPCSTR className, WNDPROC oldWndProc); void uninstallHooks(); void updatePalette(DWORD startingEntry, DWORD count); + void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc); extern CRITICAL_SECTION g_gdiCriticalSection; }; diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index 95b9f18..e4a83b1 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -1,6 +1,7 @@ #define WIN32_LEAN_AND_MEAN #include +#include #include #include @@ -9,7 +10,6 @@ #include "Common/Log.h" #include "Common/ScopedCriticalSection.h" #include "Gdi/Dc.h" -#include "Gdi/Gdi.h" #include "Gdi/ScrollBar.h" #include "Gdi/ScrollFunctions.h" #include "Gdi/TitleBar.h" @@ -26,6 +26,7 @@ namespace HHOOK g_callWndRetProcHook = nullptr; HWINEVENTHOOK g_objectStateChangeEventHook = nullptr; std::unordered_map g_windowData; + std::set g_windowPosChangeNotifyFuncs; void disableDwmAttributes(HWND hwnd); void onActivate(HWND hwnd); @@ -196,6 +197,11 @@ namespace WindowData data = getWindowData(hwnd); g_windowData[hwnd] = data; + for (auto notifyFunc : g_windowPosChangeNotifyFuncs) + { + notifyFunc(hwnd, prevData.wndRect, data.wndRect); + } + if (!prevData.sysClipRgn && !data.sysClipRgn || !Gdi::isEmulationEnabled()) { return; @@ -272,6 +278,11 @@ namespace Gdi nullptr, &objectStateChangeEvent, 0, threadId, WINEVENT_OUTOFCONTEXT); } + void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc) + { + g_windowPosChangeNotifyFuncs.insert(notifyFunc); + } + void uninstallHooks() { UnhookWinEvent(g_objectStateChangeEventHook); diff --git a/DDrawCompat/Gdi/WinProc.h b/DDrawCompat/Gdi/WinProc.h index fd9b582..928261e 100644 --- a/DDrawCompat/Gdi/WinProc.h +++ b/DDrawCompat/Gdi/WinProc.h @@ -1,10 +1,13 @@ #pragma once +#include "Gdi.h" + namespace Gdi { namespace WinProc { void installHooks(); + void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc); void uninstallHooks(); } }