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

Fixed various multi-monitor display issues

This commit is contained in:
narzoul 2018-12-09 14:23:36 +01:00
parent 727be63db1
commit 291a9c2f9a
18 changed files with 172 additions and 51 deletions

View File

@ -81,9 +81,9 @@ namespace D3dDdi
return g_ddiVersion;
}
void installHooks()
void installHooks(HMODULE origDDrawModule)
{
KernelModeThunks::installHooks();
KernelModeThunks::installHooks(origDDrawModule);
}
void onUmdFileNameQueried(const std::wstring& umdFileName)

View File

@ -5,7 +5,7 @@
namespace D3dDdi
{
UINT getDdiVersion();
void installHooks();
void installHooks(HMODULE origDDrawModule);
void onUmdFileNameQueried(const std::wstring& umdFileName);
void uninstallHooks();
}

View File

@ -1,5 +1,6 @@
#include <atomic>
#include <map>
#include <string>
#include <d3d.h>
#include <d3dumddi.h>
@ -34,6 +35,7 @@ namespace
std::map<D3DKMT_HANDLE, ContextInfo> g_contexts;
AdapterInfo g_gdiAdapterInfo = {};
AdapterInfo g_lastOpenAdapterInfo = {};
std::string g_lastDDrawCreateDcDevice;
UINT g_lastFlipInterval = 0;
UINT g_flipIntervalOverride = 0;
D3DKMT_HANDLE g_lastPresentContext = 0;
@ -108,6 +110,13 @@ namespace
return LOG_RESULT(result);
}
HDC WINAPI ddrawCreateDcA(LPCSTR pwszDriver, LPCSTR pwszDevice, LPCSTR pszPort, const DEVMODEA* pdm)
{
LOG_FUNC("ddrawCreateDCA", pwszDriver, pwszDevice, pszPort, pdm);
g_lastDDrawCreateDcDevice = pwszDevice ? pwszDevice : std::string();
return LOG_RESULT(CALL_ORIG_FUNC(CreateDCA)(pwszDriver, pwszDevice, pszPort, pdm));
}
NTSTATUS APIENTRY destroyContext(const D3DKMT_DESTROYCONTEXT* pData)
{
LOG_FUNC("D3DKMTDestroyContext", pData);
@ -123,18 +132,35 @@ namespace
return LOG_RESULT(result);
}
BOOL CALLBACK findDDrawMonitorRect(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData)
{
MONITORINFOEX mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi);
if (g_lastDDrawCreateDcDevice == mi.szDevice)
{
*reinterpret_cast<RECT*>(dwData) = mi.rcMonitor;
return FALSE;
}
return TRUE;
}
AdapterInfo getAdapterInfo(const D3DKMT_OPENADAPTERFROMHDC& data)
{
AdapterInfo adapterInfo = {};
adapterInfo.adapter = data.hAdapter;
adapterInfo.vidPnSourceId = data.VidPnSourceId;
POINT p = {};
GetDCOrgEx(data.hDc, &p);
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY), &mi);
adapterInfo.monitorRect = mi.rcMonitor;
EnumDisplayMonitors(nullptr, nullptr, findDDrawMonitorRect,
reinterpret_cast<LPARAM>(&adapterInfo.monitorRect));
if (IsRectEmpty(&adapterInfo.monitorRect))
{
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), &mi);
adapterInfo.monitorRect = mi.rcMonitor;
}
return adapterInfo;
}
@ -282,7 +308,7 @@ namespace D3dDdi
{
lastDisplaySettingsUniqueness = currentDisplaySettingsUniqueness;
CompatPtr<IUnknown> ddUnk;
primary->GetDDInterface(primary, reinterpret_cast<void**>(&ddUnk.getRef()));
primary.get()->lpVtbl->GetDDInterface(primary, reinterpret_cast<void**>(&ddUnk.getRef()));
CompatPtr<IDirectDraw7> dd7(ddUnk);
DDDEVICEIDENTIFIER2 di = {};
@ -297,7 +323,7 @@ namespace D3dDdi
return g_qpcLastVerticalBlank;
}
void installHooks()
void installHooks(HMODULE origDDrawModule)
{
HOOK_FUNCTION(gdi32, D3DKMTCloseAdapter, closeAdapter);
HOOK_FUNCTION(gdi32, D3DKMTCreateContext, createContext);
@ -308,6 +334,7 @@ namespace D3dDdi
HOOK_FUNCTION(gdi32, D3DKMTQueryAdapterInfo, queryAdapterInfo);
HOOK_FUNCTION(gdi32, D3DKMTPresent, present);
HOOK_FUNCTION(gdi32, D3DKMTSetQueuedLimit, setQueuedLimit);
Compat::hookIatFunction(origDDrawModule, "gdi32.dll", "CreateDCA", ddrawCreateDcA);
// Functions not available in Windows Vista
Compat::hookFunction("gdi32", "D3DKMTCreateContextVirtual",

View File

@ -13,7 +13,7 @@ namespace D3dDdi
UINT getLastSubmittedFrameCount();
RECT getMonitorRect();
long long getQpcLastVerticalBlank();
void installHooks();
void installHooks(HMODULE origDDrawModule);
void setFlipIntervalOverride(UINT flipInterval);
void waitForVerticalBlank();
}

View File

@ -28,22 +28,6 @@ namespace
namespace DDraw
{
CompatPtr<IDirectDrawSurface7> createCompatibleSurface(DWORD bpp)
{
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS | DDSD_PIXELFORMAT;
desc.dwWidth = 1;
desc.dwHeight = 1;
desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
desc.ddpfPixelFormat = getRgbPixelFormat(bpp);
CompatPtr<IDirectDrawSurface7> surface;
auto dd = DDraw::Repository::getDirectDraw();
dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr);
return surface;
}
template <typename TDirectDraw>
void* getDdObject(TDirectDraw& dd)
{

View File

@ -2,7 +2,6 @@
#include <ddraw.h>
#include "Common/CompatPtr.h"
#include "Common/CompatRef.h"
#include "Common/CompatVtable.h"
#include "DDraw/Visitors/DirectDrawVtblVisitor.h"
@ -10,8 +9,6 @@
namespace DDraw
{
CompatPtr<IDirectDrawSurface7> createCompatibleSurface(DWORD bpp);
template <typename TDirectDraw>
void* getDdObject(TDirectDraw& dd);

View File

@ -2,8 +2,10 @@
#include <vector>
#include "Common/CompatRef.h"
#include "D3dDdi/KernelModeThunks.h"
#include "DDraw/DirectDrawClipper.h"
#include "Gdi/Gdi.h"
#include "Gdi/Region.h"
namespace
{
@ -28,9 +30,16 @@ namespace
void updateWindowClipList(CompatRef<IDirectDrawClipper> clipper, ClipperData& data)
{
HDC dc = GetDC(data.hwnd);
HRGN rgn = CreateRectRgn(0, 0, 0, 0);
Gdi::Region rgn;
GetRandomRgn(dc, rgn, SYSRGN);
CALL_ORIG_FUNC(ReleaseDC)(data.hwnd, dc);
RECT primaryRect = D3dDdi::KernelModeThunks::getMonitorRect();
if (0 != primaryRect.left || 0 != primaryRect.top)
{
rgn.offset(-primaryRect.left, -primaryRect.top);
}
DWORD rgnSize = GetRegionData(rgn, 0, nullptr);
std::vector<unsigned char> rgnData(rgnSize);
GetRegionData(rgn, rgnSize, reinterpret_cast<RGNDATA*>(rgnData.data()));
@ -40,9 +49,6 @@ namespace
{
clipper->SetHWnd(&clipper, 0, data.hwnd);
}
DeleteObject(rgn);
CALL_ORIG_FUNC(ReleaseDC)(data.hwnd, dc);
}
HRESULT STDMETHODCALLTYPE GetHWnd(IDirectDrawClipper* This, HWND* lphWnd)
@ -115,6 +121,24 @@ namespace
namespace DDraw
{
HRGN DirectDrawClipper::getClipRgn(CompatRef<IDirectDrawClipper> clipper)
{
std::vector<unsigned char> rgnData;
DWORD size = 0;
clipper->GetClipList(&clipper, nullptr, nullptr, &size);
rgnData.resize(size);
clipper->GetClipList(&clipper, nullptr, reinterpret_cast<RGNDATA*>(rgnData.data()), &size);
return ExtCreateRegion(nullptr, size, reinterpret_cast<RGNDATA*>(rgnData.data()));
}
HRESULT DirectDrawClipper::setClipRgn(CompatRef<IDirectDrawClipper> clipper, HRGN rgn)
{
std::vector<unsigned char> rgnData;
rgnData.resize(GetRegionData(rgn, 0, nullptr));
GetRegionData(rgn, rgnData.size(), reinterpret_cast<RGNDATA*>(rgnData.data()));
return clipper->SetClipList(&clipper, reinterpret_cast<RGNDATA*>(rgnData.data()), 0);
}
void DirectDrawClipper::setCompatVtable(IDirectDrawClipperVtbl& vtable)
{
vtable.GetHWnd = &GetHWnd;

View File

@ -1,5 +1,6 @@
#pragma once
#include "Common/CompatRef.h"
#include "Common/CompatVtable.h"
#include "DDraw/Visitors/DirectDrawClipperVtblVisitor.h"
@ -8,6 +9,9 @@ namespace DDraw
class DirectDrawClipper : public CompatVtable<IDirectDrawClipperVtbl>
{
public:
static HRGN getClipRgn(CompatRef<IDirectDrawClipper> clipper);
static HRESULT setClipRgn(CompatRef<IDirectDrawClipper> clipper, HRGN rgn);
static void setCompatVtable(IDirectDrawClipperVtbl& vtable);
};
}

View File

@ -12,7 +12,7 @@ namespace
HRESULT STDMETHODCALLTYPE callImpl(TSurface* This, Params... params)
{
DDraw::Surface* surface = This ? DDraw::Surface::getSurface(*This) : nullptr;
if (!surface)
if (!surface || !(surface->getImpl<TSurface>()))
{
return (CompatVtable<Vtable<TSurface>>::s_origVtable.*origMethod)(This, params...);
}

View File

@ -78,6 +78,7 @@ namespace
void bltToWindowViaGdi(Gdi::Region* primaryRegion)
{
std::unique_ptr<HDC__, void(*)(HDC)> virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc);
RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds();
for (auto windowPair : Gdi::Window::getWindows())
{
@ -110,12 +111,14 @@ namespace
}
}
Gdi::GdiAccessGuard gdiAccessGuard(Gdi::ACCESS_READ);
Gdi::GdiAccessGuard gdiAccessGuard(Gdi::ACCESS_READ, !primaryRegion);
HWND presentationWindow = windowPair.second->getPresentationWindow();
HDC dc = GetWindowDC(presentationWindow);
RECT rect = windowPair.second->getWindowRect();
CALL_ORIG_FUNC(BitBlt)(dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
virtualScreenDc.get(), rect.left, rect.top, SRCCOPY);
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);
}
}
@ -138,6 +141,7 @@ namespace
HDC backBufferDc = nullptr;
backBuffer->GetDC(backBuffer, &backBufferDc);
RECT ddrawMonitorRect = D3dDdi::KernelModeThunks::getMonitorRect();
for (auto it = visibleLayeredWindows.rbegin(); it != visibleLayeredWindows.rend(); ++it)
{
@ -145,6 +149,12 @@ namespace
HRGN rgn = Gdi::getVisibleWindowRgn(*it);
RECT wr = {};
GetWindowRect(*it, &wr);
if (0 != ddrawMonitorRect.left || 0 != ddrawMonitorRect.top)
{
OffsetRect(&wr, -ddrawMonitorRect.left, -ddrawMonitorRect.top);
OffsetRgn(rgn, -ddrawMonitorRect.left, -ddrawMonitorRect.top);
}
SelectClipRgn(backBufferDc, rgn);
CALL_ORIG_FUNC(BitBlt)(backBufferDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,

View File

@ -1,12 +1,69 @@
#include "Common/CompatPtr.h"
#include "D3dDdi/KernelModeThunks.h"
#include "DDraw/DirectDrawClipper.h"
#include "DDraw/DirectDrawPalette.h"
#include "DDraw/DirectDrawSurface.h"
#include "DDraw/RealPrimarySurface.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "DDraw/Surfaces/PrimarySurfaceImpl.h"
#include "Dll/Procs.h"
#include "Gdi/Gdi.h"
#include "Gdi/Region.h"
#include "Gdi/VirtualScreen.h"
namespace
{
template <typename TSurface>
void bltToGdi(TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect,
DWORD dwFlags, LPDDBLTFX lpDDBltFx)
{
if (!lpDestRect)
{
return;
}
CompatPtr<IDirectDrawClipper> clipper;
CompatVtable<Vtable<TSurface>>::s_origVtable.GetClipper(This, &clipper.getRef());
if (!clipper)
{
return;
}
Gdi::Region clipRgn(DDraw::DirectDrawClipper::getClipRgn(*clipper));
RECT monitorRect = D3dDdi::KernelModeThunks::getMonitorRect();
RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds();
clipRgn.offset(monitorRect.left, monitorRect.top);
clipRgn &= virtualScreenBounds;
clipRgn -= monitorRect;
if (clipRgn.isEmpty())
{
return;
}
auto gdiSurface(Gdi::VirtualScreen::createSurface(virtualScreenBounds));
if (!gdiSurface)
{
return;
}
CompatPtr<IDirectDrawClipper> gdiClipper;
CALL_ORIG_PROC(DirectDrawCreateClipper, 0, &gdiClipper.getRef(), nullptr);
if (!gdiClipper)
{
return;
}
RECT dstRect = *lpDestRect;
OffsetRect(&dstRect, monitorRect.left - virtualScreenBounds.left, monitorRect.top - virtualScreenBounds.top);
clipRgn.offset(-virtualScreenBounds.left, -virtualScreenBounds.top);
DDraw::DirectDrawClipper::setClipRgn(*gdiClipper, clipRgn);
auto srcSurface(CompatPtr<IDirectDrawSurface7>::from(lpDDSrcSurface));
gdiSurface->SetClipper(gdiSurface, gdiClipper);
gdiSurface->Blt(gdiSurface, &dstRect, srcSurface, lpSrcRect, dwFlags, lpDDBltFx);
gdiSurface->SetClipper(gdiSurface, nullptr);
}
void restorePrimaryCaps(DWORD& caps)
{
caps &= ~DDSCAPS_OFFSCREENPLAIN;
@ -41,6 +98,7 @@ namespace DDraw
HRESULT result = m_impl.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx);
if (SUCCEEDED(result))
{
bltToGdi(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx);
RealPrimarySurface::update();
}
return result;

View File

@ -122,7 +122,7 @@ namespace DDraw
privateData.get(), sizeof(privateData.get()), DDSPD_IUNKNOWNPOINTER)))
{
CompatPtr<IUnknown> dd;
dds->GetDDInterface(&dds, reinterpret_cast<void**>(&dd.getRef()));
dds.get().lpVtbl->GetDDInterface(&dds, reinterpret_cast<void**>(&dd.getRef()));
privateData->createImpl();
privateData->m_impl->m_data = privateData.get();

View File

@ -39,7 +39,7 @@ namespace
Compat::Log() << "Installing registry hooks";
Win32::Registry::installHooks();
Compat::Log() << "Installing Direct3D driver hooks";
D3dDdi::installHooks();
D3dDdi::installHooks(g_origDDrawModule);
Compat::Log() << "Installing display mode hooks";
Win32::DisplayMode::installHooks(g_origDDrawModule);
Gdi::VirtualScreen::init();

View File

@ -111,13 +111,15 @@ namespace
}
}
void setClippingRegion(const CompatDc& compatDc, std::shared_ptr<Gdi::Window> rootWindow, const POINT& origin)
void setClippingRegion(const CompatDc& compatDc, std::shared_ptr<Gdi::Window> rootWindow,
const POINT& origin, const RECT& virtualScreenBounds)
{
if (rootWindow)
{
Gdi::Region sysRgn;
CALL_ORIG_FUNC(GetRandomRgn)(compatDc.origDc, sysRgn, SYSRGN);
sysRgn &= rootWindow->getVisibleRegion();
OffsetRgn(sysRgn, -virtualScreenBounds.left, -virtualScreenBounds.top);
SelectClipRgn(compatDc.dc, sysRgn);
}
else
@ -206,15 +208,18 @@ namespace Gdi
POINT origin = {};
GetDCOrgEx(origDc, &origin);
RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds();
origin.x -= virtualScreenBounds.left;
origin.y -= virtualScreenBounds.top;
if (wnd && GetDesktopWindow() != wnd)
{
origin.x -= virtualScreenBounds.left;
origin.y -= virtualScreenBounds.top;
}
compatDc.refCount = 1;
compatDc.origDc = origDc;
compatDc.threadId = GetCurrentThreadId();
compatDc.savedState = useMetaRgn ? SaveDC(compatDc.dc) : 0;
copyDcAttributes(compatDc, origDc, origin);
setClippingRegion(compatDc, rootWindow, origin);
setClippingRegion(compatDc, rootWindow, origin, virtualScreenBounds);
g_origDcToCompatDc.insert(CompatDcMap::value_type(origDc, compatDc));

View File

@ -14,6 +14,11 @@ namespace
namespace Gdi
{
Region::Region(HRGN rgn)
: m_region(rgn)
{
}
Region::Region(const RECT& rect)
: m_region(CreateRectRgnIndirect(&rect))
{

View File

@ -9,6 +9,7 @@ namespace Gdi
class Region
{
public:
Region(HRGN rgn);
Region(const RECT& rect = RECT{ 0, 0, 0, 0 });
~Region();
Region(const Region& other);

View File

@ -1,7 +1,9 @@
#include "Common/Hook.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "Gdi/Gdi.h"
#include "Gdi/Region.h"
#include "Gdi/TitleBar.h"
#include "Gdi/VirtualScreen.h"
namespace
{
@ -101,11 +103,11 @@ namespace Gdi
flags |= DC_GRADIENT;
}
RECT virtualScreenBounds = VirtualScreen::getBounds();
RECT clipRect = m_tbi.rcTitleBar;
OffsetRect(&clipRect, m_windowRect.left, m_windowRect.top);
HRGN clipRgn = CreateRectRgnIndirect(&clipRect);
OffsetRect(&clipRect, m_windowRect.left - virtualScreenBounds.left, m_windowRect.top - virtualScreenBounds.top);
Region clipRgn(clipRect);
SelectClipRgn(m_compatDc, clipRgn);
DeleteObject(clipRgn);
RECT textRect = m_tbi.rcTitleBar;
if (m_hasIcon)

View File

@ -1,8 +1,8 @@
#include <set>
#include "DDraw/DirectDraw.h"
#include "DDraw/Repository.h"
#include "DDraw/ScopedThreadLock.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "Gdi/Gdi.h"
#include "Gdi/Region.h"
#include "Gdi/VirtualScreen.h"
@ -134,8 +134,12 @@ namespace Gdi
(rect.top - g_bounds.top) * g_pitch +
(rect.left - g_bounds.left) * g_bpp / 8;
auto primary(DDraw::PrimarySurface::getPrimary());
CompatPtr<IUnknown> ddUnk;
primary.get()->lpVtbl->GetDDInterface(primary, reinterpret_cast<void**>(&ddUnk.getRef()));
CompatPtr<IDirectDraw7> dd(ddUnk);
CompatPtr<IDirectDrawSurface7> surface;
auto dd(DDraw::Repository::getDirectDraw());
dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr);
return surface;
}