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

Added DisplayResolution setting

This commit is contained in:
narzoul 2021-06-12 20:49:36 +02:00
parent 62983b19fe
commit 647a4bfcff
33 changed files with 1059 additions and 305 deletions

View File

@ -21,3 +21,13 @@ inline auto toTuple(const LUID& luid)
{
return std::make_tuple(luid.LowPart, luid.HighPart);
}
inline auto toTuple(const POINT& pt)
{
return std::make_tuple(pt.x, pt.y);
}
inline auto toTuple(const SIZE& size)
{
return std::make_tuple(size.cx, size.cy);
}

View File

@ -3,5 +3,6 @@
namespace Config
{
Settings::CpuAffinity cpuAffinity;
Settings::DisplayResolution displayResolution;
Settings::ThreadPriorityBoost threadPriorityBoost;
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <Config/Settings/CpuAffinity.h>
#include <Config/Settings/DisplayResolution.h>
#include <Config/Settings/ThreadPriorityBoost.h>
namespace Config
@ -10,5 +11,6 @@ namespace Config
const unsigned maxPaletteUpdatesPerMs = 5;
extern Settings::CpuAffinity cpuAffinity;
extern Settings::DisplayResolution displayResolution;
extern Settings::ThreadPriorityBoost threadPriorityBoost;
}

View File

@ -3,33 +3,26 @@
#include <Config/Parser.h>
#include <Config/EnumSetting.h>
namespace
{
std::map<std::string, unsigned> createMapping(const std::vector<std::string>& enumNames)
{
std::map<std::string, unsigned> mapping;
unsigned i = 0;
for (const auto& name : enumNames)
{
mapping[name] = i;
++i;
}
return mapping;
}
}
namespace Config
{
EnumSetting::EnumSetting(const std::string& name, unsigned default, const std::vector<std::string>& enumNames)
: Setting(name)
, m_default(default)
, m_value(default)
, m_enumNames(enumNames)
: MappedSetting(name, default, createMapping(enumNames))
{
}
std::string EnumSetting::getValueStr() const
{
return m_enumNames[m_value];
}
void EnumSetting::resetValue()
{
m_value = m_default;
}
void EnumSetting::setValue(const std::string& value)
{
auto it = std::find(m_enumNames.begin(), m_enumNames.end(), value);
if (it == m_enumNames.end())
{
throw ParsingError("invalid value: '" + value + "'");
}
m_value = it - m_enumNames.begin();
}
}

View File

@ -2,25 +2,13 @@
#include <vector>
#include <Config/Setting.h>
#include <Config/MappedSetting.h>
namespace Config
{
class EnumSetting : public Setting
class EnumSetting : public MappedSetting<unsigned>
{
public:
unsigned get() const { return m_value; }
protected:
EnumSetting(const std::string& name, unsigned default, const std::vector<std::string>& enumNames);
private:
std::string getValueStr() const override;
void resetValue() override;
void setValue(const std::string& value) override;
unsigned m_default;
unsigned m_value;
const std::vector<std::string> m_enumNames;
};
}

View File

@ -11,11 +11,11 @@ namespace Config
protected:
ListSetting(const std::string& name, const std::string& default);
private:
void resetValue() override;
void setValue(const std::string& value) override;
virtual void setValues(const std::vector<std::string>& values) = 0;
private:
std::string m_default;
};
}

View File

@ -0,0 +1,56 @@
#pragma once
#include <map>
#include <Config/Parser.h>
#include <Config/Setting.h>
namespace Config
{
template <typename Value>
class MappedSetting : public Setting
{
public:
Value get() const { return m_value; }
protected:
MappedSetting(const std::string& name, Value default, const std::map<std::string, Value>& valueMapping)
: Setting(name)
, m_default(default)
, m_value(default)
, m_valueMapping(valueMapping)
{
}
std::string getValueStr() const override
{
for (const auto& pair : m_valueMapping)
{
if (pair.second == m_value)
{
return pair.first;
}
}
throw ParsingError("MappedSetting::getValueStr(): value not found in mapping");
}
void resetValue() override
{
m_value = m_default;
}
void setValue(const std::string& value) override
{
auto it = m_valueMapping.find(value);
if (it == m_valueMapping.end())
{
throw ParsingError("invalid value: '" + value + "'");
}
m_value = it->second;
}
Value m_default;
Value m_value;
const std::map<std::string, Value> m_valueMapping;
};
}

View File

@ -142,6 +142,30 @@ namespace Config
}
}
SIZE parseResolution(const std::string& value)
{
try
{
auto pos = value.find('x');
if (pos != std::string::npos)
{
SIZE resolution = {};
resolution.cx = parseUnsigned(value.substr(0, pos));
resolution.cy = parseUnsigned(value.substr(pos + 1));
if (0 != resolution.cx && resolution.cx <= 65535 &&
0 != resolution.cy && resolution.cy <= 65535)
{
return resolution;
}
}
}
catch (ParsingError&)
{
}
throw ParsingError("invalid resolution: '" + value + "'");
}
unsigned parseUnsigned(const std::string& value)
{
if (value.empty() || std::string::npos != value.find_first_not_of("0123456789"))

View File

@ -4,6 +4,8 @@
#include <stdexcept>
#include <string>
#include <Windows.h>
namespace Config
{
class ParsingError : public std::runtime_error
@ -17,6 +19,7 @@ namespace Config
namespace Parser
{
void loadAllConfigFiles(const std::filesystem::path& processPath);
SIZE parseResolution(const std::string& value);
unsigned parseUnsigned(const std::string& value);
void registerSetting(Setting& setting);
std::string trim(const std::string& str);

View File

@ -21,11 +21,12 @@ namespace Config
void reset();
void set(const std::string& value, const std::string& source);
private:
protected:
virtual std::string getValueStr() const = 0;
virtual void resetValue() = 0;
virtual void setValue(const std::string& value) = 0;
private:
std::string m_name;
std::string m_source;
};

View File

@ -0,0 +1,39 @@
#include <Config/Settings/DisplayResolution.h>
namespace Config
{
namespace Settings
{
DisplayResolution::DisplayResolution()
: MappedSetting("DisplayResolution", DESKTOP, { {"app", APP}, {"desktop", DESKTOP} })
{
}
std::string DisplayResolution::getValueStr() const
{
try
{
return MappedSetting::getValueStr();
}
catch (const ParsingError&)
{
return std::to_string(m_value.cx) + 'x' + std::to_string(m_value.cy);
}
}
void DisplayResolution::setValue(const std::string& value)
{
try
{
MappedSetting::setValue(value);
}
catch (const ParsingError&)
{
m_value = Parser::parseResolution(value);
}
}
const SIZE DisplayResolution::APP = { 0, 0 };
const SIZE DisplayResolution::DESKTOP = { 1, 0 };
}
}

View File

@ -0,0 +1,25 @@
#pragma once
#include <Windows.h>
#include <Common/Comparison.h>
#include <Config/MappedSetting.h>
namespace Config
{
namespace Settings
{
class DisplayResolution : public MappedSetting<SIZE>
{
public:
static const SIZE APP;
static const SIZE DESKTOP;
DisplayResolution();
protected:
std::string getValueStr() const override;
void setValue(const std::string& value) override;
};
}
}

View File

@ -23,7 +23,7 @@ namespace
D3dDdi::KernelModeThunks::AdapterInfo g_gdiAdapterInfo = {};
D3dDdi::KernelModeThunks::AdapterInfo g_lastOpenAdapterInfo = {};
Compat::SrwLock g_lastOpenAdapterInfoSrwLock;
std::string g_lastDDrawCreateDcDevice;
std::string g_lastDDrawDeviceName;
std::atomic<long long> g_qpcLastVsync = 0;
UINT g_vsyncCounter = 0;
@ -104,49 +104,39 @@ namespace
LOG_FUNC("ddrawCreateDCA", pwszDriver, pwszDevice, pszPort, pdm);
if (pwszDevice)
{
g_lastDDrawCreateDcDevice = pwszDevice;
g_lastDDrawDeviceName = pwszDevice;
}
else
{
MONITORINFOEXA mi = {};
mi.cbSize = sizeof(mi);
CALL_ORIG_FUNC(GetMonitorInfoA)(MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), &mi);
g_lastDDrawCreateDcDevice = mi.szDevice;
g_lastDDrawDeviceName = mi.szDevice;
}
return LOG_RESULT(CreateDCA(pwszDriver, pwszDevice, pszPort, pdm));
}
BOOL CALLBACK findDDrawMonitorRect(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData)
BOOL CALLBACK findMonitorInfo(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData)
{
MONITORINFOEX mi = {};
MONITORINFOEXW mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi);
if (g_lastDDrawCreateDcDevice == mi.szDevice)
CALL_ORIG_FUNC(GetMonitorInfoW)(hMonitor, &mi);
if (0 == wcscmp(reinterpret_cast<MONITORINFOEXW*>(dwData)->szDevice, mi.szDevice))
{
*reinterpret_cast<RECT*>(dwData) = mi.rcMonitor;
*reinterpret_cast<MONITORINFOEXW*>(dwData) = mi;
return FALSE;
}
return TRUE;
}
D3dDdi::KernelModeThunks::AdapterInfo getAdapterInfo(const D3DKMT_OPENADAPTERFROMHDC& data)
D3dDdi::KernelModeThunks::AdapterInfo getAdapterInfo(const std::string& deviceName, const D3DKMT_OPENADAPTERFROMHDC& data)
{
D3dDdi::KernelModeThunks::AdapterInfo adapterInfo = {};
adapterInfo.adapter = data.hAdapter;
adapterInfo.vidPnSourceId = data.VidPnSourceId;
adapterInfo.luid = data.AdapterLuid;
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;
}
wcscpy_s(adapterInfo.monitorInfo.szDevice, std::wstring(deviceName.begin(), deviceName.end()).c_str());
EnumDisplayMonitors(nullptr, nullptr, findMonitorInfo, reinterpret_cast<LPARAM>(&adapterInfo.monitorInfo));
return adapterInfo;
}
@ -157,7 +147,7 @@ namespace
if (SUCCEEDED(result))
{
Compat::ScopedSrwLockExclusive lock(g_lastOpenAdapterInfoSrwLock);
g_lastOpenAdapterInfo = getAdapterInfo(*pData);
g_lastOpenAdapterInfo = getAdapterInfo(g_lastDDrawDeviceName, *pData);
}
return LOG_RESULT(result);
}
@ -228,7 +218,7 @@ namespace
data.hDc = CreateDC(mi.szDevice, mi.szDevice, nullptr, nullptr);
if (SUCCEEDED(D3DKMTOpenAdapterFromHdc(&data)))
{
g_gdiAdapterInfo = getAdapterInfo(data);
g_gdiAdapterInfo = getAdapterInfo(mi.szDevice, data);
}
DeleteDC(data.hDc);
@ -295,30 +285,6 @@ namespace D3dDdi
return g_lastOpenAdapterInfo;
}
RECT getMonitorRect()
{
auto primary(DDraw::PrimarySurface::getPrimary());
if (!primary)
{
return {};
}
static auto lastDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness() - 1;
const auto currentDisplaySettingsUniqueness = Win32::DisplayMode::queryDisplaySettingsUniqueness();
if (currentDisplaySettingsUniqueness != lastDisplaySettingsUniqueness)
{
lastDisplaySettingsUniqueness = currentDisplaySettingsUniqueness;
CompatPtr<IUnknown> ddUnk;
primary->GetDDInterface(primary, reinterpret_cast<void**>(&ddUnk.getRef()));
CompatPtr<IDirectDraw7> dd7(ddUnk);
DDDEVICEIDENTIFIER2 di = {};
dd7.get()->lpVtbl->GetDeviceIdentifier(dd7, &di, 0);
}
return getLastOpenAdapterInfo().monitorRect;
}
long long getQpcLastVsync()
{
return g_qpcLastVsync;

View File

@ -13,12 +13,11 @@ namespace D3dDdi
UINT adapter;
UINT vidPnSourceId;
LUID luid;
RECT monitorRect;
MONITORINFOEXW monitorInfo;
};
AdapterInfo getAdapterInfo(CompatRef<IDirectDraw7> dd);
AdapterInfo getLastOpenAdapterInfo();
RECT getMonitorRect();
long long getQpcLastVsync();
UINT getVsyncCounter();
void installHooks();

View File

@ -9,46 +9,49 @@
#include <D3dDdi/KernelModeThunks.h>
#include <D3dDdi/Log/DeviceFuncsLog.h>
#include <D3dDdi/Resource.h>
#include <D3dDdi/SurfaceRepository.h>
#include <DDraw/Blitter.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/Palette.h>
#include <Gdi/VirtualScreen.h>
namespace
{
D3DDDI_RESOURCEFLAGS getResourceTypeFlags();
void splitToTiles(D3DDDIARG_CREATERESOURCE& data, const UINT tileWidth, const UINT tileHeight);
const UINT g_resourceTypeFlags = getResourceTypeFlags().Value;
RECT g_presentationRect = {};
RECT calculatePresentationRect()
{
const RECT srcRect = DDraw::PrimarySurface::getMonitorRect();
const RECT dstRect = DDraw::RealPrimarySurface::getMonitorRect();
const int srcWidth = srcRect.right - srcRect.left;
const int srcHeight = srcRect.bottom - srcRect.top;
const int dstWidth = dstRect.right - dstRect.left;
const int dstHeight = dstRect.bottom - dstRect.top;
RECT rect = { 0, 0, dstWidth, dstHeight };
if (dstWidth * srcHeight > dstHeight * srcWidth)
{
rect.right = dstHeight * srcWidth / srcHeight;
}
else
{
rect.bottom = dstWidth * srcHeight / srcWidth;
}
OffsetRect(&rect, (dstWidth - rect.right) / 2, (dstHeight - rect.bottom) / 2);
return rect;
}
LONG divCeil(LONG n, LONG d)
{
return (n + d - 1) / d;
}
void fixResourceData(D3dDdi::Device& device, D3DDDIARG_CREATERESOURCE& data)
{
if (data.Flags.Primary)
{
data.Format = D3DDDIFMT_X8R8G8B8;
}
const bool isOffScreenPlain = 0 == (data.Flags.Value & g_resourceTypeFlags);
if (D3DDDIPOOL_SYSTEMMEM == data.Pool &&
(isOffScreenPlain || data.Flags.Texture) &&
1 == data.SurfCount &&
0 == data.pSurfList[0].Depth &&
0 != D3dDdi::getFormatInfo(data.Format).bytesPerPixel)
{
const auto& caps = device.getAdapter().getD3dExtendedCaps();
const auto& surfaceInfo = data.pSurfList[0];
if (0 != caps.dwMaxTextureWidth && surfaceInfo.Width > caps.dwMaxTextureWidth ||
0 != caps.dwMaxTextureHeight && surfaceInfo.Height > caps.dwMaxTextureHeight)
{
splitToTiles(data, caps.dwMaxTextureWidth, caps.dwMaxTextureHeight);
}
}
}
D3DDDI_RESOURCEFLAGS getResourceTypeFlags()
{
D3DDDI_RESOURCEFLAGS flags = {};
@ -77,32 +80,6 @@ namespace
{
HeapFree(GetProcessHeap(), 0, p);
}
void splitToTiles(D3DDDIARG_CREATERESOURCE& data, const UINT tileWidth, const UINT tileHeight)
{
static std::vector<D3DDDI_SURFACEINFO> tiles;
tiles.clear();
const UINT bytesPerPixel = D3dDdi::getFormatInfo(data.Format).bytesPerPixel;
for (UINT y = 0; y < data.pSurfList[0].Height; y += tileHeight)
{
for (UINT x = 0; x < data.pSurfList[0].Width; x += tileWidth)
{
D3DDDI_SURFACEINFO tile = {};
tile.Width = min(data.pSurfList[0].Width - x, tileWidth);
tile.Height = min(data.pSurfList[0].Height - y, tileHeight);
tile.pSysMem = static_cast<const unsigned char*>(data.pSurfList[0].pSysMem) +
y * data.pSurfList[0].SysMemPitch + x * bytesPerPixel;
tile.SysMemPitch = data.pSurfList[0].SysMemPitch;
tiles.push_back(tile);
}
}
data.SurfCount = tiles.size();
data.pSurfList = tiles.data();
data.Flags.Texture = 0;
}
}
namespace D3dDdi
@ -133,7 +110,12 @@ namespace D3dDdi
throw HResultException(E_FAIL);
}
fixResourceData(device, reinterpret_cast<D3DDDIARG_CREATERESOURCE&>(m_fixedData));
if (m_origData.Flags.Primary)
{
g_presentationRect = calculatePresentationRect();
}
fixResourceData();
m_formatInfo = getFormatInfo(m_fixedData.Format);
HRESULT result = m_device.createPrivateResource(m_fixedData);
@ -327,7 +309,7 @@ namespace D3dDdi
void Resource::createGdiLockResource()
{
auto gdiSurfaceDesc(Gdi::VirtualScreen::getSurfaceDesc(D3dDdi::KernelModeThunks::getMonitorRect()));
auto gdiSurfaceDesc(Gdi::VirtualScreen::getSurfaceDesc(DDraw::RealPrimarySurface::getMonitorRect()));
if (!gdiSurfaceDesc.lpSurface)
{
return;
@ -431,6 +413,39 @@ namespace D3dDdi
#endif
}
void Resource::fixResourceData()
{
if (m_fixedData.Flags.Primary)
{
RECT r = DDraw::RealPrimarySurface::getMonitorRect();
if (!IsRectEmpty(&r))
{
for (auto& surface : m_fixedData.surfaceData)
{
surface.Width = r.right - r.left;
surface.Height = r.bottom - r.top;
}
}
m_fixedData.Format = D3DDDIFMT_X8R8G8B8;
}
const bool isOffScreenPlain = 0 == (m_fixedData.Flags.Value & g_resourceTypeFlags);
if (D3DDDIPOOL_SYSTEMMEM == m_fixedData.Pool &&
(isOffScreenPlain || m_fixedData.Flags.Texture) &&
1 == m_fixedData.SurfCount &&
0 == m_fixedData.pSurfList[0].Depth &&
0 != D3dDdi::getFormatInfo(m_fixedData.Format).bytesPerPixel)
{
const auto& caps = m_device.getAdapter().getD3dExtendedCaps();
const auto& surfaceInfo = m_fixedData.pSurfList[0];
if (0 != caps.dwMaxTextureWidth && surfaceInfo.Width > caps.dwMaxTextureWidth ||
0 != caps.dwMaxTextureHeight && surfaceInfo.Height > caps.dwMaxTextureHeight)
{
splitToTiles(caps.dwMaxTextureWidth, caps.dwMaxTextureHeight);
}
}
}
void* Resource::getLockPtr(UINT subResourceIndex)
{
return m_lockData.empty() ? nullptr : m_lockData[subResourceIndex].data;
@ -496,16 +511,24 @@ namespace D3dDdi
}
}
HRESULT Resource::presentationBlt(const D3DDDIARG_BLT& data, Resource& srcResource)
HRESULT Resource::presentationBlt(D3DDDIARG_BLT data, Resource& srcResource)
{
if (srcResource.m_lockResource &&
srcResource.m_lockData[data.SrcSubResourceIndex].isSysMemUpToDate)
srcResource.m_lockData[0].isSysMemUpToDate)
{
srcResource.copyToVidMem(data.SrcSubResourceIndex);
srcResource.copyToVidMem(0);
}
if (D3DDDIFMT_P8 == srcResource.m_origData.Format)
{
const auto& si = srcResource.m_fixedData.pSurfList[0];
auto palettizedBltRenderTarget(SurfaceRepository::get(m_device.getAdapter()).getPaletteBltRenderTarget(
si.Width, si.Height));
if (!palettizedBltRenderTarget)
{
return E_OUTOFMEMORY;
}
auto entries(Gdi::Palette::getHardwarePalette());
RGBQUAD pal[256] = {};
for (UINT i = 0; i < 256; ++i)
@ -515,10 +538,14 @@ namespace D3dDdi
pal[i].rgbBlue = entries[i].peBlue;
}
m_device.getShaderBlitter().palettizedBlt(*this, data.DstSubResourceIndex, srcResource, pal);
return S_OK;
m_device.getShaderBlitter().palettizedBlt(*palettizedBltRenderTarget, 0, srcResource, pal);
data.hSrcResource = *palettizedBltRenderTarget;
data.SrcSubResourceIndex = 0;
}
data.DstRect = g_presentationRect;
data.Flags.Linear = 1;
data.Flags.Point = 0;
return m_device.getOrigVtable().pfnBlt(m_device, &data);
}
@ -598,6 +625,31 @@ namespace D3dDdi
return LOG_RESULT(S_OK);
}
void Resource::splitToTiles(UINT tileWidth, UINT tileHeight)
{
std::vector<D3DDDI_SURFACEINFO> tiles;
const UINT bytesPerPixel = getFormatInfo(m_fixedData.Format).bytesPerPixel;
for (UINT y = 0; y < m_fixedData.pSurfList[0].Height; y += tileHeight)
{
for (UINT x = 0; x < m_fixedData.pSurfList[0].Width; x += tileWidth)
{
D3DDDI_SURFACEINFO tile = {};
tile.Width = min(m_fixedData.pSurfList[0].Width - x, tileWidth);
tile.Height = min(m_fixedData.pSurfList[0].Height - y, tileHeight);
tile.pSysMem = static_cast<const unsigned char*>(m_fixedData.pSurfList[0].pSysMem) +
y * m_fixedData.pSurfList[0].SysMemPitch + x * bytesPerPixel;
tile.SysMemPitch = m_fixedData.pSurfList[0].SysMemPitch;
tiles.push_back(tile);
}
}
m_fixedData.surfaceData = tiles;
m_fixedData.SurfCount = m_fixedData.surfaceData.size();
m_fixedData.pSurfList = m_fixedData.surfaceData.data();
m_fixedData.Flags.Texture = 0;
}
HRESULT Resource::sysMemPreferredBlt(const D3DDDIARG_BLT& data, Resource& srcResource)
{
if (m_fixedData.Format == srcResource.m_fixedData.Format &&

View File

@ -80,14 +80,16 @@ namespace D3dDdi
void createGdiLockResource();
void createLockResource();
void createSysMemResource(const std::vector<D3DDDI_SURFACEINFO>& surfaceInfo);
void fixResourceData();
bool isOversized() const;
bool isValidRect(UINT subResourceIndex, const RECT& rect);
HRESULT presentationBlt(const D3DDDIARG_BLT& data, Resource& srcResource);
HRESULT presentationBlt(D3DDDIARG_BLT data, Resource& srcResource);
HRESULT splitBlt(D3DDDIARG_BLT& data, UINT& subResourceIndex, RECT& rect, RECT& otherRect);
template <typename Arg>
HRESULT splitLock(Arg& data, HRESULT(APIENTRY *lockFunc)(HANDLE, Arg*));
void splitToTiles(UINT tileWidth, UINT tileHeight);
HRESULT sysMemPreferredBlt(const D3DDDIARG_BLT& data, Resource& srcResource);
Device& m_device;

View File

@ -1,55 +1,15 @@
#include <map>
#include <Common/Comparison.h>
#include <Common/Log.h>
#include <D3dDdi/Adapter.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/Resource.h>
#include <D3dDdi/ShaderBlitter.h>
#include <DDraw/DirectDrawSurface.h>
#include <D3dDdi/SurfaceRepository.h>
#include <Shaders/PaletteLookup.h>
#define CONCAT_(a, b) a##b
#define CONCAT(a, b) CONCAT_(a, b)
#define SCOPED_STATE(state, ...) DeviceState::Scoped##state CONCAT(scopedState, __LINE__)(m_device.getState(), __VA_ARGS__)
namespace
{
std::map<LUID, CompatWeakPtr<IDirectDrawSurface7>> g_paletteTextures;
CompatWeakPtr<IDirectDrawSurface7> getPaletteTexture(CompatWeakPtr<IDirectDraw7> dd, LUID luid)
{
LOG_FUNC("ShaderBlitter::getPaletteTexture", dd.get(), luid);
if (!dd)
{
LOG_ONCE("Failed to create palette texture: no DirectDraw repository available")
return LOG_RESULT(nullptr);
}
auto it = g_paletteTextures.find(luid);
if (it == g_paletteTextures.end())
{
CompatPtr<IDirectDrawSurface7> paletteTexture;
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS;
desc.dwWidth = 256;
desc.dwHeight = 1;
desc.ddpfPixelFormat = DDraw::DirectDraw::getRgbPixelFormat(32);
desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY;
HRESULT result = dd->CreateSurface(dd, &desc, &paletteTexture.getRef(), nullptr);
if (FAILED(result))
{
LOG_ONCE("Failed to create palette texture: " << Compat::hex(result));
return nullptr;
}
it = g_paletteTextures.insert({ luid, paletteTexture.detach() }).first;
}
return LOG_RESULT(it->second.get());
}
}
namespace D3dDdi
{
ShaderBlitter::ShaderBlitter(Device& device)
@ -124,7 +84,8 @@ namespace D3dDdi
dp.PrimitiveType = D3DPT_TRIANGLEFAN;
dp.VStart = 0;
dp.PrimitiveCount = 2;
m_device.getDrawPrimitive().draw(dp, nullptr);
m_device.pfnDrawPrimitive(&dp, nullptr);
m_device.flushPrimitives();
}
HANDLE ShaderBlitter::createPixelShader(const BYTE* code, UINT size)
@ -167,39 +128,33 @@ namespace D3dDdi
LOG_FUNC("ShaderBlitter::palettizedBlt", static_cast<HANDLE>(dstResource), dstSubResourceIndex,
static_cast<HANDLE>(srcResource), Compat::array(reinterpret_cast<void**>(palette), 256));
if (m_paletteTexture && FAILED(m_paletteTexture->IsLost(m_paletteTexture)))
{
g_paletteTextures.erase(m_device.getAdapter().getLuid());
m_paletteTexture->Release(m_paletteTexture);
m_paletteTexture = nullptr;
}
if (!m_paletteTexture)
{
m_paletteTexture = getPaletteTexture(m_device.getAdapter().getRepository(), m_device.getAdapter().getLuid());
if (!m_paletteTexture)
{
return;
}
}
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
m_paletteTexture->Lock(m_paletteTexture, nullptr, &desc, DDLOCK_DISCARDCONTENTS | DDLOCK_WAIT, nullptr);
if (!desc.lpSurface)
auto paletteTexture(SurfaceRepository::get(m_device.getAdapter()).getPaletteTexture());
if (!paletteTexture)
{
return;
}
memcpy(desc.lpSurface, palette, 256 * sizeof(RGBQUAD));
m_paletteTexture->Unlock(m_paletteTexture, nullptr);
D3DDDIARG_LOCK lock = {};
lock.hResource = *paletteTexture;
lock.Flags.Discard = 1;
m_device.getOrigVtable().pfnLock(m_device, &lock);
if (!lock.pSurfData)
{
return;
}
memcpy(lock.pSurfData, palette, 256 * sizeof(RGBQUAD));
D3DDDIARG_UNLOCK unlock = {};
unlock.hResource = *paletteTexture;
m_device.getOrigVtable().pfnUnlock(m_device, &unlock);
const auto& dstSurface = dstResource.getFixedDesc().pSurfList[dstSubResourceIndex];
const auto& srcSurface = srcResource.getFixedDesc().pSurfList[0];
const RECT dstRect = { 0, 0, static_cast<LONG>(dstSurface.Width), static_cast<LONG>(dstSurface.Height) };
const RECT srcRect = { 0, 0, static_cast<LONG>(srcSurface.Width), static_cast<LONG>(srcSurface.Height) };
SCOPED_STATE(Texture, 1, DDraw::DirectDrawSurface::getDriverResourceHandle(*m_paletteTexture), D3DTEXF_POINT);
SCOPED_STATE(Texture, 1, *paletteTexture, D3DTEXF_POINT);
blt(dstResource, dstSubResourceIndex, dstRect, srcResource, srcRect, m_psPaletteLookup, D3DTEXF_POINT);
}
}

View File

@ -1,8 +1,6 @@
#pragma once
#include <ddraw.h>
#include <Common/CompatWeakPtr.h>
#include <Windows.h>
namespace D3dDdi
{
@ -29,7 +27,6 @@ namespace D3dDdi
HANDLE createVertexShaderDecl();
Device& m_device;
CompatWeakPtr<IDirectDrawSurface7> m_paletteTexture;
HANDLE m_psPaletteLookup;
HANDLE m_vertexShaderDecl;
};

View File

@ -0,0 +1,98 @@
#include <map>
#include <Common/Comparison.h>
#include <D3dDdi/Adapter.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/SurfaceRepository.h>
#include <DDraw/DirectDrawSurface.h>
namespace
{
std::map<LUID, D3dDdi::SurfaceRepository> g_repositories;
}
namespace D3dDdi
{
SurfaceRepository::SurfaceRepository(const Adapter& adapter)
: m_adapter(adapter)
{
}
CompatWeakPtr<IDirectDrawSurface7> SurfaceRepository::createSurface(
DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps)
{
auto dd(m_adapter.getRepository());
if (!dd)
{
LOG_ONCE("ERROR: no DirectDraw repository available");
return nullptr;
}
CompatPtr<IDirectDrawSurface7> surface;
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS;
desc.dwWidth = width;
desc.dwHeight = height;
desc.ddpfPixelFormat = pf;
desc.ddsCaps.dwCaps = caps;
HRESULT result = dd->CreateSurface(dd, &desc, &surface.getRef(), nullptr);
if (FAILED(result))
{
LOG_ONCE("ERROR: Failed to create repository surface: " << Compat::hex(result) << " " << desc);
return nullptr;
}
return surface.detach();
}
SurfaceRepository& SurfaceRepository::get(const Adapter& adapter)
{
auto it = g_repositories.find(adapter.getLuid());
if (it != g_repositories.end())
{
return it->second;
}
return g_repositories.emplace(adapter.getLuid(), SurfaceRepository(adapter)).first->second;
}
Resource* SurfaceRepository::getPaletteBltRenderTarget(DWORD width, DWORD height)
{
return getResource(m_paletteBltRenderTarget, width, height, DDraw::DirectDraw::getRgbPixelFormat(32),
DDSCAPS_3DDEVICE | DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY);
}
Resource* SurfaceRepository::getPaletteTexture()
{
return getResource(m_paletteTexture, 256, 1, DDraw::DirectDraw::getRgbPixelFormat(32),
DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY);
}
Resource* SurfaceRepository::getResource(Surface& surface, DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps)
{
if (surface.surface)
{
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
surface.surface->GetSurfaceDesc(surface.surface, &desc);
if (desc.dwWidth != width || desc.dwHeight != height || FAILED(surface.surface->IsLost(surface.surface)))
{
surface.surface->Release(surface.surface);
surface = {};
}
}
if (!surface.surface)
{
surface.surface = createSurface(width, height, pf, caps);
if (surface.surface)
{
surface.resource = D3dDdi::Device::findResource(
DDraw::DirectDrawSurface::getDriverResourceHandle(*surface.surface));
}
}
return surface.resource;
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <ddraw.h>
#include <Common/CompatWeakPtr.h>
namespace D3dDdi
{
class Adapter;
class Resource;
class SurfaceRepository
{
public:
Resource* getPaletteBltRenderTarget(DWORD width, DWORD height);
Resource* getPaletteTexture();
static SurfaceRepository& get(const Adapter& adapter);
private:
struct Surface
{
CompatWeakPtr<IDirectDrawSurface7> surface;
Resource* resource;
};
SurfaceRepository(const Adapter& adapter);
CompatWeakPtr<IDirectDrawSurface7> createSurface(DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps);
Resource* getResource(Surface& surface, DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps);
const Adapter& m_adapter;
Surface m_paletteBltRenderTarget;
Surface m_paletteTexture;
};
}

View File

@ -5,6 +5,7 @@
#include <Common/CompatVtable.h>
#include <D3dDdi/KernelModeThunks.h>
#include <DDraw/DirectDrawClipper.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/ScopedThreadLock.h>
#include <DDraw/Surfaces/Surface.h>
#include <DDraw/Visitors/DirectDrawClipperVtblVisitor.h>
@ -45,7 +46,7 @@ namespace
GetRandomRgn(dc, rgn, SYSRGN);
CALL_ORIG_FUNC(ReleaseDC)(data.hwnd, dc);
RECT primaryRect = D3dDdi::KernelModeThunks::getMonitorRect();
RECT primaryRect = DDraw::RealPrimarySurface::getMonitorRect();
if (0 != primaryRect.left || 0 != primaryRect.top)
{
rgn.offset(-primaryRect.left, -primaryRect.top);

View File

@ -27,6 +27,7 @@ namespace
CompatWeakPtr<IDirectDrawSurface7> g_frontBuffer;
CompatWeakPtr<IDirectDrawClipper> g_clipper;
RECT g_monitorRect = {};
DDSURFACEDESC2 g_surfaceDesc = {};
DDraw::IReleaseNotifier g_releaseNotifier(onRelease);
@ -121,6 +122,7 @@ namespace
g_isFullScreen = false;
g_waitingForPrimaryUnlock = false;
g_surfaceDesc = {};
g_monitorRect = {};
}
void onRestore()
@ -164,8 +166,7 @@ namespace
return;
}
RECT monitorRect = D3dDdi::KernelModeThunks::getMonitorRect();
Gdi::Region excludeRegion(monitorRect);
Gdi::Region excludeRegion(DDraw::PrimarySurface::getMonitorRect());
Gdi::Window::present(excludeRegion);
D3dDdi::KernelModeThunks::setDcPaletteOverride(true);
@ -177,7 +178,7 @@ namespace
auto backBuffer(getBackBuffer());
if (backBuffer)
{
POINT offset = { -monitorRect.left, -monitorRect.top };
POINT offset = { -g_monitorRect.left, -g_monitorRect.top };
Gdi::Window::presentLayered(*backBuffer, offset);
}
}
@ -251,6 +252,7 @@ namespace DDraw
HRESULT RealPrimarySurface::create(CompatRef<DirectDraw> dd)
{
DDraw::ScopedThreadLock lock;
g_monitorRect = D3dDdi::KernelModeThunks::getAdapterInfo(*CompatPtr<IDirectDraw7>::from(&dd)).monitorInfo.rcMonitor;
typename Types<DirectDraw>::TSurfaceDesc desc = {};
desc.dwSize = sizeof(desc);
@ -272,6 +274,7 @@ namespace DDraw
if (FAILED(result))
{
Compat::Log() << "ERROR: Failed to create the real primary surface: " << Compat::hex(result);
g_monitorRect = {};
return result;
}
@ -349,6 +352,11 @@ namespace DDraw
return gammaControl->GetGammaRamp(gammaControl, 0, rampData);
}
RECT RealPrimarySurface::getMonitorRect()
{
return g_monitorRect;
}
CompatWeakPtr<IDirectDrawSurface7> RealPrimarySurface::getSurface()
{
return g_frontBuffer;

View File

@ -18,6 +18,7 @@ namespace DDraw
static HRESULT flip(CompatPtr<IDirectDrawSurface7> surfaceTargetOverride, DWORD flags);
static void flush();
static HRESULT getGammaRamp(DDGAMMARAMP* rampData);
static RECT getMonitorRect();
static CompatWeakPtr<IDirectDrawSurface7> getSurface();
static void init();
static bool isFullScreen();

View File

@ -19,6 +19,7 @@ namespace
DWORD g_origCaps = 0;
HWND g_deviceWindow = nullptr;
HPALETTE g_palette = nullptr;
RECT g_monitorRect = {};
}
namespace DDraw
@ -37,6 +38,7 @@ namespace DDraw
DeleteObject(g_palette);
g_palette = nullptr;
}
g_monitorRect = {};
s_palette = nullptr;
DDraw::RealPrimarySurface::release();
@ -45,6 +47,11 @@ namespace DDraw
template <typename TDirectDraw, typename TSurface, typename TSurfaceDesc>
HRESULT PrimarySurface::create(CompatRef<TDirectDraw> dd, TSurfaceDesc desc, TSurface*& surface)
{
const auto& dm = DDraw::DirectDraw::getDisplayMode(*CompatPtr<IDirectDraw7>::from(&dd));
g_monitorRect = D3dDdi::KernelModeThunks::getAdapterInfo(*CompatPtr<IDirectDraw7>::from(&dd)).monitorInfo.rcMonitor;
g_monitorRect.right = g_monitorRect.left + dm.dwWidth;
g_monitorRect.bottom = g_monitorRect.top + dm.dwHeight;
HRESULT result = RealPrimarySurface::create(dd);
if (FAILED(result))
{
@ -53,7 +60,6 @@ namespace DDraw
const DWORD origCaps = desc.ddsCaps.dwCaps;
const auto& dm = DDraw::DirectDraw::getDisplayMode(*CompatPtr<IDirectDraw7>::from(&dd));
desc.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
desc.dwWidth = dm.dwWidth;
desc.dwHeight = dm.dwHeight;
@ -68,6 +74,7 @@ namespace DDraw
if (FAILED(result))
{
Compat::Log() << "ERROR: Failed to create the compat primary surface: " << Compat::hex(result);
g_monitorRect = {};
RealPrimarySurface::release();
return result;
}
@ -180,6 +187,11 @@ namespace DDraw
return g_frontResource;
}
RECT PrimarySurface::getMonitorRect()
{
return g_monitorRect;
}
DWORD PrimarySurface::getOrigCaps()
{
return g_origCaps;
@ -210,7 +222,7 @@ namespace DDraw
m_surface->GetSurfaceDesc(m_surface, &desc);
if (desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)
{
DDSURFACEDESC2 gdiDesc = Gdi::VirtualScreen::getSurfaceDesc(D3dDdi::KernelModeThunks::getMonitorRect());
DDSURFACEDESC2 gdiDesc = Gdi::VirtualScreen::getSurfaceDesc(g_monitorRect);
desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH | DDSD_LPSURFACE;
desc.lPitch = gdiDesc.lPitch;
desc.lpSurface = gdiDesc.lpSurface;

View File

@ -21,6 +21,7 @@ namespace DDraw
static CompatPtr<IDirectDrawSurface7> getGdiSurface();
static CompatPtr<IDirectDrawSurface7> getBackBuffer();
static CompatPtr<IDirectDrawSurface7> getLastSurface();
static RECT getMonitorRect();
static CompatWeakPtr<IDirectDrawSurface7> getPrimary();
static HANDLE getFrontResource();
static DWORD getOrigCaps();

View File

@ -32,7 +32,7 @@ namespace
D3dDdi::ScopedCriticalSection lock;
Gdi::Region clipRgn(DDraw::DirectDrawClipper::getClipRgn(*clipper));
RECT monitorRect = D3dDdi::KernelModeThunks::getMonitorRect();
RECT monitorRect = DDraw::RealPrimarySurface::getMonitorRect();
RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds();
clipRgn.offset(monitorRect.left, monitorRect.top);
clipRgn &= virtualScreenBounds;

View File

@ -210,9 +210,11 @@
<ClInclude Include="Config\Config.h" />
<ClInclude Include="Config\EnumSetting.h" />
<ClInclude Include="Config\ListSetting.h" />
<ClInclude Include="Config\MappedSetting.h" />
<ClInclude Include="Config\Parser.h" />
<ClInclude Include="Config\Setting.h" />
<ClInclude Include="Config\Settings\CpuAffinity.h" />
<ClInclude Include="Config\Settings\DisplayResolution.h" />
<ClInclude Include="Config\Settings\ThreadPriorityBoost.h" />
<ClInclude Include="D3dDdi\Adapter.h" />
<ClInclude Include="D3dDdi\AdapterCallbacks.h" />
@ -234,6 +236,7 @@
<ClInclude Include="D3dDdi\Resource.h" />
<ClInclude Include="D3dDdi\ScopedCriticalSection.h" />
<ClInclude Include="D3dDdi\ShaderBlitter.h" />
<ClInclude Include="D3dDdi\SurfaceRepository.h" />
<ClInclude Include="D3dDdi\Visitors\AdapterCallbacksVisitor.h" />
<ClInclude Include="D3dDdi\Visitors\AdapterFuncsVisitor.h" />
<ClInclude Include="D3dDdi\Visitors\DeviceCallbacksVisitor.h" />
@ -314,6 +317,7 @@
<ClCompile Include="Config\Parser.cpp" />
<ClCompile Include="Config\Setting.cpp" />
<ClCompile Include="Config\Settings\CpuAffinity.cpp" />
<ClCompile Include="Config\Settings\DisplayResolution.cpp" />
<ClCompile Include="D3dDdi\Adapter.cpp" />
<ClCompile Include="D3dDdi\AdapterCallbacks.cpp" />
<ClCompile Include="D3dDdi\AdapterFuncs.cpp" />
@ -334,6 +338,7 @@
<ClCompile Include="D3dDdi\Resource.cpp" />
<ClCompile Include="D3dDdi\ScopedCriticalSection.cpp" />
<ClCompile Include="D3dDdi\ShaderBlitter.cpp" />
<ClCompile Include="D3dDdi\SurfaceRepository.cpp" />
<ClCompile Include="DDraw\Blitter.cpp" />
<ClCompile Include="DDraw\DirectDraw.cpp" />
<ClCompile Include="DDraw\DirectDrawClipper.cpp" />

View File

@ -426,6 +426,15 @@
<ClInclude Include="Common\Comparison.h">
<Filter>Header Files\Common</Filter>
</ClInclude>
<ClInclude Include="Config\Settings\DisplayResolution.h">
<Filter>Header Files\Config\Settings</Filter>
</ClInclude>
<ClInclude Include="Config\MappedSetting.h">
<Filter>Header Files\Config</Filter>
</ClInclude>
<ClInclude Include="D3dDdi\SurfaceRepository.h">
<Filter>Header Files\D3dDdi</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Gdi\Gdi.cpp">
@ -668,6 +677,12 @@
<ClCompile Include="D3dDdi\ShaderBlitter.cpp">
<Filter>Source Files\D3dDdi</Filter>
</ClCompile>
<ClCompile Include="Config\Settings\DisplayResolution.cpp">
<Filter>Source Files\Config\Settings</Filter>
</ClCompile>
<ClCompile Include="D3dDdi\SurfaceRepository.cpp">
<Filter>Source Files\D3dDdi</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="DDrawCompat.rc">

View File

@ -3,18 +3,52 @@
#include <Common/Hook.h>
#include <Common/Log.h>
#include <Gdi/Metrics.h>
#include <Win32/DisplayMode.h>
namespace
{
decltype(&GetSystemMetricsForDpi) g_origGetSystemMetricsForDpi = nullptr;
int getAdjustedDisplayMetrics(int nIndex, int cxIndex)
{
int result = CALL_ORIG_FUNC(GetSystemMetrics)(nIndex);
auto dm = Win32::DisplayMode::getEmulatedDisplayMode();
if (0 == dm.rect.left && 0 == dm.rect.top)
{
result += (nIndex == cxIndex) ? dm.diff.cx : dm.diff.cy;
}
return result;
}
int WINAPI getSystemMetrics(int nIndex)
{
LOG_FUNC("GetSystemMetrics", nIndex);
if (SM_CXSIZE == nIndex)
switch (nIndex)
{
nIndex = SM_CYSIZE;
case SM_CXSCREEN:
case SM_CYSCREEN:
{
return LOG_RESULT(getAdjustedDisplayMetrics(nIndex, SM_CXSCREEN));
}
case SM_CXFULLSCREEN:
case SM_CYFULLSCREEN:
{
return LOG_RESULT(getAdjustedDisplayMetrics(nIndex, SM_CXFULLSCREEN));
}
case SM_CXMAXIMIZED:
case SM_CYMAXIMIZED:
{
return LOG_RESULT(getAdjustedDisplayMetrics(nIndex, SM_CXMAXIMIZED));
}
case SM_CXSIZE:
nIndex = SM_CYSIZE;
break;
}
return LOG_RESULT(CALL_ORIG_FUNC(GetSystemMetrics)(nIndex));
}

View File

@ -15,6 +15,7 @@
#include <Gdi/TitleBar.h>
#include <Gdi/Window.h>
#include <Gdi/WinProc.h>
#include <Win32/DisplayMode.h>
namespace
{
@ -34,6 +35,7 @@ namespace
bool isUser32ScrollBar(HWND hwnd);
void onCreateWindow(HWND hwnd);
void onDestroyWindow(HWND hwnd);
void onGetMinMaxInfo(MINMAXINFO& mmi);
void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp);
void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp);
void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW);
@ -45,6 +47,20 @@ namespace
switch (uMsg)
{
case WM_DISPLAYCHANGE:
{
if (0 != wParam)
{
return 0;
}
wParam = Win32::DisplayMode::getBpp();
break;
}
case WM_GETMINMAXINFO:
onGetMinMaxInfo(*reinterpret_cast<MINMAXINFO*>(lParam));
break;
case WM_SYNCPAINT:
if (isTopLevelWindow(hwnd))
{
@ -266,6 +282,15 @@ namespace
}
}
void onGetMinMaxInfo(MINMAXINFO& mmi)
{
MONITORINFOEXA mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfoA(MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), &mi);
mmi.ptMaxSize.x = mi.rcMonitor.right - 2 * mmi.ptMaxPosition.x;
mmi.ptMaxSize.y = mi.rcMonitor.bottom - 2 * mmi.ptMaxPosition.y;
}
void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp)
{
for (auto notifyFunc : g_windowPosChangeNotifyFuncs)

View File

@ -1,8 +1,12 @@
#include <set>
#include <string>
#include <vector>
#include <Common/Comparison.h>
#include <Common/CompatPtr.h>
#include <Common/Hook.h>
#include <Common/ScopedSrwLock.h>
#include <Config/Config.h>
#include <DDraw/DirectDraw.h>
#include <DDraw/ScopedThreadLock.h>
#include <Gdi/Gdi.h>
@ -10,10 +14,14 @@
#include <Win32/DisplayMode.h>
BOOL WINAPI DWM8And16Bit_IsShimApplied_CallOut() { return FALSE; }
ULONG WINAPI GdiEntry13() { return 0; }
BOOL WINAPI SE_COM_HookInterface(CLSID*, GUID*, DWORD, DWORD) { return 0; }
namespace
{
using Win32::DisplayMode::DisplayMode;
using Win32::DisplayMode::EmulatedDisplayMode;
template <typename Char>
struct EnumParams
{
@ -31,13 +39,48 @@ namespace
}
};
DWORD g_origBpp = 0;
DWORD g_currentBpp = 0;
DWORD g_lastBpp = 0;
struct GetMonitorFromDcEnumArgs
{
POINT org;
HMONITOR hmonitor;
};
DWORD g_desktopBpp = 0;
RECT g_cursorRect = {};
ULONG g_displaySettingsUniquenessBias = 0;
EmulatedDisplayMode g_emulatedDisplayMode = {};
Compat::SrwLock g_srwLock;
BOOL WINAPI dwm8And16BitIsShimAppliedCallOut();
BOOL WINAPI seComHookInterface(CLSID* clsid, GUID* iid, DWORD unk1, DWORD unk2);
template <typename DevMode, typename EnumDisplaySettingsExFunc, typename Char>
SIZE getConfiguredResolution(EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, const Char* deviceName);
template <typename Char>
std::wstring getDeviceName(const Char* deviceName);
HMONITOR getMonitorFromDc(HDC dc);
MONITORINFO getMonitorInfo(const std::wstring& deviceName);
template <typename DevMode, typename EnumDisplaySettingsExFunc, typename Char>
std::vector<DisplayMode> getSupportedDisplayModes(
EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, const Char* deviceName, DWORD flags);
void adjustMonitorInfo(MONITORINFO& mi)
{
Compat::ScopedSrwLockShared srwLock(g_srwLock);
if (!g_emulatedDisplayMode.deviceName.empty() &&
g_emulatedDisplayMode.rect.left == mi.rcMonitor.left &&
g_emulatedDisplayMode.rect.top == mi.rcMonitor.top)
{
mi.rcMonitor.right += g_emulatedDisplayMode.diff.cx;
mi.rcMonitor.bottom += g_emulatedDisplayMode.diff.cy;
mi.rcWork.right += g_emulatedDisplayMode.diff.cx;
mi.rcWork.bottom += g_emulatedDisplayMode.diff.cy;
}
}
template <typename CStr, typename DevMode, typename ChangeDisplaySettingsExFunc, typename EnumDisplaySettingsExFunc>
LONG changeDisplaySettingsEx(
ChangeDisplaySettingsExFunc origChangeDisplaySettingsEx,
@ -45,6 +88,19 @@ namespace
CStr lpszDeviceName, DevMode* lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam)
{
DDraw::ScopedThreadLock lock;
DevMode targetDevMode = {};
if (lpDevMode)
{
targetDevMode = *lpDevMode;
targetDevMode.dmBitsPerPel = 32;
SIZE resolutionOverride = getConfiguredResolution<DevMode>(origEnumDisplaySettingsEx, lpszDeviceName);
if (0 != resolutionOverride.cx)
{
targetDevMode.dmPelsWidth = resolutionOverride.cx;
targetDevMode.dmPelsHeight = resolutionOverride.cy;
}
}
DevMode prevDevMode = {};
if (!(dwflags & CDS_TEST))
{
@ -52,50 +108,84 @@ namespace
origEnumDisplaySettingsEx(lpszDeviceName, ENUM_CURRENT_SETTINGS, &prevDevMode, 0);
}
BOOL result = FALSE;
LONG result = 0;
if (lpDevMode)
{
DWORD origBpp = lpDevMode->dmBitsPerPel;
lpDevMode->dmBitsPerPel = 32;
result = origChangeDisplaySettingsEx(lpszDeviceName, lpDevMode, hwnd, dwflags, lParam);
lpDevMode->dmBitsPerPel = origBpp;
result = origChangeDisplaySettingsEx(lpszDeviceName, &targetDevMode, hwnd, dwflags, lParam);
if (DISP_CHANGE_SUCCESSFUL != result &&
(lpDevMode->dmPelsWidth != targetDevMode.dmPelsWidth || lpDevMode->dmPelsHeight != targetDevMode.dmPelsHeight))
{
LOG_ONCE("Failed to apply setting: DisplayResolution = " << targetDevMode.dmPelsWidth << 'x' << targetDevMode.dmPelsHeight);
targetDevMode.dmPelsWidth = lpDevMode->dmPelsWidth;
targetDevMode.dmPelsHeight = lpDevMode->dmPelsHeight;
result = origChangeDisplaySettingsEx(lpszDeviceName, &targetDevMode, hwnd, dwflags, lParam);
}
}
else
{
result = origChangeDisplaySettingsEx(lpszDeviceName, lpDevMode, hwnd, dwflags, lParam);
result = origChangeDisplaySettingsEx(lpszDeviceName, nullptr, hwnd, dwflags, lParam);
}
if (SUCCEEDED(result) && !(dwflags & CDS_TEST))
if (dwflags & CDS_TEST)
{
return result;
}
DevMode currDevMode = {};
currDevMode.dmSize = sizeof(currDevMode);
origEnumDisplaySettingsEx(lpszDeviceName, ENUM_CURRENT_SETTINGS, &currDevMode, 0);
if (0 == memcmp(&currDevMode, &prevDevMode, sizeof(currDevMode)))
{
HANDLE dwmDxFullScreenTransitionEvent = OpenEventW(
EVENT_MODIFY_STATE, FALSE, L"DWM_DX_FULLSCREEN_TRANSITION_EVENT");
SetEvent(dwmDxFullScreenTransitionEvent);
CloseHandle(dwmDxFullScreenTransitionEvent);
}
if (DISP_CHANGE_SUCCESSFUL != result)
{
return result;
}
RECT clipRect = {};
{
Compat::ScopedSrwLockExclusive srwLock(g_srwLock);
++g_displaySettingsUniquenessBias;
clipRect = g_cursorRect;
if (lpDevMode)
{
g_currentBpp = lpDevMode->dmBitsPerPel;
g_lastBpp = lpDevMode->dmBitsPerPel;
g_emulatedDisplayMode.width = lpDevMode->dmPelsWidth;
g_emulatedDisplayMode.height = lpDevMode->dmPelsHeight;
g_emulatedDisplayMode.bpp = lpDevMode->dmBitsPerPel;
g_emulatedDisplayMode.refreshRate = currDevMode.dmDisplayFrequency;
g_emulatedDisplayMode.deviceName = getDeviceName(lpszDeviceName);
g_emulatedDisplayMode.rect = getMonitorInfo(g_emulatedDisplayMode.deviceName).rcMonitor;
g_emulatedDisplayMode.rect.right = g_emulatedDisplayMode.rect.left + lpDevMode->dmPelsWidth;
g_emulatedDisplayMode.rect.bottom = g_emulatedDisplayMode.rect.top + lpDevMode->dmPelsHeight;
g_emulatedDisplayMode.diff.cx = lpDevMode->dmPelsWidth - currDevMode.dmPelsWidth;
g_emulatedDisplayMode.diff.cy = lpDevMode->dmPelsHeight - currDevMode.dmPelsHeight;
IntersectRect(&clipRect, &clipRect, &g_emulatedDisplayMode.rect);
}
else
{
g_currentBpp = g_origBpp;
g_emulatedDisplayMode = {};
g_emulatedDisplayMode.bpp = g_desktopBpp;
}
DevMode currDevMode = {};
currDevMode.dmSize = sizeof(currDevMode);
origEnumDisplaySettingsEx(lpszDeviceName, ENUM_CURRENT_SETTINGS, &currDevMode, 0);
if (currDevMode.dmPelsWidth == prevDevMode.dmPelsWidth &&
currDevMode.dmPelsHeight == prevDevMode.dmPelsHeight &&
currDevMode.dmBitsPerPel == prevDevMode.dmBitsPerPel &&
currDevMode.dmDisplayFrequency == prevDevMode.dmDisplayFrequency &&
currDevMode.dmDisplayFlags == prevDevMode.dmDisplayFlags)
{
HANDLE dwmDxFullScreenTransitionEvent = OpenEventW(
EVENT_MODIFY_STATE, FALSE, L"DWM_DX_FULLSCREEN_TRANSITION_EVENT");
SetEvent(dwmDxFullScreenTransitionEvent);
CloseHandle(dwmDxFullScreenTransitionEvent);
}
Gdi::VirtualScreen::update();
}
CALL_ORIG_FUNC(ClipCursor)(&clipRect);
auto& dm = lpDevMode ? *lpDevMode : currDevMode;
LPARAM resolution = (dm.dmPelsHeight << 16) | dm.dmPelsWidth;
EnumWindows(sendDisplayChange, resolution);
SetCursorPos(currDevMode.dmPosition.x + dm.dmPelsWidth / 2, currDevMode.dmPosition.y + dm.dmPelsHeight / 2);
Gdi::VirtualScreen::update();
return result;
}
@ -119,6 +209,31 @@ namespace
lpszDeviceName, lpDevMode, hwnd, dwflags, lParam));
}
BOOL WINAPI clipCursor(const RECT* lpRect)
{
LOG_FUNC("ClipCursor", lpRect);
BOOL result = CALL_ORIG_FUNC(ClipCursor)(lpRect);
if (!result)
{
return result;
}
RECT rect = {};
CALL_ORIG_FUNC(GetClipCursor)(&rect);
{
Compat::ScopedSrwLockExclusive srwLock(g_srwLock);
g_cursorRect = rect;
if (!g_emulatedDisplayMode.deviceName.empty())
{
IntersectRect(&rect, &rect, &g_emulatedDisplayMode.rect);
CALL_ORIG_FUNC(ClipCursor)(&rect);
}
}
return result;
}
void disableDwm8And16BitMitigation()
{
auto user32 = GetModuleHandle("user32");
@ -147,8 +262,7 @@ namespace
{
if (ENUM_REGISTRY_SETTINGS == iModeNum || !lpDevMode)
{
BOOL result = origEnumDisplaySettingsEx(lpszDeviceName, iModeNum, lpDevMode, dwFlags);
return result;
return origEnumDisplaySettingsEx(lpszDeviceName, iModeNum, lpDevMode, dwFlags);
}
if (ENUM_CURRENT_SETTINGS == iModeNum)
@ -156,46 +270,53 @@ namespace
BOOL result = origEnumDisplaySettingsEx(lpszDeviceName, iModeNum, lpDevMode, dwFlags);
if (result)
{
lpDevMode->dmBitsPerPel = g_currentBpp;
Compat::ScopedSrwLockShared srwLock(g_srwLock);
if (getDeviceName(lpszDeviceName) == g_emulatedDisplayMode.deviceName)
{
lpDevMode->dmBitsPerPel = g_emulatedDisplayMode.bpp;
lpDevMode->dmPelsWidth = g_emulatedDisplayMode.width;
lpDevMode->dmPelsHeight = g_emulatedDisplayMode.height;
}
else
{
lpDevMode->dmBitsPerPel = g_desktopBpp;
}
}
return result;
}
thread_local std::vector<DevMode> devModes;
thread_local std::vector<DisplayMode> displayModes;
thread_local EnumParams<Char> lastEnumParams = {};
EnumParams<Char> currentEnumParams = {
lpszDeviceName ? lpszDeviceName : std::basic_string<Char>(), dwFlags };
EnumParams<Char> currentEnumParams = { lpszDeviceName ? lpszDeviceName : std::basic_string<Char>(), dwFlags };
if (0 == iModeNum || devModes.empty() || currentEnumParams != lastEnumParams)
if (0 == iModeNum || displayModes.empty() || currentEnumParams != lastEnumParams)
{
devModes.clear();
displayModes = getSupportedDisplayModes<DevMode>(origEnumDisplaySettingsEx, lpszDeviceName, dwFlags);
lastEnumParams = currentEnumParams;
DWORD modeNum = 0;
DevMode dm = {};
dm.dmSize = sizeof(dm);
while (origEnumDisplaySettingsEx(lpszDeviceName, modeNum, &dm, dwFlags))
{
if (32 == dm.dmBitsPerPel)
{
dm.dmBitsPerPel = 8;
devModes.push_back(dm);
dm.dmBitsPerPel = 16;
devModes.push_back(dm);
dm.dmBitsPerPel = 32;
devModes.push_back(dm);
}
++modeNum;
}
}
if (iModeNum >= devModes.size())
if (iModeNum >= displayModes.size() * 3)
{
return FALSE;
}
*lpDevMode = devModes[iModeNum];
const auto& displayMode = displayModes[iModeNum / 3];
lpDevMode->dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT |
DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY;
lpDevMode->dmDisplayOrientation = DMDO_DEFAULT;
lpDevMode->dmPelsWidth = displayMode.width;
lpDevMode->dmPelsHeight = displayMode.height;
lpDevMode->dmDisplayFlags = 0;
lpDevMode->dmDisplayFrequency = displayMode.refreshRate;
switch (iModeNum % 3)
{
case 0: lpDevMode->dmBitsPerPel = 8; break;
case 1: lpDevMode->dmBitsPerPel = 16; break;
case 2: lpDevMode->dmBitsPerPel = 32; break;
}
return TRUE;
}
@ -215,6 +336,44 @@ namespace
lpszDeviceName, iModeNum, lpDevMode, dwFlags));
}
ULONG WINAPI gdiEntry13()
{
Compat::ScopedSrwLockShared lock(g_srwLock);
return CALL_ORIG_FUNC(GdiEntry13)() + g_displaySettingsUniquenessBias;
}
BOOL WINAPI getClipCursor(LPRECT lpRect)
{
LOG_FUNC("GetClipCursor", lpRect);
BOOL result = CALL_ORIG_FUNC(GetClipCursor)(lpRect);
if (result)
{
*lpRect = g_cursorRect;
}
return LOG_RESULT(result);
}
template <typename DevMode, typename EnumDisplaySettingsExFunc, typename Char>
SIZE getConfiguredResolution(EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, const Char* deviceName)
{
auto resolution = Config::displayResolution.get();
if (Config::Settings::DisplayResolution::DESKTOP == resolution)
{
DevMode dm = {};
dm.dmSize = sizeof(dm);
if (origEnumDisplaySettingsEx(deviceName, ENUM_REGISTRY_SETTINGS, &dm, 0))
{
resolution.cx = dm.dmPelsWidth;
resolution.cy = dm.dmPelsHeight;
}
else
{
resolution = {};
}
}
return resolution;
}
int WINAPI getDeviceCaps(HDC hdc, int nIndex)
{
LOG_FUNC("GetDeviceCaps", hdc, nIndex);
@ -223,34 +382,52 @@ namespace
case BITSPIXEL:
if (Gdi::isDisplayDc(hdc))
{
return LOG_RESULT(g_currentBpp);
return LOG_RESULT(Win32::DisplayMode::getBpp());
}
break;
case COLORRES:
if (8 == g_currentBpp && Gdi::isDisplayDc(hdc))
if (8 == Win32::DisplayMode::getBpp() && Gdi::isDisplayDc(hdc))
{
return 24;
}
break;
case HORZRES:
case VERTRES:
if (Gdi::isDisplayDc(hdc))
{
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(getMonitorFromDc(hdc), &mi);
if (HORZRES == nIndex)
{
return LOG_RESULT(mi.rcMonitor.right - mi.rcMonitor.left);
}
else
{
return LOG_RESULT(mi.rcMonitor.bottom - mi.rcMonitor.top);
}
}
break;
case NUMCOLORS:
case NUMRESERVED:
if (8 == g_currentBpp && Gdi::isDisplayDc(hdc))
if (8 == Win32::DisplayMode::getBpp() && Gdi::isDisplayDc(hdc))
{
return 20;
}
break;
case RASTERCAPS:
if (8 == g_currentBpp && Gdi::isDisplayDc(hdc))
if (8 == Win32::DisplayMode::getBpp() && Gdi::isDisplayDc(hdc))
{
return LOG_RESULT(CALL_ORIG_FUNC(GetDeviceCaps)(hdc, nIndex) | RC_PALETTE);
}
break;
case SIZEPALETTE:
if (8 == g_currentBpp && Gdi::isDisplayDc(hdc))
if (8 == Win32::DisplayMode::getBpp() && Gdi::isDisplayDc(hdc))
{
return 256;
}
@ -259,6 +436,191 @@ namespace
return LOG_RESULT(CALL_ORIG_FUNC(GetDeviceCaps)(hdc, nIndex));
}
std::size_t getDeviceNameLength(const char* deviceName)
{
return std::strlen(deviceName);
}
std::size_t getDeviceNameLength(const wchar_t* deviceName)
{
return std::wcslen(deviceName);
}
template <typename Char>
std::wstring getDeviceName(const Char* deviceName)
{
if (deviceName)
{
return std::wstring(deviceName, deviceName + getDeviceNameLength(deviceName));
}
MONITORINFOEXW mi = {};
mi.cbSize = sizeof(mi);
CALL_ORIG_FUNC(GetMonitorInfoW)(MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY), &mi);
return mi.szDevice;
}
BOOL CALLBACK getMonitorFromDcEnum(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData)
{
auto& args = *reinterpret_cast<GetMonitorFromDcEnumArgs*>(dwData);
MONITORINFOEX mi = {};
mi.cbSize = sizeof(mi);
CALL_ORIG_FUNC(GetMonitorInfoA)(hMonitor, &mi);
HDC dc = CreateDC(mi.szDevice, nullptr, nullptr, nullptr);
if (dc)
{
POINT org = {};
GetDCOrgEx(dc, &org);
DeleteDC(dc);
if (org == args.org)
{
args.hmonitor = hMonitor;
return FALSE;
}
}
return TRUE;
}
HMONITOR getMonitorFromDc(HDC dc)
{
HWND hwnd = CALL_ORIG_FUNC(WindowFromDC)(dc);
if (hwnd)
{
return MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
}
GetMonitorFromDcEnumArgs args = {};
GetDCOrgEx(dc, &args.org);
EnumDisplayMonitors(nullptr, nullptr, getMonitorFromDcEnum, reinterpret_cast<LPARAM>(&args));
return args.hmonitor ? args.hmonitor : MonitorFromPoint({}, MONITOR_DEFAULTTOPRIMARY);
}
BOOL CALLBACK getMonitorInfoEnum(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM dwData)
{
MONITORINFOEXW mi = {};
mi.cbSize = sizeof(mi);
CALL_ORIG_FUNC(GetMonitorInfoW)(hMonitor, &mi);
auto& dest = *reinterpret_cast<MONITORINFOEXW*>(dwData);
if (0 == wcscmp(mi.szDevice, dest.szDevice))
{
dest = mi;
return FALSE;
}
return TRUE;
}
MONITORINFO getMonitorInfo(const std::wstring& deviceName)
{
MONITORINFOEXW mi = {};
wcscpy_s(mi.szDevice, deviceName.c_str());
EnumDisplayMonitors(nullptr, nullptr, &getMonitorInfoEnum, reinterpret_cast<LPARAM>(&mi));
return mi;
}
BOOL WINAPI getMonitorInfoA(HMONITOR hMonitor, LPMONITORINFO lpmi)
{
LOG_FUNC("GetMonitorInfoA", hMonitor, lpmi);
BOOL result = CALL_ORIG_FUNC(GetMonitorInfoA)(hMonitor, lpmi);
if (result)
{
adjustMonitorInfo(*lpmi);
}
return LOG_RESULT(result);
}
BOOL WINAPI getMonitorInfoW(HMONITOR hMonitor, LPMONITORINFO lpmi)
{
LOG_FUNC("GetMonitorInfoW", hMonitor, lpmi);
BOOL result = CALL_ORIG_FUNC(GetMonitorInfoW)(hMonitor, lpmi);
if (result)
{
adjustMonitorInfo(*lpmi);
}
return LOG_RESULT(result);
}
template <typename DevMode, typename EnumDisplaySettingsExFunc, typename Char>
std::vector<DisplayMode> getSupportedDisplayModes(
EnumDisplaySettingsExFunc origEnumDisplaySettingsEx, const Char* deviceName, DWORD flags)
{
std::set<DisplayMode> displayModes;
DWORD modeNum = 0;
DevMode dm = {};
dm.dmSize = sizeof(dm);
while (origEnumDisplaySettingsEx(deviceName, modeNum, &dm, flags))
{
if (32 == dm.dmBitsPerPel)
{
displayModes.insert({ dm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel, dm.dmDisplayFrequency });
}
++modeNum;
}
auto resolutionOverride = getConfiguredResolution<DevMode>(origEnumDisplaySettingsEx, deviceName);
if (0 == resolutionOverride.cx)
{
return { displayModes.begin(), displayModes.end() };
}
std::set<DWORD> supportedRefreshRates;
for (const auto& mode : displayModes)
{
if (static_cast<LONG>(mode.width) == resolutionOverride.cx &&
static_cast<LONG>(mode.height) == resolutionOverride.cy)
{
supportedRefreshRates.insert(mode.refreshRate);
}
}
if (supportedRefreshRates.empty())
{
return { displayModes.begin(), displayModes.end() };
}
std::vector<DisplayMode> customDisplayModes;
DWORD prevWidth = 0;
DWORD prevHeight = 0;
for (const auto& mode : displayModes)
{
if (mode.width != prevWidth || mode.height != prevHeight)
{
for (auto refreshRate : supportedRefreshRates)
{
customDisplayModes.push_back({ mode.width, mode.height, mode.bpp, refreshRate });
}
prevWidth = mode.width;
prevHeight = mode.height;
}
}
return customDisplayModes;
}
BOOL CALLBACK initMonitor(HMONITOR hMonitor, HDC /*hdcMonitor*/, LPRECT /*lprcMonitor*/, LPARAM /*dwData*/)
{
MONITORINFOEX mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi);
DEVMODE dm = {};
dm.dmSize = sizeof(dm);
if (EnumDisplaySettings(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm))
{
if (32 != dm.dmBitsPerPel)
{
dm = {};
dm.dmSize = sizeof(dm);
dm.dmFields = DM_BITSPERPEL;
dm.dmBitsPerPel = 32;
ChangeDisplaySettingsEx(mi.szDevice, &dm, nullptr, 0, nullptr);
}
}
return TRUE;
}
BOOL WINAPI seComHookInterface(CLSID* clsid, GUID* iid, DWORD unk1, DWORD unk2)
{
LOG_FUNC("SE_COM_HookInterface", clsid, iid, unk1, unk2);
@ -268,6 +630,17 @@ namespace
}
return LOG_RESULT(CALL_ORIG_FUNC(SE_COM_HookInterface)(clsid, iid, unk1, unk2));
}
BOOL CALLBACK sendDisplayChange(HWND hwnd, LPARAM lParam)
{
DWORD pid = 0;
GetWindowThreadProcessId(hwnd, &pid);
if (GetCurrentProcessId() == pid)
{
SendNotifyMessage(hwnd, WM_DISPLAYCHANGE, 0, lParam);
}
return TRUE;
}
}
namespace Win32
@ -276,36 +649,41 @@ namespace Win32
{
DWORD getBpp()
{
return g_currentBpp;
return getEmulatedDisplayMode().bpp;
}
EmulatedDisplayMode getEmulatedDisplayMode()
{
Compat::ScopedSrwLockShared lock(g_srwLock);
return g_emulatedDisplayMode;
}
ULONG queryDisplaySettingsUniqueness()
{
static auto ddQueryDisplaySettingsUniqueness = reinterpret_cast<ULONG(APIENTRY*)()>(
GetProcAddress(GetModuleHandle("gdi32"), "GdiEntry13"));
return ddQueryDisplaySettingsUniqueness();
return CALL_ORIG_FUNC(GdiEntry13)();
}
void installHooks()
{
DEVMODEA devMode = {};
devMode.dmSize = sizeof(devMode);
EnumDisplaySettingsEx(nullptr, ENUM_CURRENT_SETTINGS, &devMode, 0);
g_origBpp = devMode.dmBitsPerPel;
g_currentBpp = g_origBpp;
g_lastBpp = g_origBpp;
DEVMODEA dm = {};
dm.dmSize = sizeof(dm);
EnumDisplaySettingsEx(nullptr, ENUM_CURRENT_SETTINGS, &dm, 0);
g_desktopBpp = dm.dmBitsPerPel;
g_emulatedDisplayMode.bpp = dm.dmBitsPerPel;
if (32 != devMode.dmBitsPerPel)
{
devMode.dmBitsPerPel = 32;
ChangeDisplaySettings(&devMode, 0);
}
GetClipCursor(&g_cursorRect);
EnumDisplayMonitors(nullptr, nullptr, &initMonitor, 0);
HOOK_FUNCTION(user32, ChangeDisplaySettingsExA, changeDisplaySettingsExA);
HOOK_FUNCTION(user32, ChangeDisplaySettingsExW, changeDisplaySettingsExW);
HOOK_FUNCTION(user32, ClipCursor, clipCursor);
HOOK_FUNCTION(user32, EnumDisplaySettingsExA, enumDisplaySettingsExA);
HOOK_FUNCTION(user32, EnumDisplaySettingsExW, enumDisplaySettingsExW);
HOOK_FUNCTION(gdi32, GdiEntry13, gdiEntry13);
HOOK_FUNCTION(user32, GetClipCursor, getClipCursor);
HOOK_FUNCTION(gdi32, GetDeviceCaps, getDeviceCaps);
HOOK_FUNCTION(user32, GetMonitorInfoA, getMonitorInfoA);
HOOK_FUNCTION(user32, GetMonitorInfoW, getMonitorInfoW);
disableDwm8And16BitMitigation();
}

View File

@ -1,14 +1,41 @@
#pragma once
#include <tuple>
#include <Windows.h>
#include <Common/Comparison.h>
namespace Win32
{
namespace DisplayMode
{
struct DisplayMode
{
DWORD width;
DWORD height;
DWORD bpp;
DWORD refreshRate;
};
struct EmulatedDisplayMode : DisplayMode
{
std::wstring deviceName;
RECT rect;
SIZE diff;
};
DWORD getBpp();
EmulatedDisplayMode getEmulatedDisplayMode();
ULONG queryDisplaySettingsUniqueness();
void installHooks();
using ::operator<;
inline auto toTuple(const DisplayMode& dm)
{
return std::make_tuple(dm.width, dm.height, dm.bpp, dm.refreshRate);
}
}
}

View File

@ -32,7 +32,7 @@ namespace
<< dm.dmPelsHeight
<< dm.dmBitsPerPel
<< dm.dmDisplayFrequency
<< dm.dmDisplayFlags;
<< Compat::hex(dm.dmDisplayFlags);
}
template <typename MdiCreateStruct>