diff --git a/logid.example.cfg b/logid.example.cfg index e49d7b5..5b64118 100644 --- a/logid.example.cfg +++ b/logid.example.cfg @@ -5,6 +5,7 @@ devices: ( { on: true; threshold: 30; + torque: 50; }; hiresscroll: { diff --git a/src/logid/backend/hidpp20/feature_defs.h b/src/logid/backend/hidpp20/feature_defs.h index 00f7993..3eebe42 100644 --- a/src/logid/backend/hidpp20/feature_defs.h +++ b/src/logid/backend/hidpp20/feature_defs.h @@ -74,6 +74,7 @@ namespace logid::backend::hidpp20 { POINTER_AXES_ORIENTATION = 0x2006, VERTICAL_SCROLLING = 0x2100, SMART_SHIFT = 0x2110, + SMART_SHIFT_V2 = 0x2111, HIRES_SCROLLING = 0x2120, HIRES_SCROLLING_V2 = 0x2121, // Referred to as Hi-res wheel in cvuchener/hidpp, seems to be V2? LORES_SCROLLING = 0x2130, diff --git a/src/logid/backend/hidpp20/features/SmartShift.cpp b/src/logid/backend/hidpp20/features/SmartShift.cpp index a5db620..5237582 100644 --- a/src/logid/backend/hidpp20/features/SmartShift.cpp +++ b/src/logid/backend/hidpp20/features/SmartShift.cpp @@ -19,26 +19,107 @@ using namespace logid::backend::hidpp20; -SmartShift::SmartShift(Device* dev) : Feature(dev, ID) { +SmartShift::SmartShift(Device* dev) : SmartShift(dev, ID) { } -SmartShift::SmartshiftStatus SmartShift::getStatus() { +SmartShift::SmartShift(Device* dev, uint16_t feature_id) : + Feature(dev, feature_id) { +} + +SmartShiftV2::SmartShiftV2(Device* dev) : SmartShift(dev, ID) { +} + +template +std::shared_ptr make_smartshift(Device* dev) { + try { + return std::make_shared(dev); + } catch (UnsupportedFeature& e) { + return {}; + } +} + +std::shared_ptr SmartShift::autoVersion(Device* dev) { + if (auto v2 = make_smartshift(dev)) + return v2; + + return std::make_shared(dev); +} + +SmartShift::Status SmartShift::getStatus() { std::vector params(0); - SmartshiftStatus status{}; + auto response = callFunction(GetStatus, params); - status.active = response[0] - 1; - status.autoDisengage = response[1]; - status.defaultAutoDisengage = response[2]; - return status; + + return { + .active = static_cast(response[0] - 1), + .autoDisengage = response[1], + .torque = 0, + .setActive = false, + .setAutoDisengage = false, + .setTorque = false + }; } -void SmartShift::setStatus(SmartshiftStatus status) { +SmartShift::Defaults SmartShift::getDefaults() { + std::vector params(0); + + auto response = callFunction(GetStatus, params); + + return { + .autoDisengage = response[2], + .torque = 0, + .maxForce = 0, + }; +} + +void SmartShift::setStatus(Status status) { std::vector params(3); if (status.setActive) params[0] = status.active + 1; if (status.setAutoDisengage) params[1] = status.autoDisengage; - if (status.setDefaultAutoDisengage) - params[2] = status.defaultAutoDisengage; callFunction(SetStatus, params); -} \ No newline at end of file +} + +SmartShift::Defaults SmartShiftV2::getDefaults() { + std::vector params(0); + auto response = callFunction(GetCapabilities, params); + + return { + .autoDisengage = response[1], + .torque = response[2], + .maxForce = response[3], + }; +} + +SmartShift::Status SmartShiftV2::getStatus() { + std::vector params(0); + auto response = callFunction(GetStatus, params); + + return { + .active = static_cast(response[0] - 1), + .autoDisengage = response[1], + .torque = response[2], + .setActive = false, .setAutoDisengage = false, .setTorque = false, + }; +} + +void SmartShiftV2::setStatus(Status status) { + std::vector params(3); + if (status.setActive) + params[0] = status.active + 1; + if (status.setAutoDisengage) + params[1] = status.autoDisengage; + if (status.setTorque) + params[2] = status.torque; + + callFunction(SetStatus, params); +} + +bool SmartShiftV2::supportsTorque() { + std::vector params(0); + auto response = callFunction(GetCapabilities, params); + + return static_cast(response[0] & 1); +} + diff --git a/src/logid/backend/hidpp20/features/SmartShift.h b/src/logid/backend/hidpp20/features/SmartShift.h index cdcfb7d..70e3357 100644 --- a/src/logid/backend/hidpp20/features/SmartShift.h +++ b/src/logid/backend/hidpp20/features/SmartShift.h @@ -20,13 +20,14 @@ #include #include +#include namespace logid::backend::hidpp20 { class SmartShift : public Feature { public: static const uint16_t ID = FeatureID::SMART_SHIFT; - uint16_t getID() final { return ID; } + uint16_t getID() override { return ID; } enum Function { GetStatus = 0, @@ -35,16 +36,54 @@ namespace logid::backend::hidpp20 { explicit SmartShift(Device* dev); - struct SmartshiftStatus { - bool active; + struct Defaults { uint8_t autoDisengage; - uint8_t defaultAutoDisengage; - bool setActive, setAutoDisengage, setDefaultAutoDisengage; + uint8_t torque; + uint8_t maxForce; }; - SmartshiftStatus getStatus(); + struct Status { + bool active; + uint8_t autoDisengage; + uint8_t torque; + bool setActive, setAutoDisengage, setTorque; + }; - void setStatus(SmartshiftStatus status); + [[nodiscard]] virtual bool supportsTorque() { return false; } + + [[nodiscard]] virtual Defaults getDefaults(); + + [[nodiscard]] virtual Status getStatus(); + + virtual void setStatus(Status status); + + [[nodiscard]] static std::shared_ptr autoVersion(Device* dev); + + protected: + SmartShift(Device* dev, uint16_t feature_id); + }; + + class SmartShiftV2 : public SmartShift + { + public: + static const uint16_t ID = FeatureID::SMART_SHIFT_V2; + uint16_t getID() final { return ID; } + + enum Function { + GetCapabilities = 0, + GetStatus = 1, + SetStatus = 2 + }; + + explicit SmartShiftV2(Device* dev); + + [[nodiscard]] bool supportsTorque() final; + + [[nodiscard]] Defaults getDefaults() final; + + [[nodiscard]] Status getStatus() final; + + void setStatus(Status status) final; }; } diff --git a/src/logid/config/schema.h b/src/logid/config/schema.h index 6f75699..6f16a8a 100644 --- a/src/logid/config/schema.h +++ b/src/logid/config/schema.h @@ -249,9 +249,10 @@ namespace logid::config { struct SmartShift : public group { std::optional on; std::optional threshold; + std::optional torque; - SmartShift() : group({"on", "threshold"}, - &SmartShift::on, &SmartShift::threshold) {} + SmartShift() : group({"on", "threshold", "torque"}, + &SmartShift::on, &SmartShift::threshold, &SmartShift::torque) {} }; diff --git a/src/logid/features/SmartShift.cpp b/src/logid/features/SmartShift.cpp index 9d960ba..dd6bff7 100644 --- a/src/logid/features/SmartShift.cpp +++ b/src/logid/features/SmartShift.cpp @@ -25,11 +25,35 @@ using namespace logid::backend; SmartShift::SmartShift(Device* device) : DeviceFeature(device), _config(device->activeProfile().smartshift) { try { - _smartshift = std::make_shared(&device->hidpp20()); + _smartshift = hidpp20::SmartShift::autoVersion(&device->hidpp20()); } catch (hidpp20::UnsupportedFeature& e) { throw UnsupportedFeature(); } + _torque_support = _smartshift->supportsTorque(); + _defaults = _smartshift->getDefaults(); + + if (_config.get().has_value()) { + auto& config = _config.get().value(); + + if (config.threshold.has_value()) { + auto& threshold = config.threshold.value(); + + /* 0 means no change, clip to 1. */ + if (threshold == 0) + threshold = 1; + } + + if (config.torque.has_value()) { + auto& torque = config.torque.value(); + /* torque is a percentage, clip between 1-100 */ + if (torque == 0) + torque = 1; + else if (torque > 100) + torque = 100; + } + } + _ipc_interface = _device->ipcNode()->make_interface(this); } @@ -45,6 +69,9 @@ void SmartShift::configure() { settings.setAutoDisengage = conf.threshold.has_value(); if (settings.setAutoDisengage) settings.autoDisengage = conf.threshold.value(); + settings.setTorque = conf.torque.has_value(); + if (settings.setTorque) + settings.torque = conf.torque.value(); _smartshift->setStatus(settings); } @@ -66,86 +93,108 @@ void SmartShift::setStatus(Status status) { _smartshift->setStatus(status); } +const hidpp20::SmartShift::Defaults& SmartShift::getDefaults() const { + return _defaults; +} + +bool SmartShift::supportsTorque() const { + return _torque_support; +} + SmartShift::IPC::IPC(SmartShift* parent) : ipcgull::interface( SERVICE_ROOT_NAME ".SmartShift", { - {"GetStatus", {this, &IPC::getStatus, {"active", "threshold"}}}, - {"SetActive", {this, &IPC::setActive, {"active"}}}, - {"SetThreshold", {this, &IPC::setThreshold, {"threshold"}}}, - {"GetDefault", {this, &IPC::getDefault, {"setActive", "active", "setThreshold", "threshold"}}}, - {"ClearDefaultActive", {this, &IPC::clearDefaultActive}}, - {"SetDefaultActive", {this, &IPC::setDefaultActive, {"active"}}}, - {"ClearDefaultThreshold", {this, &IPC::clearDefaultThreshold}}, - {"SetDefaultThreshold", {this, &IPC::setDefaultThreshold, {"threshold"}}} - }, {}, {}), + {"GetConfig", {this, &IPC::getConfig, {"active", "threshold", "torque"}}}, + {"SetActive", {this, &IPC::setActive, {"active", "clear"}}}, + {"SetThreshold", {this, &IPC::setThreshold, {"threshold", "clear"}}}, + {"SetTorque", {this, &IPC::setTorque, {"torque", "clear"}}}, + }, + { + {"TorqueSupport", ipcgull::property( + ipcgull::property_readable, parent->supportsTorque())}, + }, {}), _parent(*parent) { } -std::tuple SmartShift::IPC::getStatus() const { - auto ret = _parent.getStatus(); - return std::make_tuple(ret.active, ret.autoDisengage); +std::tuple SmartShift::IPC::getConfig() const { + std::shared_lock lock(_parent._config_mutex); + auto& config = _parent._config.get(); + auto& defaults = _parent.getDefaults(); + if (config.has_value()) { + auto& conf_value = config.value(); + return { + conf_value.on.has_value() ? (conf_value.on.value() ? 2 : 1) : 0, + conf_value.threshold.value_or(defaults.autoDisengage), + conf_value.torque.value_or(defaults.torque), + }; + } else { + return {0, 0, 0}; + } } -void SmartShift::IPC::setActive(bool active) { - Status status{}; - status.setActive = true; - status.active = active; - _parent.setStatus(status); +void SmartShift::IPC::setActive(bool active, bool clear) { + std::unique_lock lock(_parent._config_mutex); + auto& config = _parent._config.get(); + if (clear) { + if (config.has_value()) + config.value().on.reset(); + } else { + if (!config.has_value()) + config = config::SmartShift{}; + config.value().on = active; + Status status{}; + status.active = active, status.setActive = true; + _parent.setStatus(status); + } } -void SmartShift::IPC::setThreshold(uint8_t threshold) { +void SmartShift::IPC::setThreshold(uint8_t threshold, bool clear) { + std::unique_lock lock(_parent._config_mutex); + auto& config = _parent._config.get(); Status status{}; status.setAutoDisengage = true; - status.autoDisengage = threshold; + + /* clip threshold */ + if (threshold == 0) + threshold = 1; + + if (clear) { + if (config.has_value()) + config.value().threshold.reset(); + status.autoDisengage = _parent.getDefaults().autoDisengage; + } else { + if (!config.has_value()) + config = config::SmartShift{}; + config.value().threshold = threshold; + status.autoDisengage = threshold; + } _parent.setStatus(status); } -std::tuple SmartShift::IPC::getDefault() const { - std::shared_lock lock(_parent._config_mutex); - - auto& config = _parent._config.get(); - - if (!config.has_value()) - return {false, false, false, 0}; - - std::tuple ret; - std::get<0>(ret) = config.value().on.has_value(); - if (std::get<0>(ret)) - std::get<1>(ret) = config.value().on.value(); - std::get<2>(ret) = config.value().threshold.has_value(); - if (std::get<2>(ret)) - std::get<3>(ret) = config.value().threshold.value(); - - return ret; -} - -void SmartShift::IPC::clearDefaultActive() { +void SmartShift::IPC::setTorque(uint8_t torque, bool clear) { std::unique_lock lock(_parent._config_mutex); auto& config = _parent._config.get(); - if (config.has_value()) - config.value().on.reset(); -} + Status status{}; + status.setTorque = true; -void SmartShift::IPC::setDefaultActive(bool active) { - std::unique_lock lock(_parent._config_mutex); - auto& config = _parent._config.get(); - if (!config.has_value()) - config = config::SmartShift{}; - config.value().on = active; -} + /* clip torque */ + if (torque == 0) + torque = 1; + else if (torque > 100) + torque = 100; + if (!_parent.supportsTorque()) + throw std::invalid_argument("torque unsupported"); -void SmartShift::IPC::clearDefaultThreshold() { - std::unique_lock lock(_parent._config_mutex); - auto& config = _parent._config.get(); - if (config.has_value()) - config.value().threshold.reset(); -} - -void SmartShift::IPC::setDefaultThreshold(uint8_t threshold) { - std::unique_lock lock(_parent._config_mutex); - auto& config = _parent._config.get(); - if (!config.has_value()) - config = config::SmartShift{}; - config.value().threshold = threshold; + if (clear) { + if (config.has_value()) + config.value().torque.reset(); + status.torque = _parent.getDefaults().torque; + } else { + if (!config.has_value()) + config = config::SmartShift{}; + config.value().torque = torque; + status.torque = torque; + } + _parent.setStatus(status); } diff --git a/src/logid/features/SmartShift.h b/src/logid/features/SmartShift.h index 58abc06..078ec5a 100644 --- a/src/logid/features/SmartShift.h +++ b/src/logid/features/SmartShift.h @@ -27,19 +27,22 @@ namespace logid::features { class SmartShift : public DeviceFeature { public: - void configure() final; void listen() final; void setProfile(config::Profile& profile) final; - typedef backend::hidpp20::SmartShift::SmartshiftStatus Status; + typedef backend::hidpp20::SmartShift::Status Status; [[nodiscard]] Status getStatus() const; void setStatus(Status status); + [[nodiscard]] const backend::hidpp20::SmartShift::Defaults& getDefaults() const; + + [[nodiscard]] bool supportsTorque() const; + protected: explicit SmartShift(Device* dev); @@ -48,25 +51,20 @@ namespace logid::features { std::reference_wrapper> _config; std::shared_ptr _smartshift; + backend::hidpp20::SmartShift::Defaults _defaults{}; + bool _torque_support = false; + class IPC : public ipcgull::interface { public: explicit IPC(SmartShift* parent); - [[nodiscard]] std::tuple getStatus() const;; + [[nodiscard]] std::tuple getConfig() const; - void setActive(bool active); + void setActive(bool active, bool clear); - void setThreshold(uint8_t threshold); + void setThreshold(uint8_t threshold, bool clear); - [[nodiscard]] std::tuple getDefault() const; - - void clearDefaultActive(); - - void setDefaultActive(bool active); - - void clearDefaultThreshold(); - - void setDefaultThreshold(uint8_t threshold); + void setTorque(uint8_t torque, bool clear); private: SmartShift& _parent;