Merge pull request #261 from leios76/master

support smartshift(0x2111) on mx anywhere3
This commit is contained in:
pixl 2023-05-08 15:12:10 -04:00 committed by GitHub
commit 3fb18a7d5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 267 additions and 97 deletions

View File

@ -5,6 +5,7 @@ devices: (
{
on: true;
threshold: 30;
torque: 50;
};
hiresscroll:
{

View File

@ -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,

View File

@ -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<typename T>
std::shared_ptr<T> make_smartshift(Device* dev) {
try {
return std::make_shared<T>(dev);
} catch (UnsupportedFeature& e) {
return {};
}
}
std::shared_ptr<SmartShift> SmartShift::autoVersion(Device* dev) {
if (auto v2 = make_smartshift<SmartShiftV2>(dev))
return v2;
return std::make_shared<SmartShift>(dev);
}
SmartShift::Status SmartShift::getStatus() {
std::vector<uint8_t> 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<bool>(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<uint8_t> params(0);
auto response = callFunction(GetStatus, params);
return {
.autoDisengage = response[2],
.torque = 0,
.maxForce = 0,
};
}
void SmartShift::setStatus(Status status) {
std::vector<uint8_t> 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);
}
}
SmartShift::Defaults SmartShiftV2::getDefaults() {
std::vector<uint8_t> params(0);
auto response = callFunction(GetCapabilities, params);
return {
.autoDisengage = response[1],
.torque = response[2],
.maxForce = response[3],
};
}
SmartShift::Status SmartShiftV2::getStatus() {
std::vector<uint8_t> params(0);
auto response = callFunction(GetStatus, params);
return {
.active = static_cast<bool>(response[0] - 1),
.autoDisengage = response[1],
.torque = response[2],
.setActive = false, .setAutoDisengage = false, .setTorque = false,
};
}
void SmartShiftV2::setStatus(Status status) {
std::vector<uint8_t> 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<uint8_t> params(0);
auto response = callFunction(GetCapabilities, params);
return static_cast<bool>(response[0] & 1);
}

View File

@ -20,13 +20,14 @@
#include <backend/hidpp20/feature_defs.h>
#include <backend/hidpp20/Feature.h>
#include <memory>
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<SmartShift> 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;
};
}

View File

@ -249,9 +249,10 @@ namespace logid::config {
struct SmartShift : public group {
std::optional<bool> on;
std::optional<unsigned int> threshold;
std::optional<unsigned int> torque;
SmartShift() : group({"on", "threshold"},
&SmartShift::on, &SmartShift::threshold) {}
SmartShift() : group({"on", "threshold", "torque"},
&SmartShift::on, &SmartShift::threshold, &SmartShift::torque) {}
};

View File

@ -25,11 +25,35 @@ using namespace logid::backend;
SmartShift::SmartShift(Device* device) : DeviceFeature(device),
_config(device->activeProfile().smartshift) {
try {
_smartshift = std::make_shared<hidpp20::SmartShift>(&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<IPC>(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<bool>(
ipcgull::property_readable, parent->supportsTorque())},
}, {}),
_parent(*parent) {
}
std::tuple<bool, uint8_t> SmartShift::IPC::getStatus() const {
auto ret = _parent.getStatus();
return std::make_tuple(ret.active, ret.autoDisengage);
std::tuple<uint8_t, uint8_t, uint8_t> 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<bool, bool, bool, uint8_t> 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<bool, bool, bool, uint8_t> 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);
}

View File

@ -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<std::optional<config::SmartShift>> _config;
std::shared_ptr<backend::hidpp20::SmartShift> _smartshift;
backend::hidpp20::SmartShift::Defaults _defaults{};
bool _torque_support = false;
class IPC : public ipcgull::interface {
public:
explicit IPC(SmartShift* parent);
[[nodiscard]] std::tuple<bool, uint8_t> getStatus() const;;
[[nodiscard]] std::tuple<uint8_t, uint8_t, uint8_t> 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<bool, bool, bool, uint8_t> 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;