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

Fixed clipping issue when moving dialog boxes in Close Combat 4

This commit is contained in:
narzoul 2024-06-26 00:15:54 +02:00
parent 767c8d28ee
commit ddc45d7f36
10 changed files with 85 additions and 218 deletions

View File

@ -1,138 +1,14 @@
#include <map>
#include <vector>
#include <Common/CompatRef.h>
#include <Common/CompatVtable.h>
#include <D3dDdi/KernelModeThunks.h>
#include <DDraw/DirectDrawClipper.h>
#include <DDraw/ScopedThreadLock.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <DDraw/Surfaces/Surface.h>
#include <DDraw/Visitors/DirectDrawClipperVtblVisitor.h>
#include <Gdi/Gdi.h>
#include <Gdi/Region.h>
namespace
{
struct ClipperData
constexpr void setCompatVtable(IDirectDrawClipperVtbl& /*vtable*/)
{
HWND hwnd;
DWORD refCount;
std::vector<unsigned char> origClipList;
};
std::map<IDirectDrawClipper*, ClipperData> g_clipperData;
std::map<DDraw::Surface*, std::map<IDirectDrawClipper*, ClipperData>::iterator> g_surfaceToClipperData;
bool g_isInvalidated = false;
void updateWindowClipList(CompatRef<IDirectDrawClipper> clipper, ClipperData& data);
void onWindowPosChange()
{
g_isInvalidated = true;
}
void restoreOrigClipList(IDirectDrawClipper* clipper, ClipperData& clipperData)
{
getOrigVtable(clipper).SetClipList(clipper,
clipperData.origClipList.empty() ? nullptr : reinterpret_cast<RGNDATA*>(clipperData.origClipList.data()), 0);
clipperData.origClipList.clear();
}
void updateWindowClipList(CompatRef<IDirectDrawClipper> clipper, ClipperData& data)
{
HDC dc = GetDCEx(data.hwnd, nullptr, DCX_CACHE | DCX_USESTYLE);
Gdi::Region rgn;
GetRandomRgn(dc, rgn, SYSRGN);
CALL_ORIG_FUNC(ReleaseDC)(data.hwnd, dc);
auto& mi = DDraw::PrimarySurface::getMonitorInfo();
if (0 != mi.rcEmulated.left || 0 != mi.rcEmulated.top)
{
rgn.offset(-mi.rcEmulated.left, -mi.rcEmulated.top);
}
DWORD rgnSize = GetRegionData(rgn, 0, nullptr);
std::vector<unsigned char> rgnDataBuf(std::max<DWORD>(rgnSize, sizeof(RGNDATA)));
RGNDATA* rgnData = reinterpret_cast<RGNDATA*>(rgnDataBuf.data());
GetRegionData(rgn, rgnSize, rgnData);
if (0 == rgnData->rdh.nCount)
{
rgnData->rdh.nCount = 1;
}
clipper->SetHWnd(&clipper, 0, nullptr);
clipper->SetClipList(&clipper, rgnData, 0);
}
HRESULT STDMETHODCALLTYPE GetHWnd(IDirectDrawClipper* This, HWND* lphWnd)
{
if (lphWnd)
{
auto it = g_clipperData.find(This);
if (it != g_clipperData.end() && it->second.hwnd)
{
*lphWnd = it->second.hwnd;
return DD_OK;
}
}
return getOrigVtable(This).GetHWnd(This, lphWnd);
}
HRESULT STDMETHODCALLTYPE SetClipList(IDirectDrawClipper* This, LPRGNDATA lpClipList, DWORD dwFlags)
{
auto it = g_clipperData.find(This);
if (it != g_clipperData.end() && it->second.hwnd)
{
return DDERR_CLIPPERISUSINGHWND;
}
return getOrigVtable(This).SetClipList(This, lpClipList, dwFlags);
}
HRESULT STDMETHODCALLTYPE SetHWnd(IDirectDrawClipper* This, DWORD dwFlags, HWND hWnd)
{
auto it = g_clipperData.find(This);
if (it == g_clipperData.end())
{
return getOrigVtable(This).SetHWnd(This, dwFlags, hWnd);
}
std::vector<unsigned char> origClipList;
if (hWnd && !it->second.hwnd)
{
DWORD size = 0;
getOrigVtable(This).GetClipList(This, nullptr, nullptr, &size);
origClipList.resize(size);
getOrigVtable(This).GetClipList(This, nullptr, reinterpret_cast<RGNDATA*>(origClipList.data()), &size);
}
HRESULT result = getOrigVtable(This).SetHWnd(This, dwFlags, hWnd);
if (SUCCEEDED(result))
{
if (hWnd)
{
if (!it->second.hwnd)
{
it->second.origClipList = origClipList;
}
it->second.hwnd = hWnd;
updateWindowClipList(*This, it->second);
Gdi::watchWindowPosChanges(&onWindowPosChange);
}
else if (it->second.hwnd)
{
restoreOrigClipList(it->first, it->second);
it->second.hwnd = nullptr;
}
}
return result;
}
constexpr void setCompatVtable(IDirectDrawClipperVtbl& vtable)
{
vtable.GetHWnd = &GetHWnd;
vtable.SetClipList = &SetClipList;
vtable.SetHWnd = &SetHWnd;
}
}
@ -150,50 +26,6 @@ namespace DDraw
return ExtCreateRegion(nullptr, size, reinterpret_cast<RGNDATA*>(rgnData.data()));
}
void setClipper(Surface& surface, IDirectDrawClipper* clipper)
{
auto it = g_surfaceToClipperData.find(&surface);
if (it != g_surfaceToClipperData.end())
{
auto prevClipper = it->second->first;
auto& prevClipperData = it->second->second;
if (prevClipper == clipper)
{
return;
}
--prevClipperData.refCount;
if (0 == prevClipperData.refCount)
{
if (prevClipperData.hwnd)
{
restoreOrigClipList(prevClipper, prevClipperData);
getOrigVtable(prevClipper).SetHWnd(prevClipper, 0, prevClipperData.hwnd);
}
g_clipperData.erase(it->second);
}
getOrigVtable(prevClipper).Release(prevClipper);
g_surfaceToClipperData.erase(it);
}
if (clipper)
{
auto [clipperDataIter, inserted] = g_clipperData.insert({ clipper, ClipperData{} });
if (inserted)
{
HWND hwnd = nullptr;
getOrigVtable(clipper).GetHWnd(clipper, &hwnd);
if (hwnd)
{
SetHWnd(clipper, 0, hwnd);
}
}
++clipperDataIter->second.refCount;
g_surfaceToClipperData[&surface] = clipperDataIter;
getOrigVtable(clipper).AddRef(clipper);
}
}
HRESULT setClipRgn(CompatRef<IDirectDrawClipper> clipper, HRGN rgn)
{
std::vector<unsigned char> rgnData;
@ -202,14 +34,6 @@ namespace DDraw
return clipper->SetClipList(&clipper, reinterpret_cast<RGNDATA*>(rgnData.data()), 0);
}
void update()
{
for (auto& clipperData : g_clipperData)
{
updateWindowClipList(*clipperData.first, clipperData.second);
}
}
void hookVtable(const IDirectDrawClipperVtbl& vtable)
{
CompatVtable<IDirectDrawClipperVtbl>::hookVtable<ScopedThreadLock>(vtable);

View File

@ -6,14 +6,10 @@
namespace DDraw
{
class Surface;
namespace DirectDrawClipper
{
HRGN getClipRgn(CompatRef<IDirectDrawClipper> clipper);
void setClipper(Surface& surface, IDirectDrawClipper* clipper);
HRESULT setClipRgn(CompatRef<IDirectDrawClipper> clipper, HRGN rgn);
void update();
void hookVtable(const IDirectDrawClipperVtbl& vtable);
}

View File

@ -73,7 +73,6 @@ namespace
SET_COMPAT_METHOD(QueryInterface);
SET_COMPAT_METHOD(ReleaseDC);
SET_COMPAT_METHOD(Restore);
SET_COMPAT_METHOD(SetClipper);
SET_COMPAT_METHOD(SetPalette);
SET_COMPAT_METHOD(Unlock);

View File

@ -4,7 +4,6 @@
#include <Common/CompatPtr.h>
#include <Config/Settings/AlignSysMemSurfaces.h>
#include <DDraw/DirectDrawClipper.h>
#include <DDraw/DirectDrawSurface.h>
#include <DDraw/Surfaces/Surface.h>
#include <DDraw/Surfaces/SurfaceImpl.h>
@ -60,7 +59,6 @@ namespace DDraw
Surface::~Surface()
{
DirectDrawClipper::setClipper(*this, nullptr);
g_surfaces.erase(this);
}

View File

@ -7,14 +7,15 @@
#include <Common/CompatPtr.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/Resource.h>
#include <DDraw/DirectDrawClipper.h>
#include <DDraw/DirectDrawSurface.h>
#include <DDraw/LogUsedResourceFormat.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <DDraw/Surfaces/Surface.h>
#include <DDraw/Surfaces/SurfaceImpl.h>
#include <Direct3d/Direct3d.h>
#include <Dll/Dll.h>
#include <Gdi/Region.h>
#include <Gdi/WinProc.h>
namespace
@ -23,6 +24,87 @@ namespace
UINT g_bltSrcSubResourceIndex = 0;
RECT g_bltSrcRect = {};
IDirectDrawClipper* createClipper()
{
IDirectDrawClipper* clipper = nullptr;
CALL_ORIG_PROC(DirectDrawCreateClipper)(0, &clipper, nullptr);
return clipper;
}
IDirectDrawClipper* getFixedClipper(CompatWeakPtr<IDirectDrawClipper> clipper)
{
HWND hwnd = nullptr;
clipper->GetHWnd(clipper, &hwnd);
if (!hwnd)
{
return nullptr;
}
static CompatWeakPtr<IDirectDrawClipper> fixedClipper(createClipper());
if (!fixedClipper)
{
return nullptr;
}
HDC dc = GetDCEx(hwnd, nullptr, DCX_CACHE | DCX_USESTYLE);
Gdi::Region rgn;
GetRandomRgn(dc, rgn, SYSRGN);
CALL_ORIG_FUNC(ReleaseDC)(hwnd, dc);
auto& mi = DDraw::PrimarySurface::getMonitorInfo();
if (0 != mi.rcEmulated.left || 0 != mi.rcEmulated.top)
{
rgn.offset(-mi.rcEmulated.left, -mi.rcEmulated.top);
}
DWORD rgnSize = GetRegionData(rgn, 0, nullptr);
std::vector<unsigned char> rgnDataBuf(std::max<DWORD>(rgnSize, sizeof(RGNDATA) + sizeof(RECT)));
RGNDATA* rgnData = reinterpret_cast<RGNDATA*>(rgnDataBuf.data());
GetRegionData(rgn, rgnSize, rgnData);
if (0 == rgnData->rdh.nCount)
{
rgnData->rdh.nCount = 1;
}
fixedClipper->SetClipList(fixedClipper, rgnData, 0);
return fixedClipper;
}
template <typename TSurface>
class ClipperFix
{
public:
ClipperFix(TSurface* surface) : m_surface(surface)
{
HRESULT result = getOrigVtable(surface).GetClipper(surface, &m_clipper.getRef());
if (FAILED(result))
{
return;
}
auto fixedClipper = getFixedClipper(m_clipper);
if (!fixedClipper)
{
m_clipper.release();
return;
}
getOrigVtable(surface).SetClipper(surface, fixedClipper);
}
~ClipperFix()
{
if (m_clipper)
{
getOrigVtable(m_surface).SetClipper(m_surface, m_clipper);
}
}
private:
TSurface* m_surface;
CompatPtr<IDirectDrawClipper> m_clipper;
};
template <typename TSurface>
typename DDraw::Types<TSurface>::TDdsCaps getCaps(TSurface* This)
{
@ -133,7 +215,7 @@ namespace DDraw
{
Gdi::WinProc::startFrame();
RealPrimarySurface::waitForFlip(m_data->getDDS());
DirectDrawClipper::update();
ClipperFix fix(This);
return blt(This, lpDDSrcSurface, lpSrcRect, [=](TSurface* This, TSurface* lpDDSrcSurface, LPRECT lpSrcRect)
{ return getOrigVtable(This).Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); });
}
@ -287,17 +369,6 @@ namespace DDraw
return result;
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::SetClipper(TSurface* This, LPDIRECTDRAWCLIPPER lpDDClipper)
{
HRESULT result = getOrigVtable(This).SetClipper(This, lpDDClipper);
if (SUCCEEDED(result))
{
DDraw::DirectDrawClipper::setClipper(*m_data, lpDDClipper);
}
return result;
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::SetPalette(TSurface* This, LPDIRECTDRAWPALETTE lpDDPalette)
{

View File

@ -41,7 +41,6 @@ namespace DDraw
virtual HRESULT QueryInterface(TSurface* This, REFIID riid, LPVOID* obp);
virtual HRESULT ReleaseDC(TSurface* This, HDC hDC);
virtual HRESULT Restore(TSurface* This);
virtual HRESULT SetClipper(TSurface* This, LPDIRECTDRAWCLIPPER lpDDClipper);
virtual HRESULT SetPalette(TSurface* This, LPDIRECTDRAWPALETTE lpDDPalette);
virtual HRESULT SetSurfaceDesc(TSurface* This, TSurfaceDesc* lpddsd, DWORD dwFlags);
virtual HRESULT Unlock(TSurface* This, TUnlockParam lpRect);

View File

@ -119,9 +119,4 @@ namespace Gdi
SetClassLongPtr(hwnd, GCLP_WNDPROC, reinterpret_cast<LONG>(oldWndProc));
DestroyWindow(hwnd);
}
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc)
{
WinProc::watchWindowPosChanges(notifyFunc);
}
}

View File

@ -8,8 +8,6 @@ namespace Gdi
{
const ATOM MENU_ATOM = 0x8000;
typedef void(*WindowPosChangeNotifyFunc)();
void checkDesktopComposition();
void dllThreadDetach();
void installHooks();
@ -17,5 +15,4 @@ namespace Gdi
void redraw(HRGN rgn);
void redrawWindow(HWND hwnd, HRGN rgn);
void unhookWndProc(LPCSTR className, WNDPROC oldWndProc);
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc);
};

View File

@ -55,7 +55,6 @@ namespace
decltype(&DwmSetIconicThumbnail) g_dwmSetIconicThumbnail = nullptr;
std::map<HMENU, UINT> g_menuMaxHeight;
std::set<Gdi::WindowPosChangeNotifyFunc> g_windowPosChangeNotifyFuncs;
Compat::SrwLock g_windowProcSrwLock;
std::map<HWND, WindowProc> g_windowProc;
@ -580,11 +579,6 @@ namespace
void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp)
{
for (auto notifyFunc : g_windowPosChangeNotifyFuncs)
{
notifyFunc();
}
if (Gdi::Window::isTopLevelWindow(hwnd))
{
DDraw::RealPrimarySurface::setPresentationWindowTopmost();
@ -1069,10 +1063,5 @@ namespace Gdi
qpcNow = Time::queryPerformanceCounter();
}
}
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc)
{
g_windowPosChangeNotifyFuncs.insert(notifyFunc);
}
}
}

View File

@ -12,6 +12,5 @@ namespace Gdi
void onCreateWindow(HWND hwnd);
void startFrame();
void updatePresentationWindowText(HWND owner);
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc);
}
}