diff --git a/DDrawCompat/Config/EnumSetting.cpp b/DDrawCompat/Config/EnumSetting.cpp index 4e0f010..5d12d8e 100644 --- a/DDrawCompat/Config/EnumSetting.cpp +++ b/DDrawCompat/Config/EnumSetting.cpp @@ -5,14 +5,13 @@ namespace { - std::map createMapping(const std::vector& enumNames) + std::vector> createMapping(const std::vector& enumNames) { - std::map mapping; + std::vector> mapping; unsigned i = 0; for (const auto& name : enumNames) { - - mapping[name] = i; + mapping.push_back({ name, i }); ++i; } return mapping; diff --git a/DDrawCompat/Config/MappedSetting.h b/DDrawCompat/Config/MappedSetting.h index 8867d71..6f3e8f0 100644 --- a/DDrawCompat/Config/MappedSetting.h +++ b/DDrawCompat/Config/MappedSetting.h @@ -1,6 +1,7 @@ #pragma once -#include +#include +#include #include #include @@ -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& valueMapping) - : Setting(name, default) - , m_value{} - , m_valueMapping(valueMapping) + virtual std::vector 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>& 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 m_valueMapping; + const std::vector> m_valueMapping; + std::vector m_defaultValueStrings; }; } diff --git a/DDrawCompat/Config/Parser.cpp b/DDrawCompat/Config/Parser.cpp index 45dc03a..99562cc 100644 --- a/DDrawCompat/Config/Parser.cpp +++ b/DDrawCompat/Config/Parser.cpp @@ -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); diff --git a/DDrawCompat/Config/Parser.h b/DDrawCompat/Config/Parser.h index f76b4eb..b1ea43d 100644 --- a/DDrawCompat/Config/Parser.h +++ b/DDrawCompat/Config/Parser.h @@ -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); } } diff --git a/DDrawCompat/Config/Setting.cpp b/DDrawCompat/Config/Setting.cpp index 7fc2fd0..e47e02e 100644 --- a/DDrawCompat/Config/Setting.cpp +++ b/DDrawCompat/Config/Setting.cpp @@ -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 + "'"); + } + } } diff --git a/DDrawCompat/Config/Setting.h b/DDrawCompat/Config/Setting.h index 266835a..56a9c63 100644 --- a/DDrawCompat/Config/Setting.h +++ b/DDrawCompat/Config/Setting.h @@ -1,12 +1,22 @@ #pragma once #include +#include 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 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; diff --git a/DDrawCompat/Config/Settings/Antialiasing.cpp b/DDrawCompat/Config/Settings/Antialiasing.cpp index 3e7971b..3c051b2 100644 --- a/DDrawCompat/Config/Settings/Antialiasing.cpp +++ b/DDrawCompat/Config/Settings/Antialiasing.cpp @@ -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 {}; } } } diff --git a/DDrawCompat/Config/Settings/Antialiasing.h b/DDrawCompat/Config/Settings/Antialiasing.h index cdbc760..60a8e28 100644 --- a/DDrawCompat/Config/Settings/Antialiasing.h +++ b/DDrawCompat/Config/Settings/Antialiasing.h @@ -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; }; } } diff --git a/DDrawCompat/Config/Settings/CpuAffinity.cpp b/DDrawCompat/Config/Settings/CpuAffinity.cpp index c16ac04..85c834c 100644 --- a/DDrawCompat/Config/Settings/CpuAffinity.cpp +++ b/DDrawCompat/Config/Settings/CpuAffinity.cpp @@ -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); } diff --git a/DDrawCompat/Config/Settings/CpuAffinity.h b/DDrawCompat/Config/Settings/CpuAffinity.h index 63f39b2..9b1e6cc 100644 --- a/DDrawCompat/Config/Settings/CpuAffinity.h +++ b/DDrawCompat/Config/Settings/CpuAffinity.h @@ -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& values) override; unsigned m_value; diff --git a/DDrawCompat/Config/Settings/DisplayFilter.cpp b/DDrawCompat/Config/Settings/DisplayFilter.cpp index 02adc6b..c215530 100644 --- a/DDrawCompat/Config/Settings/DisplayFilter.cpp +++ b/DDrawCompat/Config/Settings/DisplayFilter.cpp @@ -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 {}; + } + } } diff --git a/DDrawCompat/Config/Settings/DisplayFilter.h b/DDrawCompat/Config/Settings/DisplayFilter.h index 2fc4504..727459a 100644 --- a/DDrawCompat/Config/Settings/DisplayFilter.h +++ b/DDrawCompat/Config/Settings/DisplayFilter.h @@ -4,24 +4,17 @@ namespace Config { - namespace Settings - { - class DisplayFilter : public MappedSetting - { - public: - static const UINT POINT = 0; - static const UINT BILINEAR = 1; + namespace Settings + { + class DisplayFilter : public MappedSetting + { + 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; + }; + } } diff --git a/DDrawCompat/Config/Settings/SupportedResolutions.h b/DDrawCompat/Config/Settings/SupportedResolutions.h index 59ebea2..12a3997 100644 --- a/DDrawCompat/Config/Settings/SupportedResolutions.h +++ b/DDrawCompat/Config/Settings/SupportedResolutions.h @@ -17,10 +17,11 @@ namespace Config SupportedResolutions(); + virtual std::string getValueStr() const override; + std::set get() const { return m_resolutions; } private: - std::string getValueStr() const override; void setValues(const std::vector& values) override; std::set m_resolutions; diff --git a/DDrawCompat/D3dDdi/Adapter.cpp b/DDrawCompat/D3dDdi/Adapter.cpp index 0f5c9c4..a06cfb3 100644 --- a/DDrawCompat/D3dDdi/Adapter.cpp +++ b/DDrawCompat/D3dDdi/Adapter.cpp @@ -110,7 +110,7 @@ namespace D3dDdi levels.Format = D3DDDIFMT_X8R8G8B8; levels.MsType = static_cast(samples); getCaps(D3DDDICAPS_GETMULTISAMPLEQUALITYLEVELS, levels); - return { levels.MsType, min(Config::antialiasing.getParam(), levels.QualityLevels - 1) }; + return { levels.MsType, min(static_cast(Config::antialiasing.getParam()), levels.QualityLevels - 1) }; } std::string Adapter::getSupportedMsaaModes(const std::map& formatOps) const diff --git a/DDrawCompat/D3dDdi/Device.cpp b/DDrawCompat/D3dDdi/Device.cpp index 40710af..293a90d 100644 --- a/DDrawCompat/D3dDdi/Device.cpp +++ b/DDrawCompat/D3dDdi/Device.cpp @@ -12,6 +12,7 @@ #include #include #include +#include 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 Device::s_devices; bool Device::s_isFlushEnabled = true; } diff --git a/DDrawCompat/D3dDdi/Device.h b/DDrawCompat/D3dDdi/Device.h index 7e2a3ba..2db86bd 100644 --- a/DDrawCompat/D3dDdi/Device.h +++ b/DDrawCompat/D3dDdi/Device.h @@ -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; diff --git a/DDrawCompat/D3dDdi/DeviceState.cpp b/DDrawCompat/D3dDdi/DeviceState.cpp index 576c9fc..ff05028 100644 --- a/DDrawCompat/D3dDdi/DeviceState.cpp +++ b/DDrawCompat/D3dDdi/DeviceState.cpp @@ -6,6 +6,27 @@ #include #include +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); } diff --git a/DDrawCompat/D3dDdi/DeviceState.h b/DDrawCompat/D3dDdi/DeviceState.h index 842db0e..ffbf3cb 100644 --- a/DDrawCompat/D3dDdi/DeviceState.h +++ b/DDrawCompat/D3dDdi/DeviceState.h @@ -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& currentState, HRESULT(APIENTRY* origSetState)(HANDLE, const StateData*)); + void updateTextureStageState(UINT stage, D3DDDITEXTURESTAGESTATETYPE state); + Device& m_device; D3DDDIARG_SETDEPTHSTENCIL m_depthStencil; HANDLE m_pixelShader; diff --git a/DDrawCompat/D3dDdi/Resource.cpp b/DDrawCompat/D3dDdi/Resource.cpp index 5d7f07f..d4322f2 100644 --- a/DDrawCompat/D3dDdi/Resource.cpp +++ b/DDrawCompat/D3dDdi/Resource.cpp @@ -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(si.Width), static_cast(si.Height) }; + g_primaryRect = { 0, 0, static_cast(si.Width), static_cast(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; + } + } } diff --git a/DDrawCompat/D3dDdi/Resource.h b/DDrawCompat/D3dDdi/Resource.h index cf73a51..ffb0f94 100644 --- a/DDrawCompat/D3dDdi/Resource.h +++ b/DDrawCompat/D3dDdi/Resource.h @@ -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 m_lockData; std::unique_ptr m_lockResource; SurfaceRepository::Surface m_customSurface; + std::pair m_multiSampleConfig; + bool m_isSurfaceRepoResource; }; } diff --git a/DDrawCompat/D3dDdi/SurfaceRepository.h b/DDrawCompat/D3dDdi/SurfaceRepository.h index 221d536..4e08542 100644 --- a/DDrawCompat/D3dDdi/SurfaceRepository.h +++ b/DDrawCompat/D3dDdi/SurfaceRepository.h @@ -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 initFunc); bool isLost(Surface& surface); - void release(Surface& surface); const Adapter& m_adapter; HCURSOR m_cursor; diff --git a/DDrawCompat/DDrawCompat.rc b/DDrawCompat/DDrawCompat.rc index 65767ff..0f6a8ee 100644 --- a/DDrawCompat/DDrawCompat.rc +++ b/DDrawCompat/DDrawCompat.rc @@ -36,3 +36,5 @@ FILESUBTYPE VFT2_UNKNOWN VALUE "Translation", 0x409, 0 } } + +BMP_ARROW BITMAP "arrow.bmp" diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index 061c793..3249eb3 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -306,10 +306,18 @@ + + + + + + + + + - @@ -395,10 +403,18 @@ + + + + + + + + + - @@ -414,6 +430,9 @@ + + + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index ff8101c..6a4095c 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -91,6 +91,18 @@ {00e06bd4-3f10-46dc-af0f-99834dfac835} + + {32765547-b1b7-48d3-bf58-d09cf388de91} + + + {38167700-fb70-4ee8-b149-824a9fde51eb} + + + {29dba6e7-0bca-44e2-9776-2ba478e088bc} + + + {e0a8a3ac-59fd-4cb0-ae81-5ae2c56fbfac} + @@ -222,9 +234,6 @@ Header Files\Direct3d\Visitors - - Header Files\Win32 - Header Files\Win32 @@ -456,6 +465,33 @@ Header Files\Config\Settings + + Header Files\Input + + + Header Files\Overlay + + + Header Files\Overlay + + + Header Files\Overlay + + + Header Files\Overlay + + + Header Files\Overlay + + + Header Files\Overlay + + + Header Files\Overlay + + + Header Files\Overlay + @@ -533,9 +569,6 @@ Source Files\Common - - Source Files\Win32 - Source Files\Win32 @@ -719,6 +752,33 @@ Source Files\Config\Settings + + Source Files\Input + + + Source Files\Overlay + + + Source Files\Overlay + + + Source Files\Overlay + + + Source Files\Overlay + + + Source Files\Overlay + + + Source Files\Overlay + + + Source Files\Overlay + + + Source Files\Overlay + @@ -744,4 +804,9 @@ Shaders + + + Resource Files + + \ No newline at end of file diff --git a/DDrawCompat/Dll/DllMain.cpp b/DDrawCompat/Dll/DllMain.cpp index 2a40cfb..f705a95 100644 --- a/DDrawCompat/Dll/DllMain.cpp +++ b/DDrawCompat/Dll/DllMain.cpp @@ -19,9 +19,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -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(); diff --git a/DDrawCompat/Gdi/PresentationWindow.cpp b/DDrawCompat/Gdi/PresentationWindow.cpp index 56d4ca4..c7c1324 100644 --- a/DDrawCompat/Gdi/PresentationWindow.cpp +++ b/DDrawCompat/Gdi/PresentationWindow.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include 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(sendMessageBlocking( - g_messageWindow, WM_CREATEPRESENTATIONWINDOW, reinterpret_cast(owner), 0)); + return reinterpret_cast(sendMessageBlocking(g_messageWindow, WM_CREATEPRESENTATIONWINDOW, + reinterpret_cast(owner), reinterpret_cast(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 = {}; diff --git a/DDrawCompat/Gdi/PresentationWindow.h b/DDrawCompat/Gdi/PresentationWindow.h index 0984ca9..31223e1 100644 --- a/DDrawCompat/Gdi/PresentationWindow.h +++ b/DDrawCompat/Gdi/PresentationWindow.h @@ -2,12 +2,18 @@ #include +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); diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index a7d7918..9ea36fe 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include 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) diff --git a/DDrawCompat/Gdi/Window.cpp b/DDrawCompat/Gdi/Window.cpp index 6e14e49..29ccfb3 100644 --- a/DDrawCompat/Gdi/Window.cpp +++ b/DDrawCompat/Gdi/Window.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include namespace { @@ -96,6 +98,64 @@ namespace return rgn; } + void presentLayeredWindow(CompatWeakPtr 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 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 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() diff --git a/DDrawCompat/Input/Input.cpp b/DDrawCompat/Input/Input.cpp new file mode 100644 index 0000000..19c0880 --- /dev/null +++ b/DDrawCompat/Input/Input.cpp @@ -0,0 +1,283 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + struct HotKeyData + { + std::function 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 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(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(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(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 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; + } + } +} diff --git a/DDrawCompat/Input/Input.h b/DDrawCompat/Input/Input.h new file mode 100644 index 0000000..404c26b --- /dev/null +++ b/DDrawCompat/Input/Input.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include + +namespace Overlay +{ + class Window; +} + +namespace Input +{ + struct HotKey + { + UINT vk; + std::set modifiers; + }; + + bool operator<(const HotKey& lhs, const HotKey& rhs); + + Overlay::Window* getCapture(); + HWND getCursorWindow(); + void installHooks(); + void registerHotKey(const HotKey& hotKey, std::function action, void* context); + void setCapture(Overlay::Window* window); +} diff --git a/DDrawCompat/Overlay/ComboBoxControl.cpp b/DDrawCompat/Overlay/ComboBoxControl.cpp new file mode 100644 index 0000000..56c475a --- /dev/null +++ b/DDrawCompat/Overlay/ComboBoxControl.cpp @@ -0,0 +1,32 @@ +#include +#include + +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); + } +} diff --git a/DDrawCompat/Overlay/ComboBoxControl.h b/DDrawCompat/Overlay/ComboBoxControl.h new file mode 100644 index 0000000..2774699 --- /dev/null +++ b/DDrawCompat/Overlay/ComboBoxControl.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include +#include + +namespace Overlay +{ + class ComboBoxControl : public Control + { + public: + ComboBoxControl(Control& parent, const RECT& rect); + + std::string getValue() const { return m_value; } + std::vector getValues() const { return m_dropDown.getValues(); } + void setValue(const std::string& value); + void setValues(const std::vector& 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; + }; +} diff --git a/DDrawCompat/Overlay/ComboBoxDropDown.cpp b/DDrawCompat/Overlay/ComboBoxDropDown.cpp new file mode 100644 index 0000000..f3d4c07 --- /dev/null +++ b/DDrawCompat/Overlay/ComboBoxDropDown.cpp @@ -0,0 +1,56 @@ +#include +#include +#include + +namespace Overlay +{ + ComboBoxDropDown::ComboBoxDropDown(ComboBoxControl& parent) + : Window(&static_cast(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(m_parent.getValues().size()) * ARROW_SIZE }; + + const Window& rootWindow = static_cast(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(control).getLabel()); + m_parent.getParent()->onNotify(*m_parent.getParent()); + } + + void ComboBoxDropDown::setValues(const std::vector& 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; + } + } +} diff --git a/DDrawCompat/Overlay/ComboBoxDropDown.h b/DDrawCompat/Overlay/ComboBoxDropDown.h new file mode 100644 index 0000000..3e0b398 --- /dev/null +++ b/DDrawCompat/Overlay/ComboBoxDropDown.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include +#include + +namespace Overlay +{ + class ComboBoxControl; + class LabelControl; + + class ComboBoxDropDown : public Window + { + public: + ComboBoxDropDown(ComboBoxControl& parent); + + virtual void onNotify(Control& control) override; + + std::vector getValues() const { return m_values; } + void setValues(const std::vector& values); + + private: + virtual RECT calculateRect(const RECT& monitorRect) const override; + virtual void onLButtonDown(POINT pos) override; + + ComboBoxControl& m_parent; + std::vector m_values; + std::list m_labels; + }; +} diff --git a/DDrawCompat/Overlay/ConfigWindow.cpp b/DDrawCompat/Overlay/ConfigWindow.cpp new file mode 100644 index 0000000..fb5fce0 --- /dev/null +++ b/DDrawCompat/Overlay/ConfigWindow.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +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; + } +} diff --git a/DDrawCompat/Overlay/ConfigWindow.h b/DDrawCompat/Overlay/ConfigWindow.h new file mode 100644 index 0000000..402cacf --- /dev/null +++ b/DDrawCompat/Overlay/ConfigWindow.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include +#include + +namespace Overlay +{ + class ConfigWindow : public Window + { + public: + ConfigWindow(); + + private: + virtual RECT calculateRect(const RECT& monitorRect) const override; + + void addControl(Config::Setting& setting); + + std::list m_controls; + }; +} diff --git a/DDrawCompat/Overlay/Control.cpp b/DDrawCompat/Overlay/Control.cpp new file mode 100644 index 0000000..7cc7d3d --- /dev/null +++ b/DDrawCompat/Overlay/Control.cpp @@ -0,0 +1,159 @@ +#include +#include + +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(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); + } + } +} diff --git a/DDrawCompat/Overlay/Control.h b/DDrawCompat/Overlay/Control.h new file mode 100644 index 0000000..07d4408 --- /dev/null +++ b/DDrawCompat/Overlay/Control.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include + +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 m_children; + }; +} diff --git a/DDrawCompat/Overlay/LabelControl.cpp b/DDrawCompat/Overlay/LabelControl.cpp new file mode 100644 index 0000000..892d702 --- /dev/null +++ b/DDrawCompat/Overlay/LabelControl.cpp @@ -0,0 +1,24 @@ +#include +#include + +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); + } +} diff --git a/DDrawCompat/Overlay/LabelControl.h b/DDrawCompat/Overlay/LabelControl.h new file mode 100644 index 0000000..f8fd07e --- /dev/null +++ b/DDrawCompat/Overlay/LabelControl.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +#include + +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; + }; +} diff --git a/DDrawCompat/Overlay/ScrollBarControl.cpp b/DDrawCompat/Overlay/ScrollBarControl.cpp new file mode 100644 index 0000000..1c03e69 --- /dev/null +++ b/DDrawCompat/Overlay/ScrollBarControl.cpp @@ -0,0 +1,149 @@ +#pragma once + +#include +#include + +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(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; + } + } +} diff --git a/DDrawCompat/Overlay/ScrollBarControl.h b/DDrawCompat/Overlay/ScrollBarControl.h new file mode 100644 index 0000000..c10ab08 --- /dev/null +++ b/DDrawCompat/Overlay/ScrollBarControl.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +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(); + }; +} diff --git a/DDrawCompat/Overlay/SettingControl.cpp b/DDrawCompat/Overlay/SettingControl.cpp new file mode 100644 index 0000000..86f4cad --- /dev/null +++ b/DDrawCompat/Overlay/SettingControl.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include + +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(*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; + } + } + } +} diff --git a/DDrawCompat/Overlay/SettingControl.h b/DDrawCompat/Overlay/SettingControl.h new file mode 100644 index 0000000..224d1de --- /dev/null +++ b/DDrawCompat/Overlay/SettingControl.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include +#include + +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 m_valueControl; + std::unique_ptr m_paramLabel; + std::unique_ptr m_paramControl; + }; +} diff --git a/DDrawCompat/Overlay/Window.cpp b/DDrawCompat/Overlay/Window.cpp new file mode 100644 index 0000000..e0d9753 --- /dev/null +++ b/DDrawCompat/Overlay/Window.cpp @@ -0,0 +1,194 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + enum class ControlType + { + COMBOBOX, + SLIDER + }; + + std::map 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(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(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(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(wParam)); + return 1; + + case WM_PAINT: + onPaint(); + return 0; + } + + return CALL_ORIG_FUNC(DefWindowProcA)(m_hwnd, uMsg, wParam, lParam); + } +} diff --git a/DDrawCompat/Overlay/Window.h b/DDrawCompat/Overlay/Window.h new file mode 100644 index 0000000..b656f01 --- /dev/null +++ b/DDrawCompat/Overlay/Window.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include + +#include +#include + +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); + }; +} diff --git a/DDrawCompat/Win32/MsgHooks.cpp b/DDrawCompat/Win32/MsgHooks.cpp deleted file mode 100644 index 31c47c2..0000000 --- a/DDrawCompat/Win32/MsgHooks.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include - -#include -#include - -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); - } - } -} diff --git a/DDrawCompat/Win32/MsgHooks.h b/DDrawCompat/Win32/MsgHooks.h deleted file mode 100644 index 5923709..0000000 --- a/DDrawCompat/Win32/MsgHooks.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -namespace Win32 -{ - namespace MsgHooks - { - void installHooks(); - } -} diff --git a/DDrawCompat/arrow.bmp b/DDrawCompat/arrow.bmp new file mode 100644 index 0000000..62b6ffc Binary files /dev/null and b/DDrawCompat/arrow.bmp differ