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

Added config overlay

This commit is contained in:
narzoul 2021-08-23 23:08:31 +02:00
parent fb6555523f
commit 199d434f29
50 changed files with 1924 additions and 332 deletions

View File

@ -5,14 +5,13 @@
namespace
{
std::map<std::string, unsigned> createMapping(const std::vector<std::string>& enumNames)
std::vector<std::pair<std::string, unsigned>> createMapping(const std::vector<std::string>& enumNames)
{
std::map<std::string, unsigned> mapping;
std::vector<std::pair<std::string, unsigned>> mapping;
unsigned i = 0;
for (const auto& name : enumNames)
{
mapping[name] = i;
mapping.push_back({ name, i });
++i;
}
return mapping;

View File

@ -1,6 +1,7 @@
#pragma once
#include <map>
#include <algorithm>
#include <vector>
#include <Config/Parser.h>
#include <Config/Setting.h>
@ -13,17 +14,23 @@ namespace Config
public:
Value get() const { return m_value; }
protected:
MappedSetting(const std::string& name, const std::string& default, const std::map<std::string, Value>& valueMapping)
: Setting(name, default)
, m_value{}
, m_valueMapping(valueMapping)
virtual std::vector<std::string> getDefaultValueStrings() override
{
}
if (m_defaultValueStrings.empty())
{
auto prevValue = m_value;
auto prevParam = m_param;
for (const auto& pair : m_valueMapping)
{
m_value = pair.second;
m_param = getParamInfo().default;
m_defaultValueStrings.push_back(getValueStr());
}
m_value = prevValue;
m_param = prevParam;
virtual std::string getParamStr() const
{
return {};
}
return m_defaultValueStrings;
}
virtual std::string getValueStr() const override
@ -32,60 +39,39 @@ namespace Config
{
if (pair.second == m_value)
{
std::string param(getParamStr());
if (!param.empty())
const auto paramInfo = getParamInfo();
if (!paramInfo.name.empty())
{
param = '(' + param + ')';
return pair.first + '(' + std::to_string(paramInfo.current) + ')';
}
return pair.first + param;
return pair.first;
}
}
throw ParsingError("MappedSetting::getValueStr(): value not found in mapping");
}
virtual void setDefaultParam(const Value& /*value*/)
protected:
MappedSetting(const std::string& name, const std::string& default,
const std::vector<std::pair<std::string, Value>>& valueMapping)
: Setting(name, default)
, m_value{}
, m_valueMapping(valueMapping)
{
}
virtual void setValue(const std::string& value) override
{
std::string val(value);
std::string param;
auto parenPos = value.find('(');
if (std::string::npos != parenPos)
{
val = value.substr(0, parenPos);
param = value.substr(parenPos + 1);
if (param.length() < 2 || param.back() != ')')
{
throw ParsingError("invalid value: '" + value + "'");
}
param = param.substr(0, param.length() - 1);
}
auto it = m_valueMapping.find(val);
auto it = std::find_if(m_valueMapping.begin(), m_valueMapping.end(),
[&](const auto& v) { return v.first == value; });
if (it == m_valueMapping.end())
{
throw ParsingError("invalid value: '" + value + "'");
}
if (param.empty())
{
m_value = it->second;
setDefaultParam(it->second);
}
else
{
setValue(it->second, param);
}
}
virtual void setValue(const Value& /*value*/, const std::string& param)
{
throw ParsingError("invalid parameter: '" + param + "'");
m_value = it->second;
}
Value m_value;
const std::map<std::string, Value> m_valueMapping;
const std::vector<std::pair<std::string, Value>> m_valueMapping;
std::vector<std::string> m_defaultValueStrings;
};
}

View File

@ -138,10 +138,26 @@ namespace Config
name.insert(name.end(), maxNameLength - name.length(), ' ');
std::string source(setting.second.getSource());
source.insert(source.end(), maxSourceLength - source.length(), ' ');
Compat::Log() << " [" << source << "] " << name << " = " << setting.second.getValueAsString();
Compat::Log() << " [" << source << "] " << name << " = " << setting.second.getValueStr();
}
}
int parseInt(const std::string& value, int min, int max)
{
if (value.empty() || std::string::npos != value.find_first_not_of("+-0123456789") ||
std::string::npos != value.substr(1).find_first_of("+-"))
{
throw ParsingError("not a valid integer: '" + value + "'");
}
int result = std::strtol(value.c_str(), nullptr, 10);
if (result < min || result > max)
{
throw ParsingError("integer out of range: '" + value + "'");
}
return result;
}
SIZE parseResolution(const std::string& value)
{
try
@ -149,14 +165,7 @@ namespace Config
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;
}
return { parseInt(value.substr(0, pos), 1, MAXUINT16), parseInt(value.substr(pos + 1), 1, MAXUINT16) };
}
}
catch (ParsingError&)
@ -166,21 +175,22 @@ namespace Config
throw ParsingError("invalid resolution: '" + value + "'");
}
unsigned parseUnsigned(const std::string& value)
{
if (value.empty() || std::string::npos != value.find_first_not_of("0123456789"))
{
throw ParsingError("not an unsigned integer: '" + value + "'");
}
return std::strtoul(value.c_str(), nullptr, 10);
}
void registerSetting(Setting& setting)
{
const auto& name = setting.getName();
getSettings().emplace(name, setting);
}
std::string removeParam(const std::string& value)
{
auto paramPos = value.find('(');
if (paramPos != std::string::npos)
{
return value.substr(0, paramPos);
}
return value;
}
std::string trim(const std::string& str)
{
auto result(str);

View File

@ -20,8 +20,9 @@ namespace Config
{
void loadAllConfigFiles(const std::filesystem::path& processPath);
SIZE parseResolution(const std::string& value);
unsigned parseUnsigned(const std::string& value);
int parseInt(const std::string& value, int min, int max);
void registerSetting(Setting& setting);
std::string removeParam(const std::string& value);
std::string trim(const std::string& str);
}
}

View File

@ -4,7 +4,8 @@
namespace Config
{
Setting::Setting(const std::string& name, const std::string& default)
: m_name(name)
: m_param(0)
, m_name(name)
, m_default(default)
{
Parser::registerSetting(*this);
@ -15,9 +16,65 @@ namespace Config
set("default", "default");
}
void Setting::set(const std::string& value)
{
if ("default" == value)
{
set(m_default);
return;
}
std::string val(value);
std::string param;
auto parenPos = value.find('(');
if (std::string::npos != parenPos)
{
val = Parser::trim(value.substr(0, parenPos));
param = value.substr(parenPos + 1);
if (param.back() != ')')
{
throw ParsingError("invalid value: '" + value + "'");
}
param = Parser::trim(param.substr(0, param.length() - 1));
}
setValue(val);
try
{
m_param = getParamInfo().default;
if (!param.empty())
{
setParam(param);
}
}
catch (const ParsingError& e)
{
throw ParsingError(Parser::removeParam(getValueStr()) + ": " + e.what());
}
}
void Setting::set(const std::string& value, const std::string& source)
{
setValue("default" == value ? m_default : value);
set(value);
m_source = source;
}
void Setting::setParam(const std::string& param)
{
const auto paramInfo = getParamInfo();
if (paramInfo.name.empty())
{
throw ParsingError("parameters are not allowed");
}
try
{
m_param = Parser::parseInt(param, paramInfo.min, paramInfo.max);
}
catch (const ParsingError&)
{
throw ParsingError("invalid parameter value: '" + param + "'");
}
}
}

View File

@ -1,12 +1,22 @@
#pragma once
#include <string>
#include <vector>
namespace Config
{
class Setting
{
public:
struct ParamInfo
{
std::string name;
int min;
int max;
int default;
int current;
};
Setting(const std::string& name, const std::string& default);
Setting(const Setting&) = delete;
@ -14,18 +24,26 @@ namespace Config
Setting& operator=(const Setting&) = delete;
Setting& operator=(Setting&&) = delete;
virtual std::vector<std::string> getDefaultValueStrings() { return {}; }
virtual ParamInfo getParamInfo() const { return {}; }
virtual std::string getValueStr() const = 0;
const std::string& getName() const { return m_name; }
int getParam() const { return m_param; }
const std::string& getSource() const { return m_source; }
std::string getValueAsString() const { return getValueStr(); }
void reset();
void set(const std::string& value);
void set(const std::string& value, const std::string& source);
protected:
virtual std::string getValueStr() const = 0;
virtual void setValue(const std::string& value) = 0;
int m_param;
private:
void setParam(const std::string& param);
std::string m_name;
std::string m_default;
std::string m_source;

View File

@ -15,33 +15,16 @@ namespace Config
{"msaa4x", D3DDDIMULTISAMPLE_4_SAMPLES},
{"msaa8x", D3DDDIMULTISAMPLE_8_SAMPLES}
})
, m_param(0)
{
}
std::string Antialiasing::getParamStr() const
Setting::ParamInfo Antialiasing::getParamInfo() const
{
return D3DDDIMULTISAMPLE_NONE != m_value ? std::to_string(m_param) : std::string();
}
void Antialiasing::setDefaultParam(const UINT& value)
{
m_param = D3DDDIMULTISAMPLE_NONE != value ? 7 : 0;
}
void Antialiasing::setValue(const UINT& value, const std::string& param)
{
if (D3DDDIMULTISAMPLE_NONE != value)
if (D3DDDIMULTISAMPLE_NONE != m_value)
{
const UINT p = Config::Parser::parseUnsigned(param);
if (p <= 7)
{
m_value = value;
m_param = p;
return;
}
return { "Quality", 0, 7, 7, m_param };
}
throw ParsingError("invalid parameter: '" + param + "'");
return {};
}
}
}

View File

@ -10,15 +10,8 @@ namespace Config
{
public:
Antialiasing();
UINT getParam() const { return m_param; }
protected:
virtual std::string getParamStr() const override;
virtual void setDefaultParam(const UINT& value) override;
virtual void setValue(const UINT& value, const std::string& param) override;
UINT m_param;
virtual ParamInfo getParamInfo() const override;
};
}
}

View File

@ -59,11 +59,7 @@ namespace Config
unsigned result = 0;
for (const auto& value : values)
{
auto num = Parser::parseUnsigned(value);
if (num < 1 || num > 32)
{
throw ParsingError("'" + value + "' is not an integer between 1 and 32");
}
auto num = Parser::parseInt(value, 1, 32);
result |= 1U << (num - 1);
}

View File

@ -11,10 +11,11 @@ namespace Config
public:
CpuAffinity();
virtual std::string getValueStr() const override;
unsigned get() const { return m_value; }
private:
std::string getValueStr() const override;
void setValues(const std::vector<std::string>& values) override;
unsigned m_value;

View File

@ -2,37 +2,20 @@
namespace Config
{
namespace Settings
{
DisplayFilter::DisplayFilter()
: MappedSetting("DisplayFilter", "bilinear(0)", { {"point", POINT}, {"bilinear", BILINEAR} })
, m_param(0)
{
}
namespace Settings
{
DisplayFilter::DisplayFilter()
: MappedSetting("DisplayFilter", "bilinear", { {"point", POINT}, {"bilinear", BILINEAR} })
{
}
std::string DisplayFilter::getParamStr() const
{
return BILINEAR == m_value ? std::to_string(m_param) : std::string();
}
void DisplayFilter::setDefaultParam(const UINT& value)
{
m_param = BILINEAR == value ? 100 : 0;
}
void DisplayFilter::setValue(const UINT& value, const std::string& param)
{
if (BILINEAR == value)
{
const UINT p = Config::Parser::parseUnsigned(param);
if (p <= 100)
{
m_value = BILINEAR;
m_param = p;
return;
}
}
throw ParsingError("invalid parameter: '" + param + "'");
}
}
Setting::ParamInfo DisplayFilter::getParamInfo() const
{
if (BILINEAR == m_value)
{
return { "Blur", 0, 100, 0, m_param };
}
return {};
}
}
}

View File

@ -4,24 +4,17 @@
namespace Config
{
namespace Settings
{
class DisplayFilter : public MappedSetting<UINT>
{
public:
static const UINT POINT = 0;
static const UINT BILINEAR = 1;
namespace Settings
{
class DisplayFilter : public MappedSetting<UINT>
{
public:
static const UINT POINT = 0;
static const UINT BILINEAR = 1;
DisplayFilter();
DisplayFilter();
UINT getParam() const { return m_param; }
protected:
virtual std::string getParamStr() const override;
virtual void setDefaultParam(const UINT& value) override;
virtual void setValue(const UINT& value, const std::string& param) override;
UINT m_param;
};
}
virtual ParamInfo getParamInfo() const override;
};
}
}

View File

@ -17,10 +17,11 @@ namespace Config
SupportedResolutions();
virtual std::string getValueStr() const override;
std::set<SIZE> get() const { return m_resolutions; }
private:
std::string getValueStr() const override;
void setValues(const std::vector<std::string>& values) override;
std::set<SIZE> m_resolutions;

View File

@ -110,7 +110,7 @@ namespace D3dDdi
levels.Format = D3DDDIFMT_X8R8G8B8;
levels.MsType = static_cast<D3DDDIMULTISAMPLE_TYPE>(samples);
getCaps(D3DDDICAPS_GETMULTISAMPLEQUALITYLEVELS, levels);
return { levels.MsType, min(Config::antialiasing.getParam(), levels.QualityLevels - 1) };
return { levels.MsType, min(static_cast<UINT>(Config::antialiasing.getParam()), levels.QualityLevels - 1) };
}
std::string Adapter::getSupportedMsaaModes(const std::map<D3DDDIFORMAT, FORMATOP>& formatOps) const

View File

@ -12,6 +12,7 @@
#include <D3dDdi/DeviceFuncs.h>
#include <D3dDdi/Resource.h>
#include <D3dDdi/ScopedCriticalSection.h>
#include <DDraw/ScopedThreadLock.h>
namespace
{
@ -335,6 +336,25 @@ namespace D3dDdi
return m_origVtable.pfnUnlock(m_device, data);
}
void Device::updateAllConfig()
{
DDraw::ScopedThreadLock ddLock;
D3dDdi::ScopedCriticalSection lock;
for (auto& device : s_devices)
{
device.second.updateConfig();
}
}
void Device::updateConfig()
{
for (auto& resource : m_resources)
{
resource.second->updateConfig();
}
m_state.updateConfig();
}
std::map<HANDLE, Device> Device::s_devices;
bool Device::s_isFlushEnabled = true;
}

View File

@ -59,6 +59,7 @@ namespace D3dDdi
void prepareForRendering(HANDLE resource, UINT subResourceIndex);
void prepareForRendering();
void setRenderTarget(const D3DDDIARG_SETRENDERTARGET& data);
void updateConfig();
static void add(Adapter& adapter, HANDLE device);
static Device& get(HANDLE device) { return s_devices.find(device)->second; }
@ -67,6 +68,7 @@ namespace D3dDdi
static Resource* findResource(HANDLE resource);
static Resource* getGdiResource();
static void setGdiResourceHandle(HANDLE resource);
static void updateAllConfig();
private:
D3DDDI_DEVICEFUNCS m_origVtable;

View File

@ -6,6 +6,27 @@
#include <D3dDdi/Log/DeviceFuncsLog.h>
#include <D3dDdi/Resource.h>
namespace
{
UINT mapTssValue(D3DDDITEXTURESTAGESTATETYPE type, UINT value)
{
switch (type)
{
case D3DDDITSS_MAGFILTER:
case D3DDDITSS_MINFILTER:
return D3DTEXF_NONE == Config::textureFilter.getFilter() ? value : Config::textureFilter.getFilter();
case D3DDDITSS_MIPFILTER:
return D3DTEXF_NONE == Config::textureFilter.getMipFilter() ? value : Config::textureFilter.getMipFilter();
case D3DDDITSS_MAXANISOTROPY:
return D3DTEXF_NONE == Config::textureFilter.getFilter() ? value : Config::textureFilter.getMaxAnisotropy();
}
return value;
}
}
namespace D3dDdi
{
DeviceState::DeviceState(Device& device)
@ -118,21 +139,12 @@ namespace D3dDdi
m_textureStageState[i][D3DDDITSS_ADDRESSU] = D3DTADDRESS_WRAP;
m_textureStageState[i][D3DDDITSS_ADDRESSV] = D3DTADDRESS_WRAP;
m_textureStageState[i][D3DDDITSS_BORDERCOLOR] = 0;
if (D3DTEXF_NONE == Config::textureFilter.getFilter())
{
m_textureStageState[i][D3DDDITSS_MAGFILTER] = D3DTEXF_POINT;
m_textureStageState[i][D3DDDITSS_MINFILTER] = D3DTEXF_POINT;
m_textureStageState[i][D3DDDITSS_MIPFILTER] = D3DTEXF_NONE;
}
else
{
m_textureStageState[i][D3DDDITSS_MAGFILTER] = Config::textureFilter.getFilter();
m_textureStageState[i][D3DDDITSS_MINFILTER] = Config::textureFilter.getFilter();
m_textureStageState[i][D3DDDITSS_MIPFILTER] = Config::textureFilter.getMipFilter();
}
m_textureStageState[i][D3DDDITSS_MAGFILTER] = mapTssValue(D3DDDITSS_MAGFILTER, D3DTEXF_POINT);
m_textureStageState[i][D3DDDITSS_MINFILTER] = mapTssValue(D3DDDITSS_MINFILTER, D3DTEXF_POINT);
m_textureStageState[i][D3DDDITSS_MIPFILTER] = mapTssValue(D3DDDITSS_MIPFILTER, D3DTEXF_NONE);
m_textureStageState[i][D3DDDITSS_MIPMAPLODBIAS] = 0;
m_textureStageState[i][D3DDDITSS_MAXMIPLEVEL] = 0;
m_textureStageState[i][D3DDDITSS_MAXANISOTROPY] = Config::textureFilter.getMaxAnisotropy();
m_textureStageState[i][D3DDDITSS_MAXANISOTROPY] = mapTssValue(D3DDDITSS_MAXANISOTROPY, 1);
m_textureStageState[i][D3DDDITSS_TEXTURETRANSFORMFLAGS] = D3DTTFF_DISABLE;
m_textureStageState[i][D3DDDITSS_SRGBTEXTURE] = FALSE;
m_textureStageState[i][D3DDDITSS_ADDRESSW] = D3DTADDRESS_WRAP;
@ -434,6 +446,10 @@ namespace D3dDdi
{
m_renderTarget = {};
}
else if (m_depthStencil.hZBuffer == resource)
{
m_depthStencil = {};
}
}
HRESULT DeviceState::setShader(HANDLE shader, HANDLE& currentShader,
@ -519,6 +535,40 @@ namespace D3dDdi
return result;
}
void DeviceState::updateConfig()
{
if (m_renderTarget.hRenderTarget)
{
auto renderTarget = m_renderTarget;
m_renderTarget = {};
pfnSetRenderTarget(&renderTarget);
}
if (m_depthStencil.hZBuffer)
{
auto depthStencil = m_depthStencil;
m_depthStencil = {};
pfnSetDepthStencil(&depthStencil);
}
for (UINT i = 0; i < m_textureStageState.size(); ++i)
{
updateTextureStageState(i, D3DDDITSS_MAGFILTER);
updateTextureStageState(i, D3DDDITSS_MINFILTER);
updateTextureStageState(i, D3DDDITSS_MIPFILTER);
updateTextureStageState(i, D3DDDITSS_MAXANISOTROPY);
}
}
void DeviceState::updateTextureStageState(UINT stage, D3DDDITEXTURESTAGESTATETYPE state)
{
D3DDDIARG_TEXTURESTAGESTATE data = {};
data.Stage = stage;
data.State = state;
data.Value = mapTssValue(state, m_textureStageState[stage][state]);
m_device.getOrigVtable().pfnSetTextureStageState(m_device, &data);
}
DeviceState::ScopedRenderState::ScopedRenderState(DeviceState& deviceState, const D3DDDIARG_RENDERSTATE& data)
: m_deviceState(deviceState)
, m_prevData{ data.State, deviceState.m_renderState[data.State] }
@ -576,7 +626,7 @@ namespace D3dDdi
DeviceState::ScopedTextureStageState::ScopedTextureStageState(
DeviceState& deviceState, const D3DDDIARG_TEXTURESTAGESTATE& data)
: m_deviceState(deviceState)
, m_prevData{ data.Stage, data.State, deviceState.m_textureStageState[data.Stage][data.State] }
, m_prevData{ data.Stage, data.State, mapTssValue(data.State, deviceState.m_textureStageState[data.Stage][data.State]) }
{
m_deviceState.m_device.getOrigVtable().pfnSetTextureStageState(m_deviceState.m_device, &data);
}

View File

@ -49,6 +49,7 @@ namespace D3dDdi
HRESULT pfnUpdateWInfo(const D3DDDIARG_WINFO* data);
void onDestroyResource(HANDLE resource);
void updateConfig();
private:
HRESULT deleteShader(HANDLE shader, HANDLE& currentShader,
@ -69,6 +70,8 @@ namespace D3dDdi
HRESULT setStateArray(const StateData* data, std::array<UINT, size>& currentState,
HRESULT(APIENTRY* origSetState)(HANDLE, const StateData*));
void updateTextureStageState(UINT stage, D3DDDITEXTURESTAGESTATETYPE state);
Device& m_device;
D3DDDIARG_SETDEPTHSTENCIL m_depthStencil;
HANDLE m_pixelShader;

View File

@ -24,8 +24,7 @@ namespace
const UINT g_resourceTypeFlags = getResourceTypeFlags().Value;
RECT g_presentationRect = {};
UINT g_presentationFilter = Config::Settings::DisplayFilter::POINT;
UINT g_presentationFilterParam = 0;
RECT g_primaryRect = {};
D3DDDIFORMAT g_formatOverride = D3DDDIFMT_UNKNOWN;
RECT calculatePresentationRect()
@ -108,6 +107,8 @@ namespace D3dDdi
, m_lockBuffer(nullptr, &heapFree)
, m_lockResource(nullptr, ResourceDeleter(device))
, m_customSurface{}
, m_multiSampleConfig{ D3DDDIMULTISAMPLE_NONE, 0 }
, m_isSurfaceRepoResource(SurfaceRepository::inCreateSurface())
{
if (m_origData.Flags.VertexBuffer &&
m_origData.Flags.MightDrawFromLocked &&
@ -120,33 +121,14 @@ namespace D3dDdi
{
g_presentationRect = calculatePresentationRect();
auto& si = m_origData.pSurfList[0];
RECT rect = { 0, 0, static_cast<LONG>(si.Width), static_cast<LONG>(si.Height) };
g_primaryRect = { 0, 0, static_cast<LONG>(si.Width), static_cast<LONG>(si.Height) };
Gdi::Cursor::setMonitorClipRect(DDraw::PrimarySurface::getMonitorRect());
if (!EqualRect(&g_presentationRect, &rect))
if (!EqualRect(&g_presentationRect, &g_primaryRect))
{
Gdi::Cursor::setEmulated(true);
}
Gdi::VirtualScreen::setFullscreenMode(true);
if (g_presentationRect.right - g_presentationRect.left != rect.right ||
g_presentationRect.bottom - g_presentationRect.top != rect.bottom)
{
g_presentationFilter = Config::displayFilter.get();
g_presentationFilterParam = Config::displayFilter.getParam();
if (Config::Settings::DisplayFilter::BILINEAR == g_presentationFilter &&
0 == g_presentationFilterParam &&
0 == (g_presentationRect.right - g_presentationRect.left) % rect.right &&
0 == (g_presentationRect.bottom - g_presentationRect.top) % rect.bottom)
{
g_presentationFilter = Config::Settings::DisplayFilter::POINT;
}
}
else
{
g_presentationFilter = Config::Settings::DisplayFilter::POINT;
}
}
fixResourceData();
@ -159,34 +141,7 @@ namespace D3dDdi
}
m_handle = m_fixedData.hResource;
if (!SurfaceRepository::inCreateSurface())
{
const auto msaa = getMultisampleConfig();
if (D3DDDIMULTISAMPLE_NONE != msaa.first)
{
g_formatOverride = m_fixedData.Format;
if (m_fixedData.Flags.ZBuffer)
{
DDPIXELFORMAT pf = {};
pf.dwSize = sizeof(pf);
pf.dwFlags = DDPF_ZBUFFER;
pf.dwZBufferBitDepth = 16;
pf.dwZBitMask = 0xFFFF;
SurfaceRepository::get(m_device.getAdapter()).getSurface(m_customSurface,
m_fixedData.surfaceData[0].Width, m_fixedData.surfaceData[0].Height, pf,
DDSCAPS_ZBUFFER | DDSCAPS_VIDEOMEMORY, m_fixedData.SurfCount);
}
else
{
SurfaceRepository::get(m_device.getAdapter()).getSurface(m_customSurface,
m_fixedData.surfaceData[0].Width, m_fixedData.surfaceData[0].Height,
DDraw::DirectDraw::getRgbPixelFormat(32),
DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY);
}
g_formatOverride = D3DDDIFMT_UNKNOWN;
}
}
updateConfig();
if (D3DDDIPOOL_SYSTEMMEM == m_fixedData.Pool &&
0 != m_formatInfo.bytesPerPixel)
@ -675,8 +630,20 @@ namespace D3dDdi
const RECT monitorRect = DDraw::PrimarySurface::getMonitorRect();
const bool isLayeredPresentNeeded = Gdi::Window::presentLayered(nullptr, monitorRect);
UINT presentationFilter = Config::displayFilter.get();
UINT presentationFilterParam = Config::displayFilter.getParam();
if (Config::Settings::DisplayFilter::BILINEAR == presentationFilter &&
(g_presentationRect.right - g_presentationRect.left == g_primaryRect.right &&
g_presentationRect.bottom - g_presentationRect.top == g_primaryRect.bottom) ||
(0 == presentationFilterParam &&
0 == (g_presentationRect.right - g_presentationRect.left) % g_primaryRect.right &&
0 == (g_presentationRect.bottom - g_presentationRect.top) % g_primaryRect.bottom))
{
presentationFilter = Config::Settings::DisplayFilter::POINT;
}
if (isPalettized || isCursorEmulated || isLayeredPresentNeeded ||
Config::Settings::DisplayFilter::POINT != g_presentationFilter)
Config::Settings::DisplayFilter::POINT != presentationFilter)
{
const auto& si = srcResource->m_fixedData.pSurfList[0];
const auto& dst(SurfaceRepository::get(m_device.getAdapter()).getRenderTarget(si.Width, si.Height));
@ -719,10 +686,10 @@ namespace D3dDdi
m_device.getShaderBlitter().cursorBlt(*dst.resource, 0, cursorInfo.hCursor, pos);
}
if (Config::Settings::DisplayFilter::BILINEAR == g_presentationFilter)
if (Config::Settings::DisplayFilter::BILINEAR == presentationFilter)
{
m_device.getShaderBlitter().genBilinearBlt(*this, data.DstSubResourceIndex, g_presentationRect,
*dst.resource, data.SrcRect, g_presentationFilterParam);
*dst.resource, data.SrcRect, presentationFilterParam);
return S_OK;
}
@ -948,4 +915,59 @@ namespace D3dDdi
return m_device.getOrigVtable().pfnUnlock(m_device, &data);
}
void Resource::updateConfig()
{
if (m_isSurfaceRepoResource)
{
return;
}
const auto msaa = getMultisampleConfig();
if (m_multiSampleConfig == msaa)
{
return;
}
m_multiSampleConfig = msaa;
if (m_customSurface.resource && m_fixedData.Flags.RenderTarget)
{
for (UINT i = 0; i < m_lockData.size(); ++i)
{
if (m_lockData[i].isCustomUpToDate && !m_lockData[i].isVidMemUpToDate)
{
copyToVidMem(i);
}
m_lockData[i].isCustomUpToDate = false;
}
}
auto& surfaceRepo(SurfaceRepository::get(m_device.getAdapter()));
surfaceRepo.release(m_customSurface);
if (D3DDDIMULTISAMPLE_NONE != msaa.first)
{
g_formatOverride = m_fixedData.Format;
if (m_fixedData.Flags.ZBuffer)
{
DDPIXELFORMAT pf = {};
pf.dwSize = sizeof(pf);
pf.dwFlags = DDPF_ZBUFFER;
pf.dwZBufferBitDepth = 16;
pf.dwZBitMask = 0xFFFF;
SurfaceRepository::get(m_device.getAdapter()).getSurface(m_customSurface,
m_fixedData.surfaceData[0].Width, m_fixedData.surfaceData[0].Height, pf,
DDSCAPS_ZBUFFER | DDSCAPS_VIDEOMEMORY, m_fixedData.SurfCount);
}
else
{
SurfaceRepository::get(m_device.getAdapter()).getSurface(m_customSurface,
m_fixedData.surfaceData[0].Width, m_fixedData.surfaceData[0].Height,
DDraw::DirectDraw::getRgbPixelFormat(32),
DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY);
}
g_formatOverride = D3DDDIFMT_UNKNOWN;
}
}
}

View File

@ -38,6 +38,7 @@ namespace D3dDdi
void prepareForRendering(UINT subResourceIndex);
void setAsGdiResource(bool isGdiResource);
HRESULT unlock(const D3DDDIARG_UNLOCK& data);
void updateConfig();
private:
class Data : public D3DDDIARG_CREATERESOURCE2
@ -106,5 +107,7 @@ namespace D3dDdi
std::vector<LockData> m_lockData;
std::unique_ptr<void, ResourceDeleter> m_lockResource;
SurfaceRepository::Surface m_customSurface;
std::pair<D3DDDIMULTISAMPLE_TYPE, UINT> m_multiSampleConfig;
bool m_isSurfaceRepoResource;
};
}

View File

@ -39,6 +39,7 @@ namespace D3dDdi
const Surface& getRenderTarget(DWORD width, DWORD height);
Surface& getSurface(Surface& surface, DWORD width, DWORD height,
const DDPIXELFORMAT& pf, DWORD caps, UINT surfaceCount = 1);
void release(Surface& surface);
static SurfaceRepository& get(const Adapter& adapter);
static bool inCreateSurface() { return s_inCreateSurface; }
@ -52,7 +53,6 @@ namespace D3dDdi
Resource* getInitializedResource(Surface& surface, DWORD width, DWORD height, const DDPIXELFORMAT& pf, DWORD caps,
std::function<void(const DDSURFACEDESC2&)> initFunc);
bool isLost(Surface& surface);
void release(Surface& surface);
const Adapter& m_adapter;
HCURSOR m_cursor;

View File

@ -36,3 +36,5 @@ FILESUBTYPE VFT2_UNKNOWN
VALUE "Translation", 0x409, 0
}
}
BMP_ARROW BITMAP "arrow.bmp"

View File

@ -306,10 +306,18 @@
<ClInclude Include="Gdi\VirtualScreen.h" />
<ClInclude Include="Gdi\Window.h" />
<ClInclude Include="Gdi\WinProc.h" />
<ClInclude Include="Input\Input.h" />
<ClInclude Include="Overlay\ComboBoxControl.h" />
<ClInclude Include="Overlay\ComboBoxDropDown.h" />
<ClInclude Include="Overlay\ConfigWindow.h" />
<ClInclude Include="Overlay\Control.h" />
<ClInclude Include="Overlay\LabelControl.h" />
<ClInclude Include="Overlay\ScrollBarControl.h" />
<ClInclude Include="Overlay\SettingControl.h" />
<ClInclude Include="Overlay\Window.h" />
<ClInclude Include="Win32\DisplayMode.h" />
<ClInclude Include="Win32\Log.h" />
<ClInclude Include="Win32\MemoryManagement.h" />
<ClInclude Include="Win32\MsgHooks.h" />
<ClInclude Include="Win32\Registry.h" />
<ClInclude Include="Win32\Thread.h" />
</ItemGroup>
@ -395,10 +403,18 @@
<ClCompile Include="Gdi\VirtualScreen.cpp" />
<ClCompile Include="Gdi\Window.cpp" />
<ClCompile Include="Gdi\WinProc.cpp" />
<ClCompile Include="Input\Input.cpp" />
<ClCompile Include="Overlay\ComboBoxControl.cpp" />
<ClCompile Include="Overlay\ComboBoxDropDown.cpp" />
<ClCompile Include="Overlay\ConfigWindow.cpp" />
<ClCompile Include="Overlay\Control.cpp" />
<ClCompile Include="Overlay\LabelControl.cpp" />
<ClCompile Include="Overlay\ScrollBarControl.cpp" />
<ClCompile Include="Overlay\SettingControl.cpp" />
<ClCompile Include="Overlay\Window.cpp" />
<ClCompile Include="Win32\DisplayMode.cpp" />
<ClCompile Include="Win32\Log.cpp" />
<ClCompile Include="Win32\MemoryManagement.cpp" />
<ClCompile Include="Win32\MsgHooks.cpp" />
<ClCompile Include="Win32\Registry.cpp" />
<ClCompile Include="Win32\Thread.cpp" />
</ItemGroup>
@ -414,6 +430,9 @@
<FxCompile Include="Shaders\PaletteLookup.hlsl" />
<FxCompile Include="Shaders\TextureSampler.hlsl" />
</ItemGroup>
<ItemGroup>
<Image Include="arrow.bmp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View File

@ -91,6 +91,18 @@
<Filter Include="Shaders">
<UniqueIdentifier>{00e06bd4-3f10-46dc-af0f-99834dfac835}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Input">
<UniqueIdentifier>{32765547-b1b7-48d3-bf58-d09cf388de91}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Input">
<UniqueIdentifier>{38167700-fb70-4ee8-b149-824a9fde51eb}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Overlay">
<UniqueIdentifier>{29dba6e7-0bca-44e2-9776-2ba478e088bc}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Overlay">
<UniqueIdentifier>{e0a8a3ac-59fd-4cb0-ae81-5ae2c56fbfac}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Gdi\Gdi.h">
@ -222,9 +234,6 @@
<ClInclude Include="Direct3d\Visitors\Direct3dVtblVisitor.h">
<Filter>Header Files\Direct3d\Visitors</Filter>
</ClInclude>
<ClInclude Include="Win32\MsgHooks.h">
<Filter>Header Files\Win32</Filter>
</ClInclude>
<ClInclude Include="Win32\Registry.h">
<Filter>Header Files\Win32</Filter>
</ClInclude>
@ -456,6 +465,33 @@
<ClInclude Include="Config\Settings\Antialiasing.h">
<Filter>Header Files\Config\Settings</Filter>
</ClInclude>
<ClInclude Include="Input\Input.h">
<Filter>Header Files\Input</Filter>
</ClInclude>
<ClInclude Include="Overlay\Window.h">
<Filter>Header Files\Overlay</Filter>
</ClInclude>
<ClInclude Include="Overlay\Control.h">
<Filter>Header Files\Overlay</Filter>
</ClInclude>
<ClInclude Include="Overlay\ConfigWindow.h">
<Filter>Header Files\Overlay</Filter>
</ClInclude>
<ClInclude Include="Overlay\SettingControl.h">
<Filter>Header Files\Overlay</Filter>
</ClInclude>
<ClInclude Include="Overlay\LabelControl.h">
<Filter>Header Files\Overlay</Filter>
</ClInclude>
<ClInclude Include="Overlay\ComboBoxControl.h">
<Filter>Header Files\Overlay</Filter>
</ClInclude>
<ClInclude Include="Overlay\ScrollBarControl.h">
<Filter>Header Files\Overlay</Filter>
</ClInclude>
<ClInclude Include="Overlay\ComboBoxDropDown.h">
<Filter>Header Files\Overlay</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Gdi\Gdi.cpp">
@ -533,9 +569,6 @@
<ClCompile Include="Common\Log.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="Win32\MsgHooks.cpp">
<Filter>Source Files\Win32</Filter>
</ClCompile>
<ClCompile Include="Win32\Registry.cpp">
<Filter>Source Files\Win32</Filter>
</ClCompile>
@ -719,6 +752,33 @@
<ClCompile Include="Config\Settings\Antialiasing.cpp">
<Filter>Source Files\Config\Settings</Filter>
</ClCompile>
<ClCompile Include="Input\Input.cpp">
<Filter>Source Files\Input</Filter>
</ClCompile>
<ClCompile Include="Overlay\Window.cpp">
<Filter>Source Files\Overlay</Filter>
</ClCompile>
<ClCompile Include="Overlay\Control.cpp">
<Filter>Source Files\Overlay</Filter>
</ClCompile>
<ClCompile Include="Overlay\ConfigWindow.cpp">
<Filter>Source Files\Overlay</Filter>
</ClCompile>
<ClCompile Include="Overlay\SettingControl.cpp">
<Filter>Source Files\Overlay</Filter>
</ClCompile>
<ClCompile Include="Overlay\LabelControl.cpp">
<Filter>Source Files\Overlay</Filter>
</ClCompile>
<ClCompile Include="Overlay\ComboBoxControl.cpp">
<Filter>Source Files\Overlay</Filter>
</ClCompile>
<ClCompile Include="Overlay\ScrollBarControl.cpp">
<Filter>Source Files\Overlay</Filter>
</ClCompile>
<ClCompile Include="Overlay\ComboBoxDropDown.cpp">
<Filter>Source Files\Overlay</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="DDrawCompat.rc">
@ -744,4 +804,9 @@
<Filter>Shaders</Filter>
</FxCompile>
</ItemGroup>
<ItemGroup>
<Image Include="arrow.bmp">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
</Project>

View File

@ -19,9 +19,9 @@
#include <Gdi/Gdi.h>
#include <Gdi/PresentationWindow.h>
#include <Gdi/VirtualScreen.h>
#include <Input/Input.h>
#include <Win32/DisplayMode.h>
#include <Win32/MemoryManagement.h>
#include <Win32/MsgHooks.h>
#include <Win32/Registry.h>
#include <Win32/Thread.h>
@ -227,8 +227,8 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
VISIT_PUBLIC_DDRAW_PROCS(HOOK_DDRAW_PROC);
Input::installHooks();
Win32::MemoryManagement::installHooks();
Win32::MsgHooks::installHooks();
Win32::Thread::installHooks();
Compat::closeDbgEng();

View File

@ -4,6 +4,7 @@
#include <Dll/Dll.h>
#include <Gdi/PresentationWindow.h>
#include <Gdi/WinProc.h>
#include <Overlay/ConfigWindow.h>
#include <Win32/DisplayMode.h>
namespace
@ -14,6 +15,7 @@ namespace
HANDLE g_presentationWindowThread = nullptr;
unsigned g_presentationWindowThreadId = 0;
Overlay::ConfigWindow* g_configWindow = nullptr;
HWND g_messageWindow = nullptr;
bool g_isThreadReady = false;
@ -65,6 +67,10 @@ namespace
if (presentationWindow)
{
if (lParam)
{
CALL_ORIG_FUNC(SetWindowLongA)(presentationWindow, GWL_WNDPROC, lParam);
}
CALL_ORIG_FUNC(SetLayeredWindowAttributes)(presentationWindow, 0, 255, LWA_ALPHA);
}
@ -131,6 +137,9 @@ namespace
Compat::closeDbgEng();
Overlay::ConfigWindow configWindow;
g_configWindow = &configWindow;
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0))
{
@ -153,10 +162,10 @@ namespace Gdi
{
namespace PresentationWindow
{
HWND create(HWND owner)
HWND create(HWND owner, WNDPROC wndProc)
{
return reinterpret_cast<HWND>(sendMessageBlocking(
g_messageWindow, WM_CREATEPRESENTATIONWINDOW, reinterpret_cast<WPARAM>(owner), 0));
return reinterpret_cast<HWND>(sendMessageBlocking(g_messageWindow, WM_CREATEPRESENTATIONWINDOW,
reinterpret_cast<WPARAM>(owner), reinterpret_cast<LPARAM>(wndProc)));
}
void destroy(HWND hwnd)
@ -164,6 +173,11 @@ namespace Gdi
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
Overlay::ConfigWindow* getConfigWindow()
{
return g_configWindow;
}
void installHooks()
{
WNDCLASS wc = {};

View File

@ -2,12 +2,18 @@
#include <Windows.h>
namespace Overlay
{
class ConfigWindow;
}
namespace Gdi
{
namespace PresentationWindow
{
HWND create(HWND owner);
HWND create(HWND owner, WNDPROC wndProc = nullptr);
void destroy(HWND hwnd);
Overlay::ConfigWindow* getConfigWindow();
bool isPresentationWindow(HWND hwnd);
bool isThreadReady();
void setWindowPos(HWND hwnd, const WINDOWPOS& wp);

View File

@ -18,6 +18,7 @@
#include <Gdi/TitleBar.h>
#include <Gdi/Window.h>
#include <Gdi/WinProc.h>
#include <Overlay/ConfigWindow.h>
#include <Win32/DisplayMode.h>
namespace
@ -53,6 +54,17 @@ namespace
switch (uMsg)
{
case WM_ACTIVATEAPP:
if (!wParam)
{
auto configWindow = Gdi::PresentationWindow::getConfigWindow();
if (configWindow)
{
configWindow->setVisible(false);
}
}
break;
case WM_DISPLAYCHANGE:
{
if (0 != wParam)

View File

@ -12,6 +12,8 @@
#include <Gdi/PresentationWindow.h>
#include <Gdi/VirtualScreen.h>
#include <Gdi/Window.h>
#include <Input/Input.h>
#include <Overlay/ConfigWindow.h>
namespace
{
@ -96,6 +98,64 @@ namespace
return rgn;
}
void presentLayeredWindow(CompatWeakPtr<IDirectDrawSurface7> dst,
HWND hwnd, RECT wr, const RECT& monitorRect, HDC& dstDc, Gdi::Region* rgn = nullptr, bool isMenu = false)
{
if (!dst)
{
throw true;
}
if (!dstDc)
{
dst->GetDC(dst, &dstDc);
if (!dstDc)
{
throw false;
}
}
OffsetRect(&wr, -monitorRect.left, -monitorRect.top);
if (rgn)
{
rgn->offset(-monitorRect.left, -monitorRect.top);
}
HDC windowDc = GetWindowDC(hwnd);
if (rgn)
{
SelectClipRgn(dstDc, *rgn);
}
COLORREF colorKey = 0;
BYTE alpha = 255;
DWORD flags = ULW_ALPHA;
if (isMenu || CALL_ORIG_FUNC(GetLayeredWindowAttributes)(hwnd, &colorKey, &alpha, &flags))
{
if (flags & LWA_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 = 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)(hwnd, windowDc);
}
void presentOverlayWindow(CompatWeakPtr<IDirectDrawSurface7> dst, HWND hwnd, const RECT& monitorRect, HDC& dstDc)
{
RECT wr = {};
GetWindowRect(hwnd, &wr);
presentLayeredWindow(dst, hwnd, wr, monitorRect, dstDc);
}
void updatePosition(Window& window, const RECT& oldWindowRect, const RECT& oldClientRect,
const Gdi::Region& oldVisibleRegion, Gdi::Region& invalidatedRegion)
{
@ -451,64 +511,47 @@ namespace Gdi
bool presentLayered(CompatWeakPtr<IDirectDrawSurface7> dst, const RECT& monitorRect)
{
HDC dstDc = nullptr;
bool result = false;
for (auto it = g_windowZOrder.rbegin(); it != g_windowZOrder.rend(); ++it)
try
{
auto& window = **it;
if (!window.isLayered || window.visibleRegion.isEmpty())
for (auto it = g_windowZOrder.rbegin(); it != g_windowZOrder.rend(); ++it)
{
continue;
}
Gdi::Region rgn(window.visibleRegion);
rgn &= monitorRect;
if (rgn.isEmpty())
{
continue;
}
RECT wr = window.windowRect;
OffsetRect(&wr, -monitorRect.left, -monitorRect.top);
rgn.offset(-monitorRect.left, -monitorRect.top);
if (!dst)
{
return true;
}
if (!dstDc)
{
dst->GetDC(dst, &dstDc);
if (!dstDc)
auto& window = **it;
if (!window.isLayered || window.visibleRegion.isEmpty())
{
return false;
continue;
}
Gdi::Region rgn(window.visibleRegion);
rgn &= monitorRect;
if (rgn.isEmpty())
{
continue;
}
presentLayeredWindow(dst, window.hwnd, window.windowRect, monitorRect, dstDc, &rgn, window.isMenu);
}
auto configWindow = PresentationWindow::getConfigWindow();
if (configWindow && configWindow->isVisible())
{
presentOverlayWindow(dst, configWindow->getWindow(), monitorRect, dstDc);
auto capture = Input::getCapture();
if (capture && capture != configWindow)
{
presentOverlayWindow(dst, capture->getWindow(), monitorRect, dstDc);
}
}
result = true;
HDC windowDc = GetWindowDC(window.hwnd);
SelectClipRgn(dstDc, rgn);
COLORREF colorKey = 0;
BYTE alpha = 255;
DWORD flags = ULW_ALPHA;
if (window.isMenu || CALL_ORIG_FUNC(GetLayeredWindowAttributes)(window.hwnd, &colorKey, &alpha, &flags))
HWND cursorWindow = Input::getCursorWindow();
if (cursorWindow)
{
if (flags & LWA_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 = 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);
}
presentOverlayWindow(dst, cursorWindow, monitorRect, dstDc);
}
CALL_ORIG_FUNC(ReleaseDC)(window.hwnd, windowDc);
}
catch (bool result)
{
return result;
}
if (dstDc)
@ -516,7 +559,8 @@ namespace Gdi
SelectClipRgn(dstDc, nullptr);
dst->ReleaseDC(dst, dstDc);
}
return result;
return false;
}
void updateAll()

283
DDrawCompat/Input/Input.cpp Normal file
View File

@ -0,0 +1,283 @@
#include <algorithm>
#include <map>
#include <tuple>
#include <Windows.h>
#include <hidusage.h>
#include <Common/Hook.h>
#include <Common/Log.h>
#include <Dll/Dll.h>
#include <DDraw/RealPrimarySurface.h>
#include <Gdi/PresentationWindow.h>
#include <Input/Input.h>
#include <Overlay/Window.h>
namespace
{
struct HotKeyData
{
std::function<void(void*)> action;
void* context;
};
const UINT WM_USER_HOTKEY = WM_USER;
const UINT WM_USER_RESET_HOOK = WM_USER + 1;
HANDLE g_bmpArrow = nullptr;
SIZE g_bmpArrowSize = {};
Overlay::Window* g_capture = nullptr;
POINT g_cursorPos = {};
HWND g_cursorWindow = nullptr;
std::map<Input::HotKey, HotKeyData> g_hotKeys;
RECT g_monitorRect = {};
DWORD g_inputThreadId = 0;
HHOOK g_keyboardHook = nullptr;
HHOOK g_mouseHook = nullptr;
LRESULT CALLBACK lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK lowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam);
void setCursorPos(POINT cp);
LRESULT CALLBACK cursorWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LOG_FUNC("cursorWindowProc", Compat::WindowMessageStruct(hwnd, uMsg, wParam, lParam));
switch (uMsg)
{
case WM_PAINT:
{
PAINTSTRUCT ps = {};
BeginPaint(hwnd, &ps);
HDC dc = CreateCompatibleDC(nullptr);
HGDIOBJ origBmp = SelectObject(dc, g_bmpArrow);
CALL_ORIG_FUNC(BitBlt)(ps.hdc, 0, 0, g_bmpArrowSize.cx, g_bmpArrowSize.cy, dc, 0, 0, SRCCOPY);
SelectObject(dc, origBmp);
DeleteDC(dc);
EndPaint(hwnd, &ps);
return 0;
}
case WM_WINDOWPOSCHANGED:
DDraw::RealPrimarySurface::scheduleUpdate();
break;
}
return CALL_ORIG_FUNC(DefWindowProcA)(hwnd, uMsg, wParam, lParam);
}
unsigned WINAPI inputThreadProc(LPVOID /*lpParameter*/)
{
g_inputThreadId = GetCurrentThreadId();
g_bmpArrow = CALL_ORIG_FUNC(LoadImageA)(Dll::g_currentModule, "BMP_ARROW", IMAGE_BITMAP, 0, 0, 0);
BITMAP bm = {};
GetObject(g_bmpArrow, sizeof(bm), &bm);
g_bmpArrowSize = { bm.bmWidth, bm.bmHeight };
g_keyboardHook = CALL_ORIG_FUNC(SetWindowsHookExA)(WH_KEYBOARD_LL, &lowLevelKeyboardProc, Dll::g_currentModule, 0);
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0))
{
if (msg.message == WM_TIMER && !msg.hwnd && msg.lParam)
{
reinterpret_cast<TIMERPROC>(msg.lParam)(nullptr, WM_TIMER, msg.wParam, 0);
}
if (!msg.hwnd)
{
if (WM_USER_HOTKEY == msg.message)
{
DWORD pid = 0;
GetWindowThreadProcessId(GetForegroundWindow(), &pid);
if (GetCurrentProcessId() == pid)
{
auto it = std::find_if(g_hotKeys.begin(), g_hotKeys.end(),
[&](const auto& v) { return v.first.vk == msg.wParam; });
if (it != g_hotKeys.end())
{
it->second.action(it->second.context);
}
}
}
else if (WM_USER_RESET_HOOK == msg.message)
{
if (msg.wParam == WH_KEYBOARD_LL)
{
UnhookWindowsHookEx(g_keyboardHook);
g_keyboardHook = CALL_ORIG_FUNC(SetWindowsHookExA)(
WH_KEYBOARD_LL, &lowLevelKeyboardProc, Dll::g_currentModule, 0);
}
else if (msg.wParam == WH_MOUSE_LL && g_mouseHook)
{
UnhookWindowsHookEx(g_mouseHook);
g_mouseHook = CALL_ORIG_FUNC(SetWindowsHookExA)(
WH_MOUSE_LL, &lowLevelMouseProc, Dll::g_currentModule, 0);
}
}
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
LRESULT CALLBACK lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (HC_ACTION == nCode && (WM_KEYDOWN == wParam || WM_SYSKEYDOWN == wParam))
{
auto llHook = reinterpret_cast<const KBDLLHOOKSTRUCT*>(lParam);
PostThreadMessage(GetCurrentThreadId(), WM_USER_HOTKEY, llHook->vkCode, llHook->scanCode);
}
return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
LRESULT CALLBACK lowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (HC_ACTION == nCode)
{
POINT cp = g_cursorPos;
POINT origCp = {};
GetCursorPos(&origCp);
auto& llHook = *reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
cp.x += (llHook.pt.x - origCp.x);
cp.y += (llHook.pt.y - origCp.y);
cp.x = min(max(g_monitorRect.left, cp.x), g_monitorRect.right);
cp.y = min(max(g_monitorRect.top, cp.y), g_monitorRect.bottom);
setCursorPos(cp);
RECT r = g_capture->getRect();
cp.x -= r.left;
cp.y -= r.top;
switch (wParam)
{
case WM_LBUTTONDOWN:
g_capture->onLButtonDown(cp);
break;
case WM_LBUTTONUP:
g_capture->onLButtonUp(cp);
break;
case WM_MOUSEMOVE:
g_capture->onMouseMove(cp);
break;
}
return 1;
}
return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
void setCursorPos(POINT cp)
{
g_cursorPos = cp;
CALL_ORIG_FUNC(SetWindowPos)(g_cursorWindow, HWND_TOPMOST, cp.x, cp.y, g_bmpArrowSize.cx, g_bmpArrowSize.cy,
SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
}
HHOOK setWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId,
decltype(&SetWindowsHookExA) origSetWindowsHookEx)
{
if (WH_KEYBOARD_LL == idHook && hmod && GetModuleHandle("AcGenral") == hmod)
{
// Disable the IgnoreAltTab shim
return nullptr;
}
HHOOK result = origSetWindowsHookEx(idHook, lpfn, hmod, dwThreadId);
if (result && g_inputThreadId && (WH_KEYBOARD_LL == idHook || WH_MOUSE_LL == idHook))
{
PostThreadMessage(g_inputThreadId, WM_USER_RESET_HOOK, idHook, 0);
}
return result;
}
HHOOK WINAPI setWindowsHookExA(int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId)
{
LOG_FUNC("SetWindowsHookExA", idHook, lpfn, hmod, Compat::hex(dwThreadId));
return LOG_RESULT(setWindowsHookEx(idHook, lpfn, hmod, dwThreadId, CALL_ORIG_FUNC(SetWindowsHookExA)));
}
HHOOK WINAPI setWindowsHookExW(int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId)
{
LOG_FUNC("SetWindowsHookExW", idHook, lpfn, hmod, Compat::hex(dwThreadId));
return LOG_RESULT(setWindowsHookEx(idHook, lpfn, hmod, dwThreadId, CALL_ORIG_FUNC(SetWindowsHookExW)));
}
auto toTuple(const Input::HotKey& hotKey)
{
return std::make_tuple(hotKey.vk, hotKey.modifiers);
}
}
namespace Input
{
bool operator<(const HotKey& lhs, const HotKey& rhs)
{
return toTuple(lhs) < toTuple(rhs);
}
Overlay::Window* getCapture()
{
return g_capture;
}
HWND getCursorWindow()
{
return g_cursorWindow;
}
void installHooks()
{
HOOK_FUNCTION(user32, SetWindowsHookExA, setWindowsHookExA);
HOOK_FUNCTION(user32, SetWindowsHookExW, setWindowsHookExW);
}
void registerHotKey(const HotKey& hotKey, std::function<void(void*)> action, void* context)
{
static HANDLE thread = Dll::createThread(&inputThreadProc, nullptr, THREAD_PRIORITY_TIME_CRITICAL);
if (thread)
{
g_hotKeys[hotKey] = { action, context };
}
}
void setCapture(Overlay::Window* window)
{
g_capture = window;
if (window)
{
if (!g_mouseHook)
{
g_cursorWindow = Gdi::PresentationWindow::create(window->getWindow(), &cursorWindowProc);
CALL_ORIG_FUNC(SetLayeredWindowAttributes)(g_cursorWindow, RGB(0xFF, 0xFF, 0xFF), 0, LWA_COLORKEY);
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
GetMonitorInfo(MonitorFromWindow(window->getWindow(), MONITOR_DEFAULTTOPRIMARY), &mi);
g_monitorRect = mi.rcMonitor;
RECT r = window->getRect();
g_cursorPos = { (r.left + r.right) / 2, (r.top + r.bottom) / 2 };
CALL_ORIG_FUNC(SetWindowPos)(g_cursorWindow, HWND_TOPMOST, g_cursorPos.x, g_cursorPos.y,
g_bmpArrowSize.cx, g_bmpArrowSize.cy, SWP_NOACTIVATE | SWP_NOSENDCHANGING);
ShowWindow(g_cursorWindow, SW_SHOW);
g_mouseHook = CALL_ORIG_FUNC(SetWindowsHookExA)(WH_MOUSE_LL, &lowLevelMouseProc, Dll::g_currentModule, 0);
}
}
else if (g_mouseHook)
{
UnhookWindowsHookEx(g_mouseHook);
g_mouseHook = nullptr;
PostMessage(g_cursorWindow, WM_CLOSE, 0, 0);
g_cursorWindow = nullptr;
}
}
}

28
DDrawCompat/Input/Input.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <functional>
#include <set>
#include <Windows.h>
namespace Overlay
{
class Window;
}
namespace Input
{
struct HotKey
{
UINT vk;
std::set<UINT> modifiers;
};
bool operator<(const HotKey& lhs, const HotKey& rhs);
Overlay::Window* getCapture();
HWND getCursorWindow();
void installHooks();
void registerHotKey(const HotKey& hotKey, std::function<void(void*)> action, void* context);
void setCapture(Overlay::Window* window);
}

View File

@ -0,0 +1,32 @@
#include <Common/Hook.h>
#include <Overlay/ComboBoxControl.h>
namespace Overlay
{
ComboBoxControl::ComboBoxControl(Control& parent, const RECT& rect)
: Control(&parent, rect, WS_BORDER | WS_VISIBLE)
, m_dropDown(*this)
{
}
void ComboBoxControl::setValue(const std::string& value)
{
m_value = value;
invalidate(m_rect);
}
void ComboBoxControl::draw(HDC dc)
{
RECT rect = m_rect;
rect.left = m_rect.right - 17;
drawArrow(dc, rect, DFCS_SCROLLDOWN);
rect.left = m_rect.left + BORDER;
CALL_ORIG_FUNC(DrawTextA)(dc, m_value.c_str(), m_value.size(), &rect, DT_NOCLIP | DT_SINGLELINE | DT_VCENTER);
}
void ComboBoxControl::onLButtonDown(POINT /*pos*/)
{
m_dropDown.setVisible(true);
}
}

View File

@ -0,0 +1,28 @@
#pragma once
#include <string>
#include <vector>
#include <Overlay/ComboBoxDropDown.h>
#include <Overlay/Control.h>
namespace Overlay
{
class ComboBoxControl : public Control
{
public:
ComboBoxControl(Control& parent, const RECT& rect);
std::string getValue() const { return m_value; }
std::vector<std::string> getValues() const { return m_dropDown.getValues(); }
void setValue(const std::string& value);
void setValues(const std::vector<std::string>& values) { m_dropDown.setValues(values); }
private:
virtual void draw(HDC dc) override;
virtual void onLButtonDown(POINT pos) override;
std::string m_value;
ComboBoxDropDown m_dropDown;
};
}

View File

@ -0,0 +1,56 @@
#include <Common/Hook.h>
#include <Overlay/ComboBoxControl.h>
#include <Overlay/ComboBoxDropDown.h>
namespace Overlay
{
ComboBoxDropDown::ComboBoxDropDown(ComboBoxControl& parent)
: Window(&static_cast<Window&>(parent.getRoot()), { 0, 0, 100, 100 })
, m_parent(parent)
{
}
RECT ComboBoxDropDown::calculateRect(const RECT& monitorRect) const
{
const RECT parentRect = m_parent.getRect();
RECT r = { parentRect.left, parentRect.bottom,
parentRect.right, parentRect.bottom + static_cast<int>(m_parent.getValues().size()) * ARROW_SIZE };
const Window& rootWindow = static_cast<const Window&>(m_parent.getRoot());
const RECT rootRect = rootWindow.calculateRect(monitorRect);
OffsetRect(&r, rootRect.left, rootRect.top);
return r;
}
void ComboBoxDropDown::onLButtonDown(POINT pos)
{
if (PtInRect(&m_rect, { m_rect.left + pos.x, m_rect.top + pos.y }))
{
propagateMouseEvent(&Control::onLButtonDown, pos);
}
else
{
setVisible(false);
}
}
void ComboBoxDropDown::onNotify(Control& control)
{
m_parent.setValue(static_cast<LabelControl&>(control).getLabel());
m_parent.getParent()->onNotify(*m_parent.getParent());
}
void ComboBoxDropDown::setValues(const std::vector<std::string>& values)
{
m_values = values;
m_labels.clear();
int i = 0;
for (const auto& v : values)
{
m_labels.emplace_back(*this,
RECT{ BORDER, i * ARROW_SIZE, m_rect.right - m_rect.left - BORDER, (i + 1) * ARROW_SIZE },
v, DT_SINGLELINE | DT_VCENTER);
++i;
}
}
}

View File

@ -0,0 +1,32 @@
#pragma once
#include <list>
#include <vector>
#include <Overlay/LabelControl.h>
#include <Overlay/Window.h>
namespace Overlay
{
class ComboBoxControl;
class LabelControl;
class ComboBoxDropDown : public Window
{
public:
ComboBoxDropDown(ComboBoxControl& parent);
virtual void onNotify(Control& control) override;
std::vector<std::string> getValues() const { return m_values; }
void setValues(const std::vector<std::string>& values);
private:
virtual RECT calculateRect(const RECT& monitorRect) const override;
virtual void onLButtonDown(POINT pos) override;
ComboBoxControl& m_parent;
std::vector<std::string> m_values;
std::list<LabelControl> m_labels;
};
}

View File

@ -0,0 +1,36 @@
#include <Common/Hook.h>
#include <Config/Config.h>
#include <Overlay/ConfigWindow.h>
#include <Overlay/SettingControl.h>
namespace Overlay
{
ConfigWindow::ConfigWindow()
: Window(nullptr, { 0, 0, SettingControl::TOTAL_WIDTH, 200 }, { VK_F11, {} })
{
addControl(Config::alternatePixelCenter);
addControl(Config::antialiasing);
addControl(Config::displayFilter);
addControl(Config::textureFilter);
}
void ConfigWindow::addControl(Config::Setting& setting)
{
const int index = m_controls.size();
const int rowHeight = 25;
RECT rect = { 0, index * rowHeight + BORDER / 2, m_rect.right, (index + 1) * rowHeight + BORDER / 2 };
m_controls.emplace_back(*this, rect, setting);
}
RECT ConfigWindow::calculateRect(const RECT& monitorRect) const
{
const LONG width = m_rect.right - m_rect.left;
const LONG height = m_rect.bottom - m_rect.top;
RECT r = { 0, 0, width, height };
OffsetRect(&r, monitorRect.left, monitorRect.top);
OffsetRect(&r, (monitorRect.right - monitorRect.left - width) / 2, (monitorRect.bottom - monitorRect.top - height) / 2);
return r;
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <list>
#include <Overlay/SettingControl.h>
#include <Overlay/Window.h>
namespace Overlay
{
class ConfigWindow : public Window
{
public:
ConfigWindow();
private:
virtual RECT calculateRect(const RECT& monitorRect) const override;
void addControl(Config::Setting& setting);
std::list<SettingControl> m_controls;
};
}

View File

@ -0,0 +1,159 @@
#include <Common/Hook.h>
#include <Overlay/Control.h>
namespace Overlay
{
Control* g_capture = nullptr;
Control::Control(Control* parent, const RECT& rect, DWORD style)
: m_parent(parent)
, m_rect(rect)
, m_style(style)
{
if (parent)
{
parent->m_children.insert(this);
}
}
Control::~Control()
{
if (m_parent)
{
m_parent->m_children.erase(this);
}
}
void Control::drawAll(HDC dc)
{
draw(dc);
for (auto control : m_children)
{
control->drawAll(dc);
}
if (m_style & WS_BORDER)
{
RECT r = m_rect;
if (!m_parent)
{
OffsetRect(&r, -m_rect.left, -m_rect.top);
}
CALL_ORIG_FUNC(Rectangle)(dc, r.left, r.top, r.right, r.bottom);
}
}
void Control::drawArrow(HDC dc, RECT rect, UINT state)
{
CALL_ORIG_FUNC(Rectangle)(dc, rect.left, rect.top, rect.right, rect.bottom);
POINT center = { (rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2 };
rect = { center.x, center.y, center.x, center.y };
InflateRect(&rect, 3, 3);
POINT poly[3] = {};
switch (state)
{
case DFCS_SCROLLDOWN:
poly[0] = { rect.left, rect.top };
poly[1] = { rect.right, rect.top };
poly[2] = { center.x, rect.bottom };
break;
case DFCS_SCROLLLEFT:
poly[0] = { rect.left, center.y };
poly[1] = { rect.right, rect.top };
poly[2] = { rect.right, rect.bottom };
break;
case DFCS_SCROLLRIGHT:
poly[0] = { rect.left, rect.top };
poly[1] = { rect.left, rect.bottom };
poly[2] = { rect.right, center.y };
break;
case DFCS_SCROLLUP:
poly[0] = { center.x, rect.top };
poly[1] = { rect.left, rect.bottom };
poly[2] = { rect.right, rect.bottom };
break;
}
CALL_ORIG_FUNC(Polygon)(dc, poly, 3);
}
Control* Control::getCapture()
{
return g_capture;
}
const Control& Control::getRoot() const
{
if (m_parent)
{
return m_parent->getRoot();
}
return *this;
}
Control& Control::getRoot()
{
return const_cast<Control&>(std::as_const(*this).getRoot());
}
void Control::invalidate(const RECT& rect)
{
if (m_parent)
{
m_parent->invalidate(rect);
}
}
void Control::onLButtonDown(POINT pos)
{
propagateMouseEvent(&Control::onLButtonDown, pos);
}
void Control::onLButtonUp(POINT pos)
{
propagateMouseEvent(&Control::onLButtonUp, pos);
}
void Control::onMouseMove(POINT pos)
{
propagateMouseEvent(&Control::onMouseMove, pos);
}
void Control::propagateMouseEvent(void(Control::* onEvent)(POINT), POINT pos)
{
if (g_capture)
{
(g_capture->*onEvent)(pos);
return;
}
for (auto child : m_children)
{
if (PtInRect(&child->m_rect, pos))
{
(child->*onEvent)(pos);
return;
}
}
}
void Control::setCapture(Control* control)
{
g_capture = control;
}
void Control::setVisible(bool isVisible)
{
if (isVisible != Control::isVisible())
{
m_style ^= WS_VISIBLE;
invalidate(m_rect);
}
}
}

View File

@ -0,0 +1,50 @@
#pragma once
#include <set>
#include <Windows.h>
namespace Overlay
{
class Control
{
public:
static const int ARROW_SIZE = 17;
static const int BORDER = 8;
Control(Control* parent, const RECT& rect, DWORD style);
virtual ~Control();
Control(const Control&) = delete;
Control(Control&&) = delete;
Control& operator=(const Control&) = delete;
Control& operator=(Control&&) = delete;
virtual void draw(HDC /*dc*/) {}
virtual void invalidate(const RECT& rect);
virtual void onLButtonDown(POINT pos);
virtual void onLButtonUp(POINT pos);
virtual void onMouseMove(POINT pos);
virtual void onNotify(Control& /*control*/) {}
virtual void setVisible(bool isVisible);
void drawAll(HDC dc);
Control* getParent() const { return m_parent; }
RECT getRect() const { return m_rect; }
const Control& getRoot() const;
Control& getRoot();
bool isVisible() const { return m_style & WS_VISIBLE; }
protected:
void drawArrow(HDC dc, RECT rect, UINT state);
void propagateMouseEvent(void(Control::* onEvent)(POINT), POINT pos);
static Control* getCapture();
static void setCapture(Control* control);
Control* m_parent;
RECT m_rect;
DWORD m_style;
std::set<Control*> m_children;
};
}

View File

@ -0,0 +1,24 @@
#include <Common/Hook.h>
#include <Overlay/LabelControl.h>
namespace Overlay
{
LabelControl::LabelControl(Control& parent, const RECT& rect, const std::string& label, UINT format)
: Control(&parent, rect, WS_VISIBLE)
, m_label(label)
, m_format(format)
{
}
void LabelControl::draw(HDC dc)
{
RECT r = { m_rect.left + BORDER, m_rect.top, m_rect.right - BORDER, m_rect.bottom };
CALL_ORIG_FUNC(DrawTextA)(dc, m_label.c_str(), m_label.size(), &r,
m_format | DT_NOCLIP | DT_SINGLELINE | DT_VCENTER);
}
void LabelControl::onLButtonDown(POINT /*pos*/)
{
m_parent->onNotify(*this);
}
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <string>
#include <Windows.h>
#include <Overlay/Control.h>
namespace Overlay
{
class LabelControl : public Control
{
public:
LabelControl(Control& parent, const RECT& rect, const std::string& label, UINT format);
virtual void onLButtonDown(POINT pos) override;
std::string getLabel() const { return m_label; }
private:
virtual void draw(HDC dc) override;
std::string m_label;
UINT m_format;
};
}

View File

@ -0,0 +1,149 @@
#pragma once
#include <Common/Hook.h>
#include <Overlay/ScrollBarControl.h>
namespace
{
const DWORD REPEAT_DELAY = 500;
const DWORD REPEAT_INTERVAL = 100;
UINT_PTR g_repeatTimerId = 0;
long roundDiv(long n, long d)
{
return (n + d / 2) / d;
}
}
namespace Overlay
{
ScrollBarControl::ScrollBarControl(Control& parent, const RECT& rect, int min, int max)
: Control(&parent, rect, WS_VISIBLE)
, m_min(min)
, m_max(max)
, m_pos(min)
, m_leftArrow{ rect.left, rect.top, rect.left + ARROW_SIZE, rect.bottom }
, m_rightArrow{ rect.right - ARROW_SIZE, rect.top, rect.right, rect.bottom }
, m_state(State::IDLE)
{
}
void ScrollBarControl::draw(HDC dc)
{
drawArrow(dc, m_leftArrow, DFCS_SCROLLLEFT);
drawArrow(dc, m_rightArrow, DFCS_SCROLLRIGHT);
RECT r = { m_leftArrow.right, m_rect.top, m_rightArrow.left, m_rect.bottom };
CALL_ORIG_FUNC(Rectangle)(dc, r.left - 1, r.top, r.right + 1, r.bottom);
const int thumbPos = (m_pos - m_min) * (r.right - r.left - ARROW_SIZE) / (m_max - m_min);
r = { m_leftArrow.right + thumbPos, r.top, m_leftArrow.right + thumbPos + ARROW_SIZE, r.bottom };
SelectObject(dc, GetStockObject(DC_BRUSH));
CALL_ORIG_FUNC(Ellipse)(dc, r.left, r.top, r.right, r.bottom);
SelectObject(dc, GetStockObject(NULL_BRUSH));
}
void ScrollBarControl::onLButtonDown(POINT pos)
{
setCapture(this);
if (PtInRect(&m_leftArrow, pos))
{
setPos(m_pos - 1);
if (State::IDLE == m_state)
{
m_state = State::LEFT_ARROW_PRESSED;
startRepeatTimer(REPEAT_DELAY);
}
}
else if (PtInRect(&m_rightArrow, pos))
{
setPos(m_pos + 1);
if (State::IDLE == m_state)
{
m_state = State::RIGHT_ARROW_PRESSED;
startRepeatTimer(REPEAT_DELAY);
}
}
else
{
onThumbTrack(pos);
if (State::IDLE == m_state)
{
m_state = State::THUMB_TRACKING;
}
}
}
void ScrollBarControl::onLButtonUp(POINT /*pos*/)
{
setCapture(nullptr);
stopRepeatTimer();
m_state = State::IDLE;
}
void ScrollBarControl::onMouseMove(POINT pos)
{
if (State::THUMB_TRACKING == m_state)
{
onThumbTrack(pos);
}
}
void ScrollBarControl::onRepeat()
{
stopRepeatTimer();
switch (m_state)
{
case State::LEFT_ARROW_PRESSED:
setPos(m_pos - 1);
startRepeatTimer(REPEAT_INTERVAL);
break;
case State::RIGHT_ARROW_PRESSED:
setPos(m_pos + 1);
startRepeatTimer(REPEAT_INTERVAL);
break;
}
}
void ScrollBarControl::onThumbTrack(POINT pos)
{
const auto minPos = m_leftArrow.right + ARROW_SIZE / 2;
const auto maxPos = m_rightArrow.left - ARROW_SIZE / 2;
pos.x = max(pos.x, minPos);
pos.x = min(pos.x, maxPos);
setPos(m_min + roundDiv((pos.x - minPos) * (m_max - m_min), maxPos - minPos));
}
void CALLBACK ScrollBarControl::repeatTimerProc(HWND /*hwnd*/, UINT /*message*/, UINT_PTR /*iTimerID*/, DWORD /*dwTime*/)
{
static_cast<ScrollBarControl*>(Overlay::Control::getCapture())->onRepeat();
}
void ScrollBarControl::setPos(int pos)
{
pos = max(pos, m_min);
pos = min(pos, m_max);
if (pos != m_pos)
{
m_pos = pos;
m_parent->onNotify(*this);
}
}
void ScrollBarControl::startRepeatTimer(DWORD time)
{
g_repeatTimerId = SetTimer(nullptr, g_repeatTimerId, time, &repeatTimerProc);
}
void ScrollBarControl::stopRepeatTimer()
{
if (0 != g_repeatTimerId)
{
KillTimer(nullptr, g_repeatTimerId);
g_repeatTimerId = 0;
}
}
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <Overlay/Control.h>
namespace Overlay
{
class ScrollBarControl : public Control
{
public:
ScrollBarControl(Control& parent, const RECT& rect, int min, int max);
int getPos() const { return m_pos; }
void setPos(int pos);
private:
enum class State
{
IDLE,
LEFT_ARROW_PRESSED,
RIGHT_ARROW_PRESSED,
THUMB_TRACKING,
};
int m_min;
int m_max;
int m_pos;
RECT m_leftArrow;
RECT m_rightArrow;
State m_state;
virtual void draw(HDC dc) override;
virtual void onLButtonDown(POINT pos) override;
virtual void onLButtonUp(POINT pos) override;
virtual void onMouseMove(POINT pos) override;
void onRepeat();
void onThumbTrack(POINT pos);
static void CALLBACK repeatTimerProc(HWND hwnd, UINT message, UINT_PTR iTimerID, DWORD dwTime);
static void startRepeatTimer(DWORD time);
static void stopRepeatTimer();
};
}

View File

@ -0,0 +1,99 @@
#include <Config/Config.h>
#include <Config/Parser.h>
#include <Config/Setting.h>
#include <D3dDdi/Device.h>
#include <DDraw/RealPrimarySurface.h>
#include <Overlay/ComboBoxControl.h>
#include <Overlay/SettingControl.h>
namespace Overlay
{
SettingControl::SettingControl(Control& parent, const RECT& rect, Config::Setting& setting)
: Control(&parent, rect, WS_VISIBLE)
, m_setting(setting)
, m_settingLabel(*this, { rect.left, rect.top, rect.left + SETTING_LABEL_WIDTH, rect.bottom }, setting.getName() + ':', 0)
{
const RECT r = { rect.left + SETTING_LABEL_WIDTH, rect.top + BORDER / 2,
rect.left + SETTING_LABEL_WIDTH + SETTING_CONTROL_WIDTH, rect.bottom - BORDER / 2 };
m_valueControl.reset(new ComboBoxControl(*this, r));
getValueComboBox().setValue(setting.getValueStr());
getValueComboBox().setValues(setting.getDefaultValueStrings());
onValueChanged();
updateValuesParam();
}
ComboBoxControl& SettingControl::getValueComboBox() const
{
return static_cast<ComboBoxControl&>(*m_valueControl);
}
void SettingControl::onNotify(Control& control)
{
if (&control == m_paramControl.get())
{
onParamChanged();
}
else
{
onValueChanged();
}
if (&Config::antialiasing == &m_setting ||
&Config::textureFilter == &m_setting)
{
D3dDdi::Device::updateAllConfig();
}
invalidate(m_rect);
}
void SettingControl::onParamChanged()
{
const std::string value(Config::Parser::removeParam(m_setting.getValueStr()) +
'(' + std::to_string(m_paramControl->getPos()) + ')');
m_setting.set(value);
getValueComboBox().setValue(value);
updateValuesParam();
}
void SettingControl::onValueChanged()
{
const std::string value(getValueComboBox().getValue());
m_setting.set(value);
if (m_paramControl)
{
m_paramLabel.reset();
m_paramControl.reset();
}
const auto paramInfo = m_setting.getParamInfo();
if (!paramInfo.name.empty())
{
RECT r = m_valueControl->getRect();
r.left = r.right;
r.right = r.left + PARAM_LABEL_WIDTH;
m_paramLabel.reset(new LabelControl(*this, r, paramInfo.name + ':', 0));
r.left = r.right;
r.right = r.left + PARAM_CONTROL_WIDTH;
m_paramControl.reset(new ScrollBarControl(*this, r, paramInfo.min, paramInfo.max));
m_paramControl->setPos(m_setting.getParam());
}
}
void SettingControl::updateValuesParam()
{
const auto currentValue(Config::Parser::removeParam(m_setting.getValueStr()));
auto values(getValueComboBox().getValues());
for (auto& v : values)
{
if (Config::Parser::removeParam(v) == currentValue)
{
v = m_setting.getValueStr();
getValueComboBox().setValues(values);
break;
}
}
}
}

View File

@ -0,0 +1,44 @@
#pragma once
#include <memory>
#include <string>
#include <Overlay/LabelControl.h>
#include <Overlay/ScrollBarControl.h>
namespace Config
{
class Setting;
}
namespace Overlay
{
class ComboBoxControl;
class SettingControl : public Control
{
public:
static const int PARAM_LABEL_WIDTH = 50;
static const int PARAM_CONTROL_WIDTH = 151;
static const int SETTING_LABEL_WIDTH = 120;
static const int SETTING_CONTROL_WIDTH = 151;
static const int TOTAL_WIDTH =
SETTING_LABEL_WIDTH + SETTING_CONTROL_WIDTH + PARAM_LABEL_WIDTH + PARAM_CONTROL_WIDTH + BORDER;
SettingControl(Control& parent, const RECT& rect, Config::Setting& setting);
virtual void onNotify(Control& control) override;
private:
ComboBoxControl& getValueComboBox() const;
void onParamChanged();
void onValueChanged();
void updateValuesParam();
Config::Setting& m_setting;
LabelControl m_settingLabel;
std::unique_ptr<Control> m_valueControl;
std::unique_ptr<LabelControl> m_paramLabel;
std::unique_ptr<ScrollBarControl> m_paramControl;
};
}

View File

@ -0,0 +1,194 @@
#include <map>
#include <Windows.h>
#include <CommCtrl.h>
#include <Common/Hook.h>
#include <Common/Log.h>
#include <Config/Config.h>
#include <Dll/Dll.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <Gdi/PresentationWindow.h>
#include <Input/Input.h>
#include <Overlay/Control.h>
#include <Overlay/Window.h>
#include <Win32/DisplayMode.h>
namespace
{
enum class ControlType
{
COMBOBOX,
SLIDER
};
std::map<HWND, Overlay::Window&> g_windows;
HFONT createDefaultFont()
{
LOGFONT lf = {};
lf.lfHeight = 13;
lf.lfWeight = FW_NORMAL;
lf.lfQuality = NONANTIALIASED_QUALITY;
strcpy_s(lf.lfFaceName, "Segoe UI");
return CreateFontIndirect(&lf);
}
void toggleWindow(void* window)
{
auto wnd = static_cast<Overlay::Window*>(window);
wnd->setVisible(!wnd->isVisible());
}
}
namespace Overlay
{
Window::Window(Window* parentWindow, const RECT& rect, const Input::HotKey& hotKey)
: Control(nullptr, rect, WS_BORDER)
, m_hwnd(Gdi::PresentationWindow::create(parentWindow ? parentWindow->m_hwnd : nullptr, &staticWindowProc))
, m_parentWindow(parentWindow)
, m_transparency(25)
{
g_windows.emplace(m_hwnd, *this);
setTransparency(m_transparency);
if (0 != hotKey.vk)
{
Input::registerHotKey(hotKey, &toggleWindow, this);
}
}
Window::~Window()
{
Gdi::PresentationWindow::destroy(m_hwnd);
g_windows.erase(m_hwnd);
}
void Window::draw(HDC /*dc*/)
{
}
void Window::invalidate(const RECT& rect)
{
InvalidateRect(m_hwnd, &rect, TRUE);
}
void Window::onEraseBackground(HDC dc)
{
RECT r = { 0, 0, m_rect.right - m_rect.left, m_rect.bottom - m_rect.top };
CALL_ORIG_FUNC(FillRect)(dc, &r, static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
}
void Window::onPaint()
{
static HFONT font = createDefaultFont();
PAINTSTRUCT ps = {};
HDC dc = BeginPaint(m_hwnd, &ps);
SelectObject(dc, font);
SelectObject(dc, GetStockObject(DC_PEN));
SelectObject(dc, GetStockObject(NULL_BRUSH));
SetBkColor(dc, RGB(0, 0, 0));
SetDCBrushColor(dc, RGB(0, 0, 0));
SetDCPenColor(dc, RGB(0, 255, 0));
SetTextColor(dc, RGB(0, 255, 0));
drawAll(dc);
EndPaint(m_hwnd, &ps);
DDraw::RealPrimarySurface::scheduleUpdate();
}
void Window::setTransparency(int transparency)
{
m_transparency = transparency;
CALL_ORIG_FUNC(SetLayeredWindowAttributes)(m_hwnd, 0, static_cast<BYTE>(100 - transparency) * 255 / 100, ULW_ALPHA);
}
void Window::setVisible(bool isVisible)
{
if (isVisible == Window::isVisible())
{
return;
}
m_style ^= WS_VISIBLE;
if (m_style & WS_VISIBLE)
{
updatePos();
ShowWindow(m_hwnd, SW_SHOWNA);
Input::setCapture(this);
}
else
{
auto capture = Input::getCapture();
if (capture != this && capture->m_parentWindow == this)
{
capture->setVisible(false);
}
ShowWindow(m_hwnd, SW_HIDE);
Input::setCapture(m_parentWindow);
}
}
LRESULT CALLBACK Window::staticWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return g_windows.find(hwnd)->second.windowProc(uMsg, wParam, lParam);
}
void Window::updatePos()
{
auto monitorRect = Win32::DisplayMode::getEmulatedDisplayMode().rect;
if (IsRectEmpty(&monitorRect))
{
monitorRect = DDraw::PrimarySurface::getMonitorRect();
if (IsRectEmpty(&monitorRect))
{
HMONITOR monitor = nullptr;
HWND foregroundWindow = GetForegroundWindow();
if (foregroundWindow)
{
monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTONEAREST);
}
else
{
monitor = MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY);
}
MONITORINFO mi = {};
mi.cbSize = sizeof(mi);
CALL_ORIG_FUNC(GetMonitorInfoA)(monitor, &mi);
monitorRect = mi.rcMonitor;
if (IsRectEmpty(&monitorRect))
{
monitorRect = { 0, 0, m_rect.right - m_rect.left, m_rect.bottom - m_rect.top };
}
}
}
m_rect = calculateRect(monitorRect);
CALL_ORIG_FUNC(SetWindowPos)(m_hwnd, HWND_TOPMOST, m_rect.left, m_rect.top,
m_rect.right - m_rect.left, m_rect.bottom - m_rect.top, SWP_NOACTIVATE);
}
LRESULT Window::windowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DISPLAYCHANGE:
updatePos();
break;
case WM_ERASEBKGND:
onEraseBackground(reinterpret_cast<HDC>(wParam));
return 1;
case WM_PAINT:
onPaint();
return 0;
}
return CALL_ORIG_FUNC(DefWindowProcA)(m_hwnd, uMsg, wParam, lParam);
}
}

View File

@ -0,0 +1,41 @@
#pragma once
#include <vector>
#include <Windows.h>
#include <Input/Input.h>
#include <Overlay/Control.h>
namespace Overlay
{
class Window : public Control
{
public:
Window(Window* parentWindow, const RECT& rect, const Input::HotKey& hotKey = {});
virtual ~Window() override;
virtual RECT calculateRect(const RECT& monitorRect) const = 0;
virtual void invalidate(const RECT& rect) override;
virtual void setVisible(bool isVisible) override;
HWND getWindow() const { return m_hwnd; }
void setTransparency(int transparency);
protected:
HWND m_hwnd;
Window* m_parentWindow;
int m_transparency;
void updatePos();
private:
virtual void draw(HDC dc) override;
void onEraseBackground(HDC dc);
void onPaint();
LRESULT windowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK staticWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
}

View File

@ -1,28 +0,0 @@
#include <Windows.h>
#include <Common/Hook.h>
#include <Win32/MsgHooks.h>
namespace
{
HHOOK WINAPI setWindowsHookExA(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId)
{
if (WH_KEYBOARD_LL == idHook && hMod && GetModuleHandle("AcGenral") == hMod)
{
// This effectively disables the IgnoreAltTab shim
return nullptr;
}
return CALL_ORIG_FUNC(SetWindowsHookExA)(idHook, lpfn, hMod, dwThreadId);
}
}
namespace Win32
{
namespace MsgHooks
{
void installHooks()
{
HOOK_FUNCTION(user32, SetWindowsHookExA, setWindowsHookExA);
}
}
}

View File

@ -1,9 +0,0 @@
#pragma once
namespace Win32
{
namespace MsgHooks
{
void installHooks();
}
}

BIN
DDrawCompat/arrow.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B