mirror of
https://github.com/PixlOne/logiops.git
synced 2025-07-14 05:12:34 +08:00
Merge pull request #292 from PixlOne/config_rework
Rework config interface
This commit is contained in:
commit
6614702929
@ -1 +1 @@
|
||||
Subproject commit ac4cd8f52eb6d632e885c2b1ed3eb4b310897800
|
||||
Subproject commit 22559236d8c6e30aac640f79b8d4983467ba5cc8
|
@ -1,7 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(logid)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
# C++20 is only needed for string literal template parameters
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../CMake")
|
||||
|
||||
|
@ -25,154 +25,41 @@
|
||||
|
||||
using namespace logid;
|
||||
using namespace libconfig;
|
||||
using namespace std::chrono;
|
||||
using namespace logid::config;
|
||||
|
||||
Configuration::Configuration(const std::string& config_file)
|
||||
Configuration::Configuration(const std::string& config_file) :
|
||||
_config_file (config_file)
|
||||
{
|
||||
try {
|
||||
_config.readFile(config_file.c_str());
|
||||
_config.readFile(_config_file);
|
||||
} catch(const FileIOException &e) {
|
||||
logPrintf(ERROR, "I/O Error while reading %s: %s", config_file.c_str(),
|
||||
e.what());
|
||||
throw e;
|
||||
logPrintf(ERROR, "I/O Error while reading %s: %s", _config_file.c_str(),
|
||||
e.what());
|
||||
throw;
|
||||
} catch(const ParseException &e) {
|
||||
logPrintf(ERROR, "Parse error in %s, line %d: %s", e.getFile(),
|
||||
e.getLine(), e.getError());
|
||||
throw e;
|
||||
e.getLine(), e.getError());
|
||||
throw;
|
||||
}
|
||||
|
||||
const Setting &root = _config.getRoot();
|
||||
Config::operator=(get<Config>(_config.getRoot()));
|
||||
|
||||
if(!devices.has_value())
|
||||
devices = decltype(config::Config::devices)();
|
||||
}
|
||||
|
||||
void Configuration::save()
|
||||
{
|
||||
config::set(_config.getRoot(), *this);
|
||||
try {
|
||||
auto& worker_count = root["workers"];
|
||||
if(worker_count.getType() == Setting::TypeInt) {
|
||||
_worker_threads = worker_count;
|
||||
if(_worker_threads < 0)
|
||||
logPrintf(WARN, "Line %d: workers cannot be negative.",
|
||||
worker_count.getSourceLine());
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: workers must be an integer.",
|
||||
worker_count.getSourceLine());
|
||||
}
|
||||
} catch(const SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
_config.writeFile(_config_file);
|
||||
} catch(const FileIOException &e) {
|
||||
logPrintf(ERROR, "I/O Error while writing %s: %s",
|
||||
_config_file.c_str(), e.what());
|
||||
throw;
|
||||
} catch(const std::exception& e) {
|
||||
logPrintf(ERROR, "Error while writing %s: %s",
|
||||
_config_file.c_str(), e.what());
|
||||
throw;
|
||||
}
|
||||
|
||||
try {
|
||||
auto& timeout = root["io_timeout"];
|
||||
if(timeout.isNumber()) {
|
||||
if(timeout.getType() == Setting::TypeFloat)
|
||||
_io_timeout = duration_cast<milliseconds>(
|
||||
duration<double, std::milli>(timeout));
|
||||
else
|
||||
_io_timeout = milliseconds((int)timeout);
|
||||
} else
|
||||
logPrintf(WARN, "Line %d: io_timeout must be a number.",
|
||||
timeout.getSourceLine());
|
||||
} catch(const SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
try {
|
||||
auto& devices = root["devices"];
|
||||
|
||||
for(int i = 0; i < devices.getLength(); i++) {
|
||||
const Setting& device = devices[i];
|
||||
std::string name;
|
||||
try {
|
||||
if(!device.lookupValue("name", name)) {
|
||||
logPrintf(WARN, "Line %d: 'name' must be a string, skipping"
|
||||
" device.", device["name"].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
} catch(SettingNotFoundException &e) {
|
||||
logPrintf(WARN, "Line %d: Missing name field, skipping device."
|
||||
, device.getSourceLine());
|
||||
continue;
|
||||
}
|
||||
_device_paths.insert({name, device.getPath()});
|
||||
}
|
||||
}
|
||||
catch(const SettingNotFoundException &e) {
|
||||
logPrintf(WARN, "No devices listed in config file.");
|
||||
}
|
||||
|
||||
try {
|
||||
auto& ignore = root.lookup("ignore");
|
||||
if(ignore.getType() == libconfig::Setting::TypeInt) {
|
||||
_ignore_list.insert((int)ignore);
|
||||
} else if(ignore.isList() || ignore.isArray()) {
|
||||
int ignore_count = ignore.getLength();
|
||||
for(int i = 0; i < ignore_count; i++) {
|
||||
if(ignore[i].getType() != libconfig::Setting::TypeInt) {
|
||||
logPrintf(WARN, "Line %d: ignore must refer to device PIDs",
|
||||
ignore[i].getSourceLine());
|
||||
if(ignore.isArray())
|
||||
break;
|
||||
} else
|
||||
_ignore_list.insert((int)ignore[i]);
|
||||
}
|
||||
}
|
||||
} catch(const SettingNotFoundException& e) {
|
||||
// May be called blacklist
|
||||
try {
|
||||
auto& ignore = root.lookup("blacklist");
|
||||
if(ignore.getType() == libconfig::Setting::TypeInt) {
|
||||
_ignore_list.insert((int)ignore);
|
||||
} else if(ignore.isList() || ignore.isArray()) {
|
||||
int ignore_count = ignore.getLength();
|
||||
for(int i = 0; i < ignore_count; i++) {
|
||||
if(ignore[i].getType() != libconfig::Setting::TypeInt) {
|
||||
logPrintf(WARN, "Line %d: blacklist must refer to "
|
||||
"device PIDs",
|
||||
ignore[i].getSourceLine());
|
||||
if(ignore.isArray())
|
||||
break;
|
||||
} else
|
||||
_ignore_list.insert((int)ignore[i]);
|
||||
}
|
||||
}
|
||||
} catch(const SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libconfig::Setting& Configuration::getSetting(const std::string& path)
|
||||
{
|
||||
return _config.lookup(path);
|
||||
}
|
||||
|
||||
std::string Configuration::getDevice(const std::string& name)
|
||||
{
|
||||
auto it = _device_paths.find(name);
|
||||
if(it == _device_paths.end())
|
||||
throw DeviceNotFound(name);
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool Configuration::isIgnored(uint16_t pid) const
|
||||
{
|
||||
return _ignore_list.find(pid) != _ignore_list.end();
|
||||
}
|
||||
|
||||
Configuration::DeviceNotFound::DeviceNotFound(std::string name) :
|
||||
_name (std::move(name))
|
||||
{
|
||||
}
|
||||
|
||||
const char * Configuration::DeviceNotFound::what() const noexcept
|
||||
{
|
||||
return _name.c_str();
|
||||
}
|
||||
|
||||
int Configuration::workerCount() const
|
||||
{
|
||||
return _worker_threads;
|
||||
}
|
||||
|
||||
std::chrono::milliseconds Configuration::ioTimeout() const
|
||||
{
|
||||
return _io_timeout;
|
||||
}
|
||||
}
|
@ -25,36 +25,27 @@
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
|
||||
#define LOGID_DEFAULT_IO_TIMEOUT std::chrono::milliseconds(400)
|
||||
#define LOGID_DEFAULT_WORKER_COUNT 4
|
||||
#include "config/schema.h"
|
||||
|
||||
namespace logid
|
||||
{
|
||||
class Configuration
|
||||
namespace defaults {
|
||||
static constexpr double io_timeout = 500;
|
||||
static constexpr int gesture_threshold = 50;
|
||||
static constexpr int worker_count = 4;
|
||||
}
|
||||
|
||||
class Configuration : public config::Config
|
||||
{
|
||||
public:
|
||||
explicit Configuration(const std::string& config_file);
|
||||
Configuration() = default;
|
||||
libconfig::Setting& getSetting(const std::string& path);
|
||||
std::string getDevice(const std::string& name);
|
||||
bool isIgnored(uint16_t pid) const;
|
||||
|
||||
class DeviceNotFound : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit DeviceNotFound(std::string name);
|
||||
const char* what() const noexcept override;
|
||||
private:
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
std::chrono::milliseconds ioTimeout() const;
|
||||
int workerCount() const;
|
||||
// Reloading is not safe, references will be invalidated
|
||||
//void reload();
|
||||
void save();
|
||||
private:
|
||||
std::map<std::string, std::string> _device_paths;
|
||||
std::set<uint16_t> _ignore_list;
|
||||
std::chrono::milliseconds _io_timeout = LOGID_DEFAULT_IO_TIMEOUT;
|
||||
int _worker_threads = LOGID_DEFAULT_WORKER_COUNT;
|
||||
std::string _config_file;
|
||||
libconfig::Config _config;
|
||||
};
|
||||
|
||||
|
@ -98,9 +98,10 @@ std::shared_ptr<Device> Device::make(
|
||||
Device::Device(std::string path, backend::hidpp::DeviceIndex index,
|
||||
std::shared_ptr<DeviceManager> manager) :
|
||||
_hidpp20 (path, index,
|
||||
manager->config()->ioTimeout(), manager->workQueue()),
|
||||
manager->config()->io_timeout.value_or(defaults::io_timeout),
|
||||
manager->workQueue()),
|
||||
_path (std::move(path)), _index (index),
|
||||
_config (manager->config(), this),
|
||||
_config (_getConfig(manager, _hidpp20.name())),
|
||||
_receiver (nullptr),
|
||||
_manager (manager),
|
||||
_nickname (manager),
|
||||
@ -115,7 +116,7 @@ Device::Device(std::shared_ptr<backend::raw::RawDevice> raw_device,
|
||||
std::shared_ptr<DeviceManager> manager) :
|
||||
_hidpp20(raw_device, index),
|
||||
_path (raw_device->hidrawPath()), _index (index),
|
||||
_config (manager->config(), this),
|
||||
_config (_getConfig(manager, _hidpp20.name())),
|
||||
_receiver (nullptr),
|
||||
_manager (manager),
|
||||
_nickname (manager),
|
||||
@ -129,7 +130,7 @@ Device::Device(Receiver* receiver, hidpp::DeviceIndex index,
|
||||
std::shared_ptr<DeviceManager> manager) :
|
||||
_hidpp20 (receiver->rawReceiver(), index),
|
||||
_path (receiver->path()), _index (index),
|
||||
_config (manager->config(), this),
|
||||
_config (_getConfig(manager, _hidpp20.name())),
|
||||
_receiver (receiver),
|
||||
_manager (manager),
|
||||
_nickname (manager),
|
||||
@ -144,6 +145,10 @@ void Device::_init()
|
||||
logPrintf(INFO, "Device found: %s on %s:%d", name().c_str(),
|
||||
hidpp20().devicePath().c_str(), _index);
|
||||
|
||||
_profile = _config.profiles.find(_config.default_profile);
|
||||
if(_profile == _config.profiles.end())
|
||||
_profile = _config.profiles.insert({_config.default_profile, {}}).first;
|
||||
|
||||
_addFeature<features::DPI>("dpi");
|
||||
_addFeature<features::SmartShift>("smartshift");
|
||||
_addFeature<features::HiresScroll>("hiresscroll");
|
||||
@ -239,9 +244,18 @@ std::shared_ptr<ipcgull::node> Device::ipcNode() const
|
||||
return _ipc_node;
|
||||
}
|
||||
|
||||
Device::Config& Device::config()
|
||||
/*config::Device& Device::config()
|
||||
{
|
||||
return _config;
|
||||
}*/
|
||||
|
||||
config::Profile& Device::activeProfile()
|
||||
{
|
||||
return _profile->second;
|
||||
}
|
||||
const config::Profile& Device::activeProfile() const
|
||||
{
|
||||
return _profile->second;
|
||||
}
|
||||
|
||||
hidpp20::Device& Device::hidpp20()
|
||||
@ -285,118 +299,28 @@ void Device::DeviceIPC::notifyStatus() const
|
||||
emit_signal("StatusChanged", (bool)(_device._awake));
|
||||
}
|
||||
|
||||
Device::Config::Config(const std::shared_ptr<Configuration>& config, Device*
|
||||
device) : _device (device), _config (config)
|
||||
config::Device& Device::_getConfig(
|
||||
const std::shared_ptr<DeviceManager>& manager,
|
||||
const std::string& name)
|
||||
{
|
||||
try {
|
||||
_root_setting = config->getDevice(device->name());
|
||||
} catch(Configuration::DeviceNotFound& e) {
|
||||
logPrintf(INFO, "Device %s not configured, using default config.",
|
||||
device->name().c_str());
|
||||
return;
|
||||
static std::mutex config_mutex;
|
||||
std::lock_guard<std::mutex> lock(config_mutex);
|
||||
auto& devices = manager->config()->devices;
|
||||
if(!devices.has_value())
|
||||
devices = decltype(config::Config::devices)();
|
||||
auto& device = devices.value()[name];
|
||||
if(std::holds_alternative<config::Profile>(device)) {
|
||||
config::Device d;
|
||||
d.profiles["default"] = std::get<config::Profile>(device);
|
||||
d.default_profile = "default";
|
||||
device = std::move(d);
|
||||
}
|
||||
|
||||
try {
|
||||
auto& profiles = _config->getSetting(_root_setting + "/profiles");
|
||||
int profile_index = 0;
|
||||
if(!profiles.isList()) {
|
||||
logPrintf(WARN, "Line %d: profiles must be a list, defaulting to"
|
||||
"old-style config", profiles.getSourceLine());
|
||||
}
|
||||
|
||||
try {
|
||||
auto& default_profile = _config->getSetting(_root_setting +
|
||||
"/default_profile");
|
||||
if(default_profile.getType() == libconfig::Setting::TypeString) {
|
||||
_profile_name = (const char*)default_profile;
|
||||
} else if(default_profile.isNumber()) {
|
||||
profile_index = default_profile;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: default_profile must be a string or"
|
||||
" integer, defaulting to first profile",
|
||||
default_profile.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(INFO, "Line %d: default_profile missing, defaulting to "
|
||||
"first profile", profiles.getSourceLine());
|
||||
}
|
||||
|
||||
if(profiles.getLength() <= profile_index) {
|
||||
if(profiles.getLength() == 0) {
|
||||
logPrintf(WARN, "Line %d: No profiles defined",
|
||||
profiles.getSourceLine());
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: default_profile does not exist, "
|
||||
"defaulting to first",
|
||||
profiles.getSourceLine());
|
||||
profile_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < profiles.getLength(); i++) {
|
||||
const auto& profile = profiles[i];
|
||||
std::string name;
|
||||
if(!profile.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: Profile must be a group, "
|
||||
"skipping", profile.getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const auto& name_setting = profile.lookup("name");
|
||||
if(name_setting.getType() !=
|
||||
libconfig::Setting::TypeString) {
|
||||
logPrintf(WARN, "Line %d: Profile names must be "
|
||||
"strings, using name \"%d\"",
|
||||
name_setting.getSourceLine(), i);
|
||||
name = std::to_string(i);
|
||||
} else {
|
||||
name = (const char *) name_setting;
|
||||
}
|
||||
|
||||
if(_profiles.find(name) != _profiles.end()) {
|
||||
logPrintf(WARN, "Line %d: Profile with the same name "
|
||||
"already exists, skipping.",
|
||||
name_setting.getSourceLine());
|
||||
continue;
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(INFO, "Line %d: Profile is unnamed, using name "
|
||||
"\"%d\"", profile.getSourceLine(), i);
|
||||
}
|
||||
|
||||
if(i == profile_index && _profile_name.empty())
|
||||
_profile_root = profile.getPath();
|
||||
_profiles[name] = profile.getPath();
|
||||
}
|
||||
|
||||
if(_profiles.empty()) {
|
||||
_profile_name = "0";
|
||||
}
|
||||
|
||||
if(_profile_root.empty())
|
||||
_profile_root = _profiles[_profile_name];
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// No profiles, default to root
|
||||
_profile_root = _root_setting;
|
||||
auto& conf = std::get<config::Device>(device);
|
||||
if(conf.profiles.empty()) {
|
||||
conf.profiles["default"] = std::get<config::Profile>(device);
|
||||
conf.default_profile = "default";
|
||||
}
|
||||
}
|
||||
|
||||
libconfig::Setting& Device::Config::getSetting(const std::string& path)
|
||||
{
|
||||
if(_profile_root.empty())
|
||||
throw libconfig::SettingNotFoundException((_root_setting + '/' +
|
||||
path).c_str());
|
||||
return _config->getSetting(_profile_root + '/' + path);
|
||||
}
|
||||
|
||||
const std::map<std::string, std::string> & Device::Config::getProfiles() const
|
||||
{
|
||||
return _profiles;
|
||||
}
|
||||
|
||||
void Device::Config::setProfile(const std::string &name)
|
||||
{
|
||||
_profile_name = name;
|
||||
_profile_root = _profiles[name];
|
||||
return conf;
|
||||
}
|
||||
|
@ -59,7 +59,9 @@ namespace logid
|
||||
std::string name();
|
||||
uint16_t pid();
|
||||
|
||||
Config& config();
|
||||
//config::Device& config();
|
||||
config::Profile& activeProfile();
|
||||
const config::Profile& activeProfile() const;
|
||||
backend::hidpp20::Device& hidpp20();
|
||||
|
||||
static std::shared_ptr<Device> make(
|
||||
@ -108,6 +110,10 @@ namespace logid
|
||||
Device(Receiver* receiver, backend::hidpp::DeviceIndex index,
|
||||
std::shared_ptr<DeviceManager> manager);
|
||||
|
||||
static config::Device& _getConfig(
|
||||
const std::shared_ptr<DeviceManager>& manager,
|
||||
const std::string& name);
|
||||
|
||||
void _init();
|
||||
|
||||
/* Adds a feature without calling an error if unsupported */
|
||||
@ -120,29 +126,13 @@ namespace logid
|
||||
}
|
||||
}
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
Config(const std::shared_ptr<Configuration>& config, Device*
|
||||
device);
|
||||
libconfig::Setting& getSetting(const std::string& path);
|
||||
const std::map<std::string, std::string>& getProfiles() const;
|
||||
void setProfile(const std::string& name);
|
||||
private:
|
||||
Device* _device;
|
||||
std::string _root_setting;
|
||||
std::string _profile_root;
|
||||
std::string _profile_name;
|
||||
std::map<std::string, std::string> _profiles;
|
||||
std::shared_ptr<Configuration> _config;
|
||||
};
|
||||
|
||||
backend::hidpp20::Device _hidpp20;
|
||||
std::string _path;
|
||||
backend::hidpp::DeviceIndex _index;
|
||||
std::map<std::string, std::shared_ptr<features::DeviceFeature>>
|
||||
_features;
|
||||
Config _config;
|
||||
config::Device& _config;
|
||||
std::map<std::string, config::Profile>::iterator _profile;
|
||||
|
||||
Receiver* _receiver;
|
||||
const std::weak_ptr<DeviceManager> _manager;
|
||||
|
@ -44,7 +44,7 @@ namespace logid {
|
||||
DeviceManager::DeviceManager(std::shared_ptr<Configuration> config,
|
||||
std::shared_ptr<InputDevice> virtual_input,
|
||||
std::shared_ptr<ipcgull::server> server) :
|
||||
backend::raw::DeviceMonitor(config->workerCount()),
|
||||
backend::raw::DeviceMonitor(config->workers.value_or(defaults::worker_count)),
|
||||
_server (std::move(server)), _config (std::move(config)),
|
||||
_virtual_input (std::move(virtual_input)),
|
||||
_root_node (ipcgull::node::make_root("")),
|
||||
@ -95,8 +95,11 @@ void DeviceManager::addDevice(std::string path)
|
||||
|
||||
// Check if device is ignored before continuing
|
||||
{
|
||||
raw::RawDevice raw_dev(path, config()->ioTimeout(), workQueue());
|
||||
if(config()->isIgnored(raw_dev.productId())) {
|
||||
raw::RawDevice raw_dev(
|
||||
path,config()->io_timeout.value_or(defaults::io_timeout),
|
||||
workQueue());
|
||||
if(config()->ignore.has_value() &&
|
||||
config()->ignore.value().contains(raw_dev.productId())) {
|
||||
logPrintf(DEBUG, "%s: Device 0x%04x ignored.",
|
||||
path.c_str(), raw_dev.productId());
|
||||
return;
|
||||
@ -104,8 +107,10 @@ void DeviceManager::addDevice(std::string path)
|
||||
}
|
||||
|
||||
try {
|
||||
hidpp::Device device(path, hidpp::DefaultDevice,
|
||||
config()->ioTimeout(), workQueue());
|
||||
hidpp::Device device(
|
||||
path, hidpp::DefaultDevice,
|
||||
config()->io_timeout.value_or(defaults::io_timeout),
|
||||
workQueue());
|
||||
isReceiver = device.version() == std::make_tuple(1, 0);
|
||||
} catch(hidpp10::Error &e) {
|
||||
if(e.code() != hidpp10::Error::UnknownDevice)
|
||||
|
@ -65,7 +65,8 @@ std::shared_ptr<Receiver> Receiver::make(
|
||||
Receiver::Receiver(const std::string& path,
|
||||
const std::shared_ptr<DeviceManager>& manager) :
|
||||
dj::ReceiverMonitor(path,
|
||||
manager->config()->ioTimeout(),
|
||||
manager->config()->io_timeout.value_or(
|
||||
defaults::io_timeout),
|
||||
manager->workQueue()),
|
||||
_path (path), _manager (manager), _nickname (manager),
|
||||
_ipc_node (manager->receiversNode()->make_child(_nickname)),
|
||||
@ -99,7 +100,8 @@ void Receiver::addDevice(hidpp::DeviceConnectionEvent event)
|
||||
|
||||
try {
|
||||
// Check if device is ignored before continuing
|
||||
if(manager->config()->isIgnored(event.pid)) {
|
||||
if(manager->config()->ignore.value_or(
|
||||
std::set<uint16_t>()).contains(event.pid)) {
|
||||
logPrintf(DEBUG, "%s:%d: Device 0x%04x ignored.",
|
||||
_path.c_str(), event.index, event.pid);
|
||||
return;
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include "Action.h"
|
||||
#include "../util/log.h"
|
||||
#include "KeypressAction.h"
|
||||
#include "ToggleSmartShift.h"
|
||||
#include "ToggleHiresScroll.h"
|
||||
@ -31,49 +30,39 @@
|
||||
using namespace logid;
|
||||
using namespace logid::actions;
|
||||
|
||||
std::shared_ptr<Action> Action::makeAction(Device *device, libconfig::Setting
|
||||
&setting)
|
||||
template <typename T>
|
||||
struct action_type {
|
||||
typedef typename T::action type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct action_type<const T> : action_type<T> { };
|
||||
|
||||
template <typename T>
|
||||
struct action_type<T&> : action_type<T> { };
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<Action> _makeAction(Device* device,
|
||||
T action) {
|
||||
return std::make_shared<typename action_type<T>::type>(device, action);
|
||||
}
|
||||
|
||||
std::shared_ptr<Action> Action::makeAction(Device *device,
|
||||
config::BasicAction& action)
|
||||
{
|
||||
if(!setting.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: Action is not a group, ignoring.",
|
||||
setting.getSourceLine());
|
||||
throw InvalidAction();
|
||||
}
|
||||
std::shared_ptr<Action> ret;
|
||||
std::visit([&device, &ret](auto&& x) {
|
||||
ret = _makeAction(device, x);
|
||||
}, action);
|
||||
return ret;
|
||||
}
|
||||
|
||||
try {
|
||||
auto& action_type = setting.lookup("type");
|
||||
|
||||
if(action_type.getType() != libconfig::Setting::TypeString) {
|
||||
logPrintf(WARN, "Line %d: Action type must be a string",
|
||||
action_type.getSourceLine());
|
||||
throw InvalidAction();
|
||||
}
|
||||
|
||||
std::string type = action_type;
|
||||
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
||||
|
||||
if(type == "keypress")
|
||||
return std::make_shared<KeypressAction>(device, setting);
|
||||
else if(type == "togglesmartshift")
|
||||
return std::make_shared<ToggleSmartShift>(device);
|
||||
else if(type == "togglehiresscroll")
|
||||
return std::make_shared<ToggleHiresScroll>(device);
|
||||
else if(type == "gestures")
|
||||
return std::make_shared<GestureAction>(device, setting);
|
||||
else if(type == "cycledpi")
|
||||
return std::make_shared<CycleDPI>(device, setting);
|
||||
else if(type == "changedpi")
|
||||
return std::make_shared<ChangeDPI>(device, setting);
|
||||
else if(type == "none")
|
||||
return std::make_shared<NullAction>(device);
|
||||
else if(type == "changehost")
|
||||
return std::make_shared<ChangeHostAction>(device, setting);
|
||||
else
|
||||
throw InvalidAction(type);
|
||||
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: Action type is missing, ignoring.",
|
||||
setting.getSourceLine());
|
||||
throw InvalidAction();
|
||||
}
|
||||
}
|
||||
std::shared_ptr<Action> Action::makeAction(Device *device,
|
||||
config::Action& action)
|
||||
{
|
||||
std::shared_ptr<Action> ret;
|
||||
std::visit([&device, &ret](auto&& x) {
|
||||
ret = _makeAction(device, x);
|
||||
}, action);
|
||||
return ret;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <atomic>
|
||||
#include <libconfig.h++>
|
||||
#include <memory>
|
||||
#include "../config/schema.h"
|
||||
|
||||
namespace logid {
|
||||
class Device;
|
||||
@ -46,7 +47,10 @@ namespace actions {
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<Action> makeAction(Device* device,
|
||||
libconfig::Setting& setting);
|
||||
config::BasicAction& action);
|
||||
|
||||
static std::shared_ptr<Action> makeAction(Device* device,
|
||||
config::Action& action);
|
||||
|
||||
virtual void press() = 0;
|
||||
virtual void release() = 0;
|
||||
@ -65,14 +69,6 @@ namespace actions {
|
||||
|
||||
virtual ~Action() = default;
|
||||
|
||||
class Config
|
||||
{
|
||||
protected:
|
||||
explicit Config(Device* device) : _device (device)
|
||||
{
|
||||
}
|
||||
Device* _device;
|
||||
};
|
||||
protected:
|
||||
explicit Action(Device* device) : _device (device), _pressed (false)
|
||||
{
|
||||
|
@ -24,8 +24,8 @@
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
ChangeDPI::ChangeDPI(Device *device, libconfig::Setting &setting) :
|
||||
Action(device), _config(device, setting)
|
||||
ChangeDPI::ChangeDPI(Device *device, config::ChangeDPI& config) :
|
||||
Action(device), _config (config)
|
||||
{
|
||||
_dpi = _device->getFeature<features::DPI>("dpi");
|
||||
if(!_dpi)
|
||||
@ -42,15 +42,16 @@ void ChangeDPI::press()
|
||||
task::spawn(_device->workQueue(),
|
||||
[this]{
|
||||
try {
|
||||
uint16_t last_dpi = _dpi->getDPI(_config.sensor());
|
||||
_dpi->setDPI(last_dpi + _config.interval(), _config.sensor());
|
||||
uint16_t last_dpi = _dpi->getDPI(_config.sensor.value_or(0));
|
||||
_dpi->setDPI(last_dpi + _config.inc,
|
||||
_config.sensor.value_or(0));
|
||||
} catch (backend::hidpp20::Error& e) {
|
||||
if(e.code() == backend::hidpp20::Error::InvalidArgument)
|
||||
logPrintf(WARN, "%s:%d: Could not get/set DPI for sensor "
|
||||
"%d",
|
||||
_device->hidpp20().devicePath().c_str(),
|
||||
_device->hidpp20().deviceIndex(),
|
||||
_config.sensor());
|
||||
_config.sensor.value_or(0));
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
@ -67,44 +68,3 @@ uint8_t ChangeDPI::reprogFlags() const
|
||||
{
|
||||
return backend::hidpp20::ReprogControls::TemporaryDiverted;
|
||||
}
|
||||
|
||||
ChangeDPI::Config::Config(Device *device, libconfig::Setting &config) :
|
||||
Action::Config(device), _interval (0), _sensor (0)
|
||||
{
|
||||
if(!config.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: action must be an object, skipping.",
|
||||
config.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto& inc = config.lookup("inc");
|
||||
if(inc.getType() != libconfig::Setting::TypeInt)
|
||||
logPrintf(WARN, "Line %d: inc must be an integer",
|
||||
inc.getSourceLine());
|
||||
_interval = (int)inc;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: inc is a required field, skipping.",
|
||||
config.getSourceLine());
|
||||
}
|
||||
|
||||
try {
|
||||
auto& sensor = config.lookup("sensor");
|
||||
if(sensor.getType() != libconfig::Setting::TypeInt)
|
||||
logPrintf(WARN, "Line %d: sensor must be an integer",
|
||||
sensor.getSourceLine());
|
||||
_sensor = (int)sensor;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t ChangeDPI::Config::interval() const
|
||||
{
|
||||
return _interval;
|
||||
}
|
||||
|
||||
uint8_t ChangeDPI::Config::sensor() const
|
||||
{
|
||||
return _sensor;
|
||||
}
|
@ -27,26 +27,15 @@ namespace logid {
|
||||
class ChangeDPI : public Action
|
||||
{
|
||||
public:
|
||||
explicit ChangeDPI(Device* device, libconfig::Setting& setting);
|
||||
explicit ChangeDPI(Device* device, config::ChangeDPI& setting);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
|
||||
class Config : public Action::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& setting);
|
||||
uint16_t interval() const;
|
||||
uint8_t sensor() const;
|
||||
private:
|
||||
uint16_t _interval;
|
||||
uint8_t _sensor;
|
||||
};
|
||||
|
||||
protected:
|
||||
Config _config;
|
||||
config::ChangeDPI& _config;
|
||||
std::shared_ptr<features::DPI> _dpi;
|
||||
};
|
||||
}}
|
||||
|
@ -24,9 +24,14 @@
|
||||
using namespace logid::actions;
|
||||
using namespace logid::backend;
|
||||
|
||||
ChangeHostAction::ChangeHostAction(Device *device, libconfig::Setting&
|
||||
config) : Action(device), _config (device, config)
|
||||
ChangeHostAction::ChangeHostAction(Device *device, config::ChangeHost& config)
|
||||
: Action(device), _config (config)
|
||||
{
|
||||
if(std::holds_alternative<std::string>(_config.host)) {
|
||||
auto& host = std::get<std::string>(_config.host);
|
||||
std::transform(host.begin(), host.end(),
|
||||
host.begin(), ::tolower);
|
||||
}
|
||||
try {
|
||||
_change_host = std::make_shared<hidpp20::ChangeHost>(&device->hidpp20());
|
||||
} catch (hidpp20::UnsupportedFeature& e) {
|
||||
@ -47,9 +52,21 @@ void ChangeHostAction::release()
|
||||
task::spawn(_device->workQueue(),
|
||||
[this] {
|
||||
auto host_info = _change_host->getHostInfo();
|
||||
auto next_host = _config.nextHost(host_info);
|
||||
int next_host;
|
||||
if(std::holds_alternative<std::string>(_config.host)) {
|
||||
const auto& host = std::get<std::string>(_config.host);
|
||||
if(host == "next")
|
||||
next_host = host_info.currentHost + 1;
|
||||
else if(host == "prev" || host == "previous")
|
||||
next_host = host_info.currentHost - 1;
|
||||
else
|
||||
next_host = host_info.currentHost;
|
||||
} else {
|
||||
next_host = std::get<int>(_config.host)-1;
|
||||
}
|
||||
next_host %= host_info.hostCount;
|
||||
if(next_host != host_info.currentHost)
|
||||
_change_host->setHost(next_host);
|
||||
_change_host->setHost(next_host-1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -58,63 +75,3 @@ uint8_t ChangeHostAction::reprogFlags() const
|
||||
{
|
||||
return hidpp20::ReprogControls::TemporaryDiverted;
|
||||
}
|
||||
|
||||
ChangeHostAction::Config::Config(Device *device, libconfig::Setting& config)
|
||||
: Action::Config(device)
|
||||
{
|
||||
try {
|
||||
auto& host = config.lookup("host");
|
||||
if(host.getType() == libconfig::Setting::TypeInt) {
|
||||
_offset = false;
|
||||
_host = host;
|
||||
_host--; // hosts are one-indexed in config
|
||||
|
||||
if(_host < 0) {
|
||||
logPrintf(WARN, "Line %d: host must be positive.",
|
||||
host.getSourceLine());
|
||||
_offset = true;
|
||||
_host = 0;
|
||||
}
|
||||
|
||||
} else if(host.getType() == libconfig::Setting::TypeString) {
|
||||
_offset = true;
|
||||
std::string hostmode = host;
|
||||
std::transform(hostmode.begin(), hostmode.end(),
|
||||
hostmode.begin(), ::tolower);
|
||||
|
||||
if(hostmode == "next")
|
||||
_host = 1;
|
||||
else if(hostmode == "prev" || hostmode == "previous")
|
||||
_host = -1;
|
||||
else {
|
||||
logPrintf(WARN, "Line %d: host must equal an integer, 'next',"
|
||||
"or 'prev'.", host.getSourceLine());
|
||||
_host = 0;
|
||||
}
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: host must equal an integer, 'next',"
|
||||
"or 'prev'.", host.getSourceLine());
|
||||
_offset = true;
|
||||
_host = 0;
|
||||
}
|
||||
} catch (libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: host is a required field, skipping.",
|
||||
config.getSourceLine());
|
||||
_offset = true;
|
||||
_host = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ChangeHostAction::Config::nextHost(hidpp20::ChangeHost::HostInfo info)
|
||||
{
|
||||
if(_offset) {
|
||||
return (info.currentHost + _host) % info.hostCount;
|
||||
} else {
|
||||
if(_host >= info.hostCount || _host < 0) {
|
||||
logPrintf(WARN, "No such host %d, defaulting to current.",
|
||||
_host+1);
|
||||
return info.currentHost;
|
||||
} else
|
||||
return _host;
|
||||
}
|
||||
}
|
@ -28,26 +28,16 @@ namespace actions
|
||||
class ChangeHostAction : public Action
|
||||
{
|
||||
public:
|
||||
ChangeHostAction(Device* device, libconfig::Setting& config);
|
||||
ChangeHostAction(Device* device, config::ChangeHost& config);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
|
||||
class Config : public Action::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& setting);
|
||||
uint8_t nextHost(backend::hidpp20::ChangeHost::HostInfo info);
|
||||
private:
|
||||
bool _offset;
|
||||
int _host;
|
||||
};
|
||||
|
||||
protected:
|
||||
std::shared_ptr<backend::hidpp20::ChangeHost> _change_host;
|
||||
Config _config;
|
||||
config::ChangeHost& _config;
|
||||
};
|
||||
}}
|
||||
|
||||
|
@ -25,8 +25,8 @@
|
||||
using namespace logid::actions;
|
||||
using namespace libconfig;
|
||||
|
||||
CycleDPI::CycleDPI(Device* device, libconfig::Setting& setting) :
|
||||
Action (device), _config (device, setting)
|
||||
CycleDPI::CycleDPI(Device* device, config::CycleDPI& config) :
|
||||
Action (device), _config (config), _current_dpi (_config.dpis.begin())
|
||||
{
|
||||
_dpi = _device->getFeature<features::DPI>("dpi");
|
||||
if(!_dpi)
|
||||
@ -39,18 +39,21 @@ CycleDPI::CycleDPI(Device* device, libconfig::Setting& setting) :
|
||||
void CycleDPI::press()
|
||||
{
|
||||
_pressed = true;
|
||||
if(_dpi && !_config.empty()) {
|
||||
if(_dpi && !_config.dpis.empty()) {
|
||||
task::spawn(_device->workQueue(),
|
||||
[this](){
|
||||
uint16_t dpi = _config.nextDPI();
|
||||
std::lock_guard<std::mutex> lock(_dpi_lock);
|
||||
++_current_dpi;
|
||||
if(_current_dpi == _config.dpis.end())
|
||||
_current_dpi = _config.dpis.begin();
|
||||
try {
|
||||
_dpi->setDPI(dpi, _config.sensor());
|
||||
_dpi->setDPI(*_current_dpi, _config.sensor.value_or(0));
|
||||
} catch (backend::hidpp20::Error& e) {
|
||||
if(e.code() == backend::hidpp20::Error::InvalidArgument)
|
||||
logPrintf(WARN, "%s:%d: Could not set DPI to %d for "
|
||||
"sensor %d", _device->hidpp20().devicePath().c_str(),
|
||||
_device->hidpp20().deviceIndex(), dpi,
|
||||
_config.sensor());
|
||||
_device->hidpp20().deviceIndex(), *_current_dpi,
|
||||
_config.sensor.value_or(0));
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
@ -67,68 +70,3 @@ uint8_t CycleDPI::reprogFlags() const
|
||||
{
|
||||
return backend::hidpp20::ReprogControls::TemporaryDiverted;
|
||||
}
|
||||
|
||||
CycleDPI::Config::Config(Device *device, libconfig::Setting &config) :
|
||||
Action::Config(device), _current_index (0), _sensor (0)
|
||||
{
|
||||
if(!config.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: action must be an object, skipping.",
|
||||
config.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto& sensor = config.lookup("sensor");
|
||||
if(sensor.getType() != Setting::TypeInt)
|
||||
logPrintf(WARN, "Line %d: sensor must be an integer",
|
||||
sensor.getSourceLine());
|
||||
_sensor = (int)sensor;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
try {
|
||||
auto& dpis = config.lookup("dpis");
|
||||
if(!dpis.isList() && !dpis.isArray()) {
|
||||
logPrintf(WARN, "Line %d: dpis must be a list or array, skipping.",
|
||||
dpis.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
int dpi_count = dpis.getLength();
|
||||
for(int i = 0; i < dpi_count; i++) {
|
||||
if(dpis[i].getType() != Setting::TypeInt) {
|
||||
logPrintf(WARN, "Line %d: dpis must be integers, skipping.",
|
||||
dpis[i].getSourceLine());
|
||||
if(dpis.isList())
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
_dpis.push_back((int)(dpis[i]));
|
||||
}
|
||||
|
||||
} catch (libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: dpis is a required field, skipping.",
|
||||
config.getSourceLine());
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t CycleDPI::Config::nextDPI()
|
||||
{
|
||||
uint16_t dpi = _dpis[_current_index++];
|
||||
if(_current_index >= _dpis.size())
|
||||
_current_index = 0;
|
||||
return dpi;
|
||||
}
|
||||
|
||||
bool CycleDPI::Config::empty() const
|
||||
{
|
||||
return _dpis.empty();
|
||||
}
|
||||
|
||||
uint8_t CycleDPI::Config::sensor() const
|
||||
{
|
||||
return _sensor;
|
||||
}
|
@ -27,29 +27,18 @@ namespace actions {
|
||||
class CycleDPI : public Action
|
||||
{
|
||||
public:
|
||||
explicit CycleDPI(Device* device, libconfig::Setting& setting);
|
||||
explicit CycleDPI(Device* device, config::CycleDPI& setting);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
|
||||
class Config : public Action::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& setting);
|
||||
uint16_t nextDPI();
|
||||
bool empty() const;
|
||||
uint8_t sensor() const;
|
||||
private:
|
||||
std::size_t _current_index;
|
||||
std::vector<uint16_t> _dpis;
|
||||
uint8_t _sensor;
|
||||
};
|
||||
|
||||
protected:
|
||||
Config _config;
|
||||
std::mutex _dpi_lock;
|
||||
config::CycleDPI& _config;
|
||||
std::shared_ptr<features::DPI> _dpi;
|
||||
std::list<int>::const_iterator _current_dpi;
|
||||
};
|
||||
}}
|
||||
|
||||
|
@ -54,16 +54,28 @@ GestureAction::Direction GestureAction::toDirection(int16_t x, int16_t y)
|
||||
return x <= -y ? Up : Right;
|
||||
}
|
||||
|
||||
GestureAction::GestureAction(Device* dev, libconfig::Setting& config) :
|
||||
Action (dev), _config (dev, config)
|
||||
GestureAction::GestureAction(Device* dev, config::GestureAction& config) :
|
||||
Action (dev), _config (config)
|
||||
{
|
||||
if(_config.gestures.has_value()) {
|
||||
auto& gestures = _config.gestures.value();
|
||||
for(auto& x : gestures) {
|
||||
try {
|
||||
auto direction = toDirection(x.first);
|
||||
_gestures.emplace(direction,
|
||||
Gesture::makeGesture(dev, x.second));
|
||||
} catch(std::invalid_argument& e) {
|
||||
logPrintf(WARN, "%s is not a direction", x.first.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GestureAction::press()
|
||||
{
|
||||
_pressed = true;
|
||||
_x = 0, _y = 0;
|
||||
for(auto& gesture : _config.gestures())
|
||||
for(auto& gesture : _gestures)
|
||||
gesture.second->press();
|
||||
}
|
||||
|
||||
@ -73,13 +85,13 @@ void GestureAction::release()
|
||||
bool threshold_met = false;
|
||||
|
||||
auto d = toDirection(_x, _y);
|
||||
auto primary_gesture = _config.gestures().find(d);
|
||||
if(primary_gesture != _config.gestures().end()) {
|
||||
auto primary_gesture = _gestures.find(d);
|
||||
if(primary_gesture != _gestures.end()) {
|
||||
threshold_met = primary_gesture->second->metThreshold();
|
||||
primary_gesture->second->release(true);
|
||||
}
|
||||
|
||||
for(auto& gesture : _config.gestures()) {
|
||||
for(auto& gesture : _gestures) {
|
||||
if(gesture.first == d)
|
||||
continue;
|
||||
if(!threshold_met) {
|
||||
@ -96,10 +108,13 @@ void GestureAction::release()
|
||||
}
|
||||
|
||||
if(!threshold_met) {
|
||||
if(_config.noneAction()) {
|
||||
_config.noneAction()->press();
|
||||
_config.noneAction()->release();
|
||||
}
|
||||
try {
|
||||
auto none = _gestures.at(None);
|
||||
if(none) {
|
||||
none->press();
|
||||
none->release();
|
||||
}
|
||||
} catch(std::out_of_range& e) { }
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,60 +124,60 @@ void GestureAction::move(int16_t x, int16_t y)
|
||||
|
||||
if(abs(x) > 0) {
|
||||
if(_x < 0 && new_x >= 0) { // Left -> Origin/Right
|
||||
auto left = _config.gestures().find(Left);
|
||||
if(left != _config.gestures().end())
|
||||
auto left = _gestures.find(Left);
|
||||
if(left != _gestures.end() && left->second)
|
||||
left->second->move(_x);
|
||||
if(new_x) { // Ignore to origin
|
||||
auto right = _config.gestures().find(Right);
|
||||
if(right != _config.gestures().end())
|
||||
auto right = _gestures.find(Right);
|
||||
if(right != _gestures.end() && right->second)
|
||||
right->second->move(new_x);
|
||||
}
|
||||
} else if(_x > 0 && new_x <= 0) { // Right -> Origin/Left
|
||||
auto right = _config.gestures().find(Right);
|
||||
if(right != _config.gestures().end())
|
||||
auto right = _gestures.find(Right);
|
||||
if(right != _gestures.end() && right->second)
|
||||
right->second->move(-_x);
|
||||
if(new_x) { // Ignore to origin
|
||||
auto left = _config.gestures().find(Left);
|
||||
if(left != _config.gestures().end())
|
||||
auto left = _gestures.find(Left);
|
||||
if(left != _gestures.end() && left->second)
|
||||
left->second->move(-new_x);
|
||||
}
|
||||
} else if(new_x < 0) { // Origin/Left to Left
|
||||
auto left = _config.gestures().find(Left);
|
||||
if(left != _config.gestures().end())
|
||||
auto left = _gestures.find(Left);
|
||||
if(left != _gestures.end() && left->second)
|
||||
left->second->move(-x);
|
||||
} else if(new_x > 0) { // Origin/Right to Right
|
||||
auto right = _config.gestures().find(Right);
|
||||
if(right != _config.gestures().end())
|
||||
auto right = _gestures.find(Right);
|
||||
if(right != _gestures.end() && right->second)
|
||||
right->second->move(x);
|
||||
}
|
||||
}
|
||||
|
||||
if(abs(y) > 0) {
|
||||
if(_y > 0 && new_y <= 0) { // Up -> Origin/Down
|
||||
auto up = _config.gestures().find(Up);
|
||||
if(up != _config.gestures().end())
|
||||
auto up = _gestures.find(Up);
|
||||
if(up != _gestures.end() && up->second)
|
||||
up->second->move(_y);
|
||||
if(new_y) { // Ignore to origin
|
||||
auto down = _config.gestures().find(Down);
|
||||
if(down != _config.gestures().end())
|
||||
auto down = _gestures.find(Down);
|
||||
if(down != _gestures.end() && down->second)
|
||||
down->second->move(new_y);
|
||||
}
|
||||
} else if(_y < 0 && new_y >= 0) { // Down -> Origin/Up
|
||||
auto down = _config.gestures().find(Down);
|
||||
if(down != _config.gestures().end())
|
||||
auto down = _gestures.find(Down);
|
||||
if(down != _gestures.end() && down->second)
|
||||
down->second->move(-_y);
|
||||
if(new_y) { // Ignore to origin
|
||||
auto up = _config.gestures().find(Up);
|
||||
if(up != _config.gestures().end())
|
||||
auto up = _gestures.find(Up);
|
||||
if(up != _gestures.end() && up->second)
|
||||
up->second->move(-new_y);
|
||||
}
|
||||
} else if(new_y < 0) { // Origin/Up to Up
|
||||
auto up = _config.gestures().find(Up);
|
||||
if(up != _config.gestures().end())
|
||||
auto up = _gestures.find(Up);
|
||||
if(up != _gestures.end() && up->second)
|
||||
up->second->move(-y);
|
||||
} else if(new_y > 0) {// Origin/Down to Down
|
||||
auto down = _config.gestures().find(Down);
|
||||
if(down != _config.gestures().end())
|
||||
auto down = _gestures.find(Down);
|
||||
if(down != _gestures.end() && down->second)
|
||||
down->second->move(y);
|
||||
}
|
||||
}
|
||||
@ -175,117 +190,3 @@ uint8_t GestureAction::reprogFlags() const
|
||||
return (hidpp20::ReprogControls::TemporaryDiverted |
|
||||
hidpp20::ReprogControls::RawXYDiverted);
|
||||
}
|
||||
|
||||
GestureAction::Config::Config(Device* device, libconfig::Setting &root) :
|
||||
Action::Config(device)
|
||||
{
|
||||
try {
|
||||
auto& gestures = root.lookup("gestures");
|
||||
|
||||
if(!gestures.isList()) {
|
||||
logPrintf(WARN, "Line %d: gestures must be a list, ignoring.",
|
||||
gestures.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
int gesture_count = gestures.getLength();
|
||||
|
||||
for(int i = 0; i < gesture_count; i++) {
|
||||
if(!gestures[i].isGroup()) {
|
||||
logPrintf(WARN, "Line %d: gesture must be a group, skipping.",
|
||||
gestures[i].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
Direction d;
|
||||
try {
|
||||
auto& direction = gestures[i].lookup("direction");
|
||||
if(direction.getType() != libconfig::Setting::TypeString) {
|
||||
logPrintf(WARN, "Line %d: direction must be a string, "
|
||||
"skipping.", direction.getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
d = toDirection(direction);
|
||||
} catch(std::invalid_argument& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid direction %s",
|
||||
direction.getSourceLine(), (const char*)direction);
|
||||
continue;
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: direction is a required field, "
|
||||
"skipping.", gestures[i].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
if(_gestures.find(d) != _gestures.end() || (d == None && _none_action)) {
|
||||
logPrintf(WARN, "Line %d: Gesture is already defined for "
|
||||
"this direction, duplicate ignored.",
|
||||
gestures[i].getSourceLine());
|
||||
continue;
|
||||
}
|
||||
|
||||
if(d == None) {
|
||||
try {
|
||||
auto& mode = gestures[i].lookup("mode");
|
||||
if(mode.getType() == libconfig::Setting::TypeString) {
|
||||
std::string mode_str = mode;
|
||||
std::transform(mode_str.begin(), mode_str.end(),
|
||||
mode_str.begin(), ::tolower);
|
||||
if(mode_str == "nopress") // No action
|
||||
continue;
|
||||
else if(mode_str != "onrelease")
|
||||
logPrintf(WARN, "Line %d: Only NoPress and "
|
||||
"OnRelease are supported for the "
|
||||
"None direction, defaulting to "
|
||||
"OnRelease.", mode.getSourceLine());
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: mode must be a string, "
|
||||
"defaulting to OnRelease",
|
||||
mode.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// Default to OnRelease
|
||||
}
|
||||
|
||||
try {
|
||||
_none_action = Action::makeAction(_device,
|
||||
gestures[i].lookup("action"));
|
||||
} catch (InvalidAction& e) {
|
||||
logPrintf(WARN, "Line %d: %s is not a valid action, "
|
||||
"skipping.", gestures[i].lookup("action")
|
||||
.getSourceLine(), e.what());
|
||||
} catch (libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: action is a required field, "
|
||||
"skipping.", gestures[i].getSourceLine(),
|
||||
e.what());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
_gestures.emplace(d, Gesture::makeGesture(_device,
|
||||
gestures[i]));
|
||||
} catch(InvalidGesture& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid gesture: %s",
|
||||
gestures[i].getSourceLine(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: gestures is a required field, ignoring.",
|
||||
root.getSourceLine());
|
||||
}
|
||||
}
|
||||
|
||||
std::map<GestureAction::Direction, std::shared_ptr<Gesture>>&
|
||||
GestureAction::Config::gestures()
|
||||
{
|
||||
return _gestures;
|
||||
}
|
||||
|
||||
std::shared_ptr<Action> GestureAction::Config::noneAction()
|
||||
{
|
||||
return _none_action;
|
||||
}
|
@ -39,7 +39,7 @@ namespace actions {
|
||||
static Direction toDirection(std::string direction);
|
||||
static Direction toDirection(int16_t x, int16_t y);
|
||||
|
||||
GestureAction(Device* dev, libconfig::Setting& config);
|
||||
GestureAction(Device* dev, config::GestureAction& config);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
@ -47,20 +47,10 @@ namespace actions {
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
|
||||
class Config : public Action::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& root);
|
||||
std::map<Direction, std::shared_ptr<Gesture>>& gestures();
|
||||
std::shared_ptr<Action> noneAction();
|
||||
protected:
|
||||
std::map<Direction, std::shared_ptr<Gesture>> _gestures;
|
||||
std::shared_ptr<Action> _none_action;
|
||||
};
|
||||
|
||||
protected:
|
||||
int16_t _x, _y;
|
||||
Config _config;
|
||||
std::map<Direction, std::shared_ptr<Gesture>> _gestures;
|
||||
config::GestureAction& _config;
|
||||
};
|
||||
}}
|
||||
|
||||
|
@ -24,22 +24,57 @@
|
||||
using namespace logid::actions;
|
||||
using namespace logid::backend;
|
||||
|
||||
KeypressAction::KeypressAction(Device *device, libconfig::Setting& config) :
|
||||
Action(device), _config (device, config)
|
||||
KeypressAction::KeypressAction(Device *device, config::KeypressAction& config) :
|
||||
Action(device), _config (config)
|
||||
{
|
||||
if(std::holds_alternative<std::string>(_config.keys)) {
|
||||
const auto& key = std::get<std::string>(_config.keys);
|
||||
try {
|
||||
auto code = _device->virtualInput()->toKeyCode(key);
|
||||
_device->virtualInput()->registerKey(code);
|
||||
_keys.emplace_back(code);
|
||||
} catch(InputDevice::InvalidEventCode& e) {
|
||||
logPrintf(WARN, "Invalid keycode %s, skipping.", key.c_str());
|
||||
}
|
||||
} else if(std::holds_alternative<uint>(_config.keys)) {
|
||||
const auto& key = std::get<uint>(_config.keys);
|
||||
_device->virtualInput()->registerKey(key);
|
||||
_keys.emplace_back(key);
|
||||
} else if(std::holds_alternative<std::list<std::variant<uint, std::string
|
||||
>>>(_config.keys)) {
|
||||
const auto& keys = std::get<std::list<std::variant<uint, std::string>>>(
|
||||
_config.keys);
|
||||
for(const auto& key : keys) {
|
||||
if(std::holds_alternative<std::string>(_config.keys)) {
|
||||
const auto& key_str = std::get<std::string>(key);
|
||||
try {
|
||||
auto code = _device->virtualInput()->toKeyCode(key_str);
|
||||
_device->virtualInput()->registerKey(code);
|
||||
_keys.emplace_back(code);
|
||||
} catch(InputDevice::InvalidEventCode& e) {
|
||||
logPrintf(WARN, "Invalid keycode %s, skipping.",
|
||||
key_str.c_str());
|
||||
}
|
||||
} else if(std::holds_alternative<uint>(_config.keys)) {
|
||||
auto& code = std::get<uint>(_config.keys);
|
||||
_device->virtualInput()->registerKey(code);
|
||||
_keys.emplace_back(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeypressAction::press()
|
||||
{
|
||||
_pressed = true;
|
||||
for(auto& key : _config.keys())
|
||||
for(auto& key : _keys)
|
||||
_device->virtualInput()->pressKey(key);
|
||||
}
|
||||
|
||||
void KeypressAction::release()
|
||||
{
|
||||
_pressed = false;
|
||||
for(auto& key : _config.keys())
|
||||
for(auto& key : _keys)
|
||||
_device->virtualInput()->releaseKey(key);
|
||||
}
|
||||
|
||||
@ -47,48 +82,3 @@ uint8_t KeypressAction::reprogFlags() const
|
||||
{
|
||||
return hidpp20::ReprogControls::TemporaryDiverted;
|
||||
}
|
||||
|
||||
KeypressAction::Config::Config(Device* device, libconfig::Setting& config) :
|
||||
Action::Config(device)
|
||||
{
|
||||
if(!config.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: action must be an object, skipping.",
|
||||
config.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto &keys = config.lookup("keys");
|
||||
if(keys.isArray() || keys.isList()) {
|
||||
int key_count = keys.getLength();
|
||||
for(int i = 0; i < key_count; i++) {
|
||||
auto& key = keys[i];
|
||||
if(key.isNumber()) {
|
||||
_keys.push_back(key);
|
||||
_device->virtualInput()->registerKey(key);
|
||||
} else if(key.getType() == libconfig::Setting::TypeString) {
|
||||
try {
|
||||
_keys.push_back(
|
||||
_device->virtualInput()->toKeyCode(key));
|
||||
_device->virtualInput()->registerKey(
|
||||
_device->virtualInput()->toKeyCode(key));
|
||||
} catch(InputDevice::InvalidEventCode& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid keycode %s, skipping."
|
||||
, key.getSourceLine(), key.c_str());
|
||||
}
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: keycode must be string or int",
|
||||
key.getSourceLine(), key.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: keys is a required field, skipping.",
|
||||
config.getSourceLine());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint>& KeypressAction::Config::keys()
|
||||
{
|
||||
return _keys;
|
||||
}
|
@ -27,23 +27,15 @@ namespace actions {
|
||||
class KeypressAction : public Action
|
||||
{
|
||||
public:
|
||||
KeypressAction(Device* dev, libconfig::Setting& config);
|
||||
KeypressAction(Device* dev, config::KeypressAction& config);
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
||||
virtual uint8_t reprogFlags() const;
|
||||
|
||||
class Config : public Action::Config
|
||||
{
|
||||
public:
|
||||
explicit Config(Device* device, libconfig::Setting& root);
|
||||
std::vector<uint>& keys();
|
||||
protected:
|
||||
std::vector<uint> _keys;
|
||||
};
|
||||
protected:
|
||||
Config _config;
|
||||
config::KeypressAction& _config;
|
||||
std::list<uint> _keys;
|
||||
};
|
||||
}}
|
||||
|
||||
|
@ -27,6 +27,8 @@ namespace actions
|
||||
{
|
||||
public:
|
||||
explicit NullAction(Device* device);
|
||||
NullAction(Device* device, [[maybe_unused]] config::NoAction& config) :
|
||||
NullAction(device) { }
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
@ -28,6 +28,9 @@ namespace actions
|
||||
{
|
||||
public:
|
||||
explicit ToggleHiresScroll(Device* dev);
|
||||
ToggleHiresScroll(Device* device,
|
||||
[[maybe_unused]] config::ToggleHiresScroll& action) :
|
||||
ToggleHiresScroll(device) { }
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
@ -28,6 +28,9 @@ namespace actions {
|
||||
{
|
||||
public:
|
||||
explicit ToggleSmartShift(Device* dev);
|
||||
ToggleSmartShift(Device* device,
|
||||
[[maybe_unused]] config::ToggleSmartShift& action) :
|
||||
ToggleSmartShift(device) { }
|
||||
|
||||
virtual void press();
|
||||
virtual void release();
|
||||
|
@ -23,14 +23,28 @@
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
AxisGesture::AxisGesture(Device *device, libconfig::Setting &root) :
|
||||
Gesture (device), _config (device, root)
|
||||
AxisGesture::AxisGesture(Device *device, config::AxisGesture& config) :
|
||||
Gesture (device), _multiplier (1), _config (config)
|
||||
{
|
||||
if(std::holds_alternative<uint>(_config.axis)) {
|
||||
_input_axis = std::get<uint>(_config.axis);
|
||||
} else {
|
||||
const auto& axis = std::get<std::string>(_config.axis);
|
||||
try {
|
||||
_axis = _device->virtualInput()->toAxisCode(axis);
|
||||
_device->virtualInput()->registerAxis(_axis);
|
||||
} catch(InputDevice::InvalidEventCode& e) {
|
||||
logPrintf(WARN, "Invalid axis %s.");
|
||||
throw InvalidGesture();
|
||||
}
|
||||
}
|
||||
_device->virtualInput()->registerAxis(_input_axis);
|
||||
}
|
||||
|
||||
void AxisGesture::press(bool init_threshold)
|
||||
{
|
||||
_axis = init_threshold ? _config.threshold() : 0;
|
||||
_axis = init_threshold ?
|
||||
_config.threshold.value_or(defaults::gesture_threshold) : 0;
|
||||
_axis_remainder = 0;
|
||||
_hires_remainder = 0;
|
||||
}
|
||||
@ -43,19 +57,23 @@ void AxisGesture::release(bool primary)
|
||||
|
||||
void AxisGesture::move(int16_t axis)
|
||||
{
|
||||
const auto threshold = _config.threshold.value_or(
|
||||
defaults::gesture_threshold);
|
||||
int16_t new_axis = _axis+axis;
|
||||
int low_res_axis = InputDevice::getLowResAxis(_config.axis());
|
||||
int low_res_axis = InputDevice::getLowResAxis(axis);
|
||||
int hires_remainder = _hires_remainder;
|
||||
|
||||
if(new_axis > _config.threshold()) {
|
||||
if(new_axis > threshold) {
|
||||
double move = axis;
|
||||
if(_axis < _config.threshold())
|
||||
move = new_axis - _config.threshold();
|
||||
bool negative_multiplier = _config.multiplier() < 0;
|
||||
if(_axis < threshold)
|
||||
move = new_axis - threshold;
|
||||
bool negative_multiplier = _config.axis_multiplier.value_or(1) < 0;
|
||||
if(negative_multiplier)
|
||||
move *= -_config.multiplier();
|
||||
move *= -_config.axis_multiplier.value_or(1);
|
||||
else
|
||||
move *= _config.multiplier();
|
||||
move *= _config.axis_multiplier.value_or(1);
|
||||
// Handle hi-res multiplier
|
||||
move *= _multiplier;
|
||||
|
||||
double move_floor = floor(move);
|
||||
_axis_remainder = move - move_floor;
|
||||
@ -70,7 +88,7 @@ void AxisGesture::move(int16_t axis)
|
||||
|
||||
if(low_res_axis != -1) {
|
||||
int lowres_movement = 0, hires_movement = move_floor;
|
||||
_device->virtualInput()->moveAxis(_config.axis(), hires_movement);
|
||||
_device->virtualInput()->moveAxis(_input_axis, hires_movement);
|
||||
hires_remainder += hires_movement;
|
||||
if(abs(hires_remainder) >= 60) {
|
||||
lowres_movement = hires_remainder/120;
|
||||
@ -82,7 +100,7 @@ void AxisGesture::move(int16_t axis)
|
||||
|
||||
_hires_remainder = hires_remainder;
|
||||
} else {
|
||||
_device->virtualInput()->moveAxis(_config.axis(), move_floor);
|
||||
_device->virtualInput()->moveAxis(_input_axis, move_floor);
|
||||
}
|
||||
}
|
||||
_axis = new_axis;
|
||||
@ -90,72 +108,7 @@ void AxisGesture::move(int16_t axis)
|
||||
|
||||
bool AxisGesture::metThreshold() const
|
||||
{
|
||||
return _axis >= _config.threshold();
|
||||
}
|
||||
|
||||
void AxisGesture::setHiresMultiplier(double multiplier)
|
||||
{
|
||||
_config.setHiresMultiplier(multiplier);
|
||||
}
|
||||
|
||||
AxisGesture::Config::Config(Device *device, libconfig::Setting &setting) :
|
||||
Gesture::Config(device, setting, false)
|
||||
{
|
||||
try {
|
||||
auto& axis = setting.lookup("axis");
|
||||
if(axis.isNumber()) {
|
||||
_axis = axis;
|
||||
_device->virtualInput()->registerAxis(_axis);
|
||||
} else if(axis.getType() == libconfig::Setting::TypeString) {
|
||||
try {
|
||||
_axis = _device->virtualInput()->toAxisCode(axis);
|
||||
_device->virtualInput()->registerAxis(_axis);
|
||||
} catch(InputDevice::InvalidEventCode& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid axis %s, skipping."
|
||||
, axis.getSourceLine(), axis.c_str());
|
||||
}
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: axis must be string or int, skipping.",
|
||||
axis.getSourceLine(), axis.c_str());
|
||||
throw InvalidGesture();
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: axis is a required field, skippimg.",
|
||||
setting.getSourceLine());
|
||||
throw InvalidGesture();
|
||||
}
|
||||
|
||||
try {
|
||||
auto& multiplier = setting.lookup("axis_multiplier");
|
||||
if(multiplier.isNumber()) {
|
||||
if(multiplier.getType() == libconfig::Setting::TypeFloat)
|
||||
_multiplier = multiplier;
|
||||
else
|
||||
_multiplier = (int)multiplier;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: axis_multiplier must be a number, "
|
||||
"setting to default (1).",
|
||||
multiplier.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
int low_res_axis = InputDevice::getLowResAxis(_axis);
|
||||
if(low_res_axis != -1) {
|
||||
_multiplier *= 120;
|
||||
_device->virtualInput()->registerAxis(low_res_axis);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int AxisGesture::Config::axis() const
|
||||
{
|
||||
return _axis;
|
||||
}
|
||||
|
||||
double AxisGesture::Config::multiplier() const
|
||||
{
|
||||
return _multiplier;
|
||||
return _axis >= _config.threshold.value_or(defaults::gesture_threshold);
|
||||
}
|
||||
|
||||
bool AxisGesture::wheelCompatibility() const
|
||||
@ -163,15 +116,9 @@ bool AxisGesture::wheelCompatibility() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void AxisGesture::Config::setHiresMultiplier(double multiplier)
|
||||
void AxisGesture::setHiresMultiplier(double multiplier)
|
||||
{
|
||||
if(_hires_multiplier == multiplier || multiplier == 0)
|
||||
return;
|
||||
|
||||
if(InputDevice::getLowResAxis(_axis) != -1) {
|
||||
_multiplier *= _hires_multiplier;
|
||||
_multiplier /= multiplier;
|
||||
_multiplier = multiplier;
|
||||
}
|
||||
|
||||
_hires_multiplier = multiplier;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ namespace logid {
|
||||
class AxisGesture : public Gesture
|
||||
{
|
||||
public:
|
||||
AxisGesture(Device* device, libconfig::Setting& root);
|
||||
AxisGesture(Device* device, config::AxisGesture& config);
|
||||
|
||||
virtual void press(bool init_threshold=false);
|
||||
virtual void release(bool primary=false);
|
||||
@ -37,24 +37,13 @@ namespace logid {
|
||||
|
||||
void setHiresMultiplier(double multiplier);
|
||||
|
||||
class Config : public Gesture::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& setting);
|
||||
unsigned int axis() const;
|
||||
double multiplier() const;
|
||||
void setHiresMultiplier(double multiplier);
|
||||
private:
|
||||
unsigned int _axis;
|
||||
double _multiplier = 1;
|
||||
double _hires_multiplier = 1;
|
||||
};
|
||||
|
||||
protected:
|
||||
int16_t _axis;
|
||||
double _axis_remainder;
|
||||
int _hires_remainder;
|
||||
Config _config;
|
||||
uint _input_axis;
|
||||
double _multiplier;
|
||||
config::AxisGesture& _config;
|
||||
};
|
||||
}}
|
||||
|
||||
|
@ -18,104 +18,42 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include "Gesture.h"
|
||||
#include "../../util/log.h"
|
||||
#include "ReleaseGesture.h"
|
||||
#include "ThresholdGesture.h"
|
||||
#include "../../backend/hidpp20/features/ReprogControls.h"
|
||||
#include "IntervalGesture.h"
|
||||
#include "AxisGesture.h"
|
||||
#include "NullGesture.h"
|
||||
|
||||
using namespace logid;
|
||||
using namespace logid::actions;
|
||||
|
||||
Gesture::Gesture(Device *device) : _device (device)
|
||||
{
|
||||
}
|
||||
|
||||
Gesture::Config::Config(Device* device, libconfig::Setting& root,
|
||||
bool action_required) : _device (device)
|
||||
{
|
||||
if(action_required) {
|
||||
try {
|
||||
_action = Action::makeAction(_device,
|
||||
root.lookup("action"));
|
||||
} catch (libconfig::SettingNotFoundException &e) {
|
||||
throw InvalidGesture("action is missing");
|
||||
}
|
||||
template <typename T>
|
||||
struct gesture_type {
|
||||
typedef typename T::gesture type;
|
||||
};
|
||||
|
||||
if(_action->reprogFlags() & backend::hidpp20::ReprogControls::RawXYDiverted)
|
||||
throw InvalidGesture("gesture cannot require RawXY");
|
||||
}
|
||||
template <typename T>
|
||||
struct gesture_type<const T> : gesture_type<T> { };
|
||||
|
||||
_threshold = LOGID_GESTURE_DEFAULT_THRESHOLD;
|
||||
try {
|
||||
auto& threshold = root.lookup("threshold");
|
||||
if(threshold.getType() == libconfig::Setting::TypeInt) {
|
||||
_threshold = (int)threshold;
|
||||
if(_threshold <= 0) {
|
||||
_threshold = LOGID_GESTURE_DEFAULT_THRESHOLD;
|
||||
logPrintf(WARN, "Line %d: threshold must be positive, setting "
|
||||
"to default (%d)", threshold.getSourceLine(),
|
||||
_threshold);
|
||||
}
|
||||
} else
|
||||
logPrintf(WARN, "Line %d: threshold must be an integer, setting "
|
||||
"to default (%d).", threshold.getSourceLine());
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// Ignore
|
||||
}
|
||||
template <typename T>
|
||||
struct gesture_type<T&> : gesture_type<T> { };
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<Gesture> _makeGesture(Device* device,
|
||||
T gesture) {
|
||||
return std::make_shared<typename gesture_type<T>::type>(device, gesture);
|
||||
}
|
||||
|
||||
std::shared_ptr<Gesture> Gesture::makeGesture(Device *device,
|
||||
libconfig::Setting &setting)
|
||||
config::Gesture& gesture)
|
||||
{
|
||||
if(!setting.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: Gesture is not a group, ignoring.",
|
||||
setting.getSourceLine());
|
||||
throw InvalidGesture();
|
||||
}
|
||||
|
||||
try {
|
||||
auto& gesture_mode = setting.lookup("mode");
|
||||
|
||||
if(gesture_mode.getType() != libconfig::Setting::TypeString) {
|
||||
logPrintf(WARN, "Line %d: Gesture mode must be a string,"
|
||||
"defaulting to OnRelease.",
|
||||
gesture_mode.getSourceLine());
|
||||
return std::make_shared<ReleaseGesture>(device, setting);
|
||||
}
|
||||
|
||||
std::string type = gesture_mode;
|
||||
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
||||
|
||||
if(type == "onrelease")
|
||||
return std::make_shared<ReleaseGesture>(device, setting);
|
||||
else if(type == "onthreshold")
|
||||
return std::make_shared<ThresholdGesture>(device, setting);
|
||||
else if(type == "oninterval" || type == "onfewpixels")
|
||||
return std::make_shared<IntervalGesture>(device, setting);
|
||||
else if(type == "axis")
|
||||
return std::make_shared<AxisGesture>(device, setting);
|
||||
else if(type == "nopress")
|
||||
return std::make_shared<NullGesture>(device, setting);
|
||||
else {
|
||||
logPrintf(WARN, "Line %d: Unknown gesture mode %s, defaulting to "
|
||||
"OnRelease.", gesture_mode.getSourceLine(),
|
||||
(const char*)gesture_mode);
|
||||
return std::make_shared<ReleaseGesture>(device, setting);
|
||||
}
|
||||
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
return std::make_shared<ReleaseGesture>(device, setting);
|
||||
}
|
||||
std::shared_ptr<Gesture> ret;
|
||||
std::visit([&device, &ret](auto&& x) {
|
||||
ret = _makeGesture(device, x);
|
||||
}, gesture);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int16_t Gesture::Config::threshold() const
|
||||
{
|
||||
return _threshold;
|
||||
}
|
||||
|
||||
std::shared_ptr<Action> Gesture::Config::action()
|
||||
{
|
||||
return _action;
|
||||
}
|
@ -51,21 +51,8 @@ namespace actions
|
||||
|
||||
virtual ~Gesture() = default;
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& root,
|
||||
bool action_required=true);
|
||||
virtual int16_t threshold() const;
|
||||
virtual std::shared_ptr<Action> action();
|
||||
protected:
|
||||
Device* _device;
|
||||
std::shared_ptr<Action> _action;
|
||||
int16_t _threshold;
|
||||
};
|
||||
|
||||
static std::shared_ptr<Gesture> makeGesture(Device* device,
|
||||
libconfig::Setting& setting);
|
||||
config::Gesture& gesture);
|
||||
|
||||
protected:
|
||||
explicit Gesture(Device* device);
|
||||
|
@ -17,17 +17,26 @@
|
||||
*/
|
||||
#include "IntervalGesture.h"
|
||||
#include "../../util/log.h"
|
||||
#include "../../Configuration.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
IntervalGesture::IntervalGesture(Device *device, libconfig::Setting &root) :
|
||||
Gesture (device), _config (device, root)
|
||||
IntervalGesture::IntervalGesture(Device *device, config::IntervalGesture& config) :
|
||||
Gesture (device), _config (config)
|
||||
{
|
||||
if(config.action) {
|
||||
try {
|
||||
_action = Action::makeAction(device, config.action.value());
|
||||
} catch(InvalidAction& e) {
|
||||
logPrintf(WARN, "Mapping gesture to invalid action");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntervalGesture::press(bool init_threshold)
|
||||
{
|
||||
_axis = init_threshold ? _config.threshold() : 0;
|
||||
_axis = init_threshold ?
|
||||
_config.threshold.value_or(defaults::gesture_threshold) : 0;
|
||||
_interval_pass_count = 0;
|
||||
}
|
||||
|
||||
@ -39,15 +48,19 @@ void IntervalGesture::release(bool primary)
|
||||
|
||||
void IntervalGesture::move(int16_t axis)
|
||||
{
|
||||
const auto threshold =
|
||||
_config.threshold.value_or(defaults::gesture_threshold);
|
||||
_axis += axis;
|
||||
if(_axis < _config.threshold())
|
||||
if(_axis < threshold)
|
||||
return;
|
||||
|
||||
int16_t new_interval_count = (_axis - _config.threshold())/
|
||||
_config.interval();
|
||||
int16_t new_interval_count = (_axis - threshold)/
|
||||
_config.interval;
|
||||
if(new_interval_count > _interval_pass_count) {
|
||||
_config.action()->press();
|
||||
_config.action()->release();
|
||||
if(_action) {
|
||||
_action->press();
|
||||
_action->release();
|
||||
}
|
||||
}
|
||||
_interval_pass_count = new_interval_count;
|
||||
}
|
||||
@ -59,38 +72,5 @@ bool IntervalGesture::wheelCompatibility() const
|
||||
|
||||
bool IntervalGesture::metThreshold() const
|
||||
{
|
||||
return _axis >= _config.threshold();
|
||||
return _axis >= _config.threshold.value_or(defaults::gesture_threshold);;
|
||||
}
|
||||
|
||||
IntervalGesture::Config::Config(Device *device, libconfig::Setting &setting) :
|
||||
Gesture::Config(device, setting)
|
||||
{
|
||||
try {
|
||||
auto& interval = setting.lookup("interval");
|
||||
if(interval.getType() != libconfig::Setting::TypeInt) {
|
||||
logPrintf(WARN, "Line %d: interval must be an integer, skipping.",
|
||||
interval.getSourceLine());
|
||||
throw InvalidGesture();
|
||||
}
|
||||
_interval = (int)interval;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
try {
|
||||
// pixels is an alias for interval
|
||||
auto& interval = setting.lookup("pixels");
|
||||
if(interval.getType() != libconfig::Setting::TypeInt) {
|
||||
logPrintf(WARN, "Line %d: pixels must be an integer, skipping.",
|
||||
interval.getSourceLine());
|
||||
throw InvalidGesture();
|
||||
}
|
||||
_interval = (int)interval;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: interval is a required field, skipping.",
|
||||
setting.getSourceLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t IntervalGesture::Config::interval() const
|
||||
{
|
||||
return _interval;
|
||||
}
|
@ -26,7 +26,7 @@ namespace actions
|
||||
class IntervalGesture : public Gesture
|
||||
{
|
||||
public:
|
||||
IntervalGesture(Device* device, libconfig::Setting& root);
|
||||
IntervalGesture(Device* device, config::IntervalGesture& config);
|
||||
|
||||
virtual void press(bool init_threshold=false);
|
||||
virtual void release(bool primary=false);
|
||||
@ -35,19 +35,11 @@ namespace actions
|
||||
virtual bool wheelCompatibility() const;
|
||||
virtual bool metThreshold() const;
|
||||
|
||||
class Config : public Gesture::Config
|
||||
{
|
||||
public:
|
||||
Config(Device* device, libconfig::Setting& setting);
|
||||
int16_t interval() const;
|
||||
private:
|
||||
int16_t _interval;
|
||||
};
|
||||
|
||||
protected:
|
||||
int16_t _axis;
|
||||
int16_t _interval_pass_count;
|
||||
Config _config;
|
||||
std::shared_ptr<Action> _action;
|
||||
config::IntervalGesture& _config;
|
||||
};
|
||||
}}
|
||||
|
||||
|
@ -16,17 +16,20 @@
|
||||
*
|
||||
*/
|
||||
#include "NullGesture.h"
|
||||
#include "../../Configuration.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
NullGesture::NullGesture(Device *device, libconfig::Setting& setting) :
|
||||
Gesture (device), _config (device, setting, false)
|
||||
NullGesture::NullGesture(Device *device,
|
||||
config::NoGesture& config) :
|
||||
Gesture (device), _config (config)
|
||||
{
|
||||
}
|
||||
|
||||
void NullGesture::press(bool init_threshold)
|
||||
{
|
||||
_axis = init_threshold ? _config.threshold() : 0;
|
||||
_axis = init_threshold ? _config.threshold.value_or(
|
||||
defaults::gesture_threshold) : 0;
|
||||
}
|
||||
|
||||
void NullGesture::release(bool primary)
|
||||
@ -47,5 +50,5 @@ bool NullGesture::wheelCompatibility() const
|
||||
|
||||
bool NullGesture::metThreshold() const
|
||||
{
|
||||
return _axis > _config.threshold();
|
||||
return _axis > _config.threshold.value_or(defaults::gesture_threshold);
|
||||
}
|
@ -26,7 +26,8 @@ namespace actions
|
||||
class NullGesture : public Gesture
|
||||
{
|
||||
public:
|
||||
NullGesture(Device* device, libconfig::Setting& setting);
|
||||
NullGesture(Device* device,
|
||||
config::NoGesture& config);
|
||||
|
||||
virtual void press(bool init_threshold=false);
|
||||
virtual void release(bool primary=false);
|
||||
@ -36,7 +37,7 @@ namespace actions
|
||||
virtual bool metThreshold() const;
|
||||
protected:
|
||||
int16_t _axis;
|
||||
Gesture::Config _config;
|
||||
config::NoGesture& _config;
|
||||
};
|
||||
}}
|
||||
|
||||
|
@ -16,24 +16,30 @@
|
||||
*
|
||||
*/
|
||||
#include "ReleaseGesture.h"
|
||||
#include "../../Configuration.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
ReleaseGesture::ReleaseGesture(Device *device, libconfig::Setting &root) :
|
||||
Gesture (device), _config (device, root)
|
||||
ReleaseGesture::ReleaseGesture(Device *device, config::ReleaseGesture& config) :
|
||||
Gesture (device), _config (config)
|
||||
{
|
||||
if(_config.action.has_value())
|
||||
_action = Action::makeAction(device, _config.action.value());
|
||||
}
|
||||
|
||||
void ReleaseGesture::press(bool init_threshold)
|
||||
{
|
||||
_axis = init_threshold ? _config.threshold() : 0;
|
||||
_axis = init_threshold ? _config.threshold.value_or(
|
||||
defaults::gesture_threshold) : 0;
|
||||
}
|
||||
|
||||
void ReleaseGesture::release(bool primary)
|
||||
{
|
||||
if(metThreshold() && primary) {
|
||||
_config.action()->press();
|
||||
_config.action()->release();
|
||||
if(_action) {
|
||||
_action->press();
|
||||
_action->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,5 +55,5 @@ bool ReleaseGesture::wheelCompatibility() const
|
||||
|
||||
bool ReleaseGesture::metThreshold() const
|
||||
{
|
||||
return _axis >= _config.threshold();
|
||||
return _axis >= _config.threshold.value_or(defaults::gesture_threshold);
|
||||
}
|
@ -26,7 +26,7 @@ namespace actions
|
||||
class ReleaseGesture : public Gesture
|
||||
{
|
||||
public:
|
||||
ReleaseGesture(Device* device, libconfig::Setting& root);
|
||||
ReleaseGesture(Device* device, config::ReleaseGesture& config);
|
||||
|
||||
virtual void press(bool init_threshold=false);
|
||||
virtual void release(bool primary=false);
|
||||
@ -37,7 +37,8 @@ namespace actions
|
||||
|
||||
protected:
|
||||
int16_t _axis;
|
||||
Gesture::Config _config;
|
||||
std::shared_ptr<Action> _action;
|
||||
config::ReleaseGesture& _config;
|
||||
};
|
||||
}}
|
||||
|
||||
|
@ -16,17 +16,27 @@
|
||||
*
|
||||
*/
|
||||
#include "ThresholdGesture.h"
|
||||
#include "../../Configuration.h"
|
||||
|
||||
using namespace logid::actions;
|
||||
|
||||
ThresholdGesture::ThresholdGesture(Device *device, libconfig::Setting &root) :
|
||||
Gesture (device), _config (device, root)
|
||||
ThresholdGesture::ThresholdGesture(Device *device,
|
||||
config::ThresholdGesture& config) :
|
||||
Gesture (device), _config (config)
|
||||
{
|
||||
if(config.action) {
|
||||
try {
|
||||
_action = Action::makeAction(device, config.action.value());
|
||||
} catch(InvalidAction& e) {
|
||||
logPrintf(WARN, "Mapping gesture to invalid action");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThresholdGesture::press(bool init_threshold)
|
||||
{
|
||||
_axis = init_threshold ? _config.threshold() : 0;
|
||||
_axis = init_threshold ?
|
||||
_config.threshold.value_or(defaults::gesture_threshold) : 0;
|
||||
this->_executed = false;
|
||||
}
|
||||
|
||||
@ -42,15 +52,17 @@ void ThresholdGesture::move(int16_t axis)
|
||||
_axis += axis;
|
||||
|
||||
if(!this->_executed && metThreshold()) {
|
||||
_config.action()->press();
|
||||
_config.action()->release();
|
||||
if(_action) {
|
||||
_action->press();
|
||||
_action->release();
|
||||
}
|
||||
this->_executed = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ThresholdGesture::metThreshold() const
|
||||
{
|
||||
return _axis >= _config.threshold();
|
||||
return _axis >= _config.threshold.value_or(defaults::gesture_threshold);
|
||||
}
|
||||
|
||||
bool ThresholdGesture::wheelCompatibility() const
|
||||
|
@ -26,7 +26,7 @@ namespace actions
|
||||
class ThresholdGesture : public Gesture
|
||||
{
|
||||
public:
|
||||
ThresholdGesture(Device* device, libconfig::Setting& root);
|
||||
ThresholdGesture(Device* device, config::ThresholdGesture& config);
|
||||
|
||||
virtual void press(bool init_threshold=false);
|
||||
virtual void release(bool primary=false);
|
||||
@ -38,7 +38,8 @@ namespace actions
|
||||
|
||||
protected:
|
||||
int16_t _axis;
|
||||
Gesture::Config _config;
|
||||
std::shared_ptr<actions::Action> _action;
|
||||
config::ThresholdGesture& _config;
|
||||
|
||||
private:
|
||||
bool _executed = false;
|
||||
|
@ -45,7 +45,7 @@ InvalidReceiver::Reason InvalidReceiver::code() const noexcept
|
||||
}
|
||||
|
||||
Receiver::Receiver(std::string path,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
double io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq) :
|
||||
_raw_device (std::make_shared<raw::RawDevice>(
|
||||
std::move(path), io_timeout, wq)),
|
||||
|
@ -53,7 +53,7 @@ namespace dj
|
||||
{
|
||||
public:
|
||||
Receiver(std::string path,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
double io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq);
|
||||
|
||||
enum DjEvents : uint8_t
|
||||
|
@ -27,7 +27,7 @@ using namespace logid::backend::dj;
|
||||
|
||||
ReceiverMonitor::ReceiverMonitor(
|
||||
std::string path,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
double io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq) :
|
||||
_workqueue (wq),
|
||||
_receiver (std::make_shared<Receiver>(std::move(path), io_timeout, wq))
|
||||
|
@ -33,7 +33,7 @@ namespace dj
|
||||
{
|
||||
public:
|
||||
ReceiverMonitor(std::string path,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
double io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq);
|
||||
virtual ~ReceiverMonitor();
|
||||
|
||||
|
@ -50,7 +50,7 @@ Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept
|
||||
}
|
||||
|
||||
Device::Device(const std::string& path, DeviceIndex index,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
double io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq):
|
||||
_raw_device (std::make_shared<raw::RawDevice>(path, io_timeout, wq)),
|
||||
_receiver (nullptr), _path (path), _index (index)
|
||||
|
@ -62,7 +62,7 @@ namespace hidpp
|
||||
};
|
||||
|
||||
Device(const std::string& path, DeviceIndex index,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
double io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq);
|
||||
Device(std::shared_ptr<raw::RawDevice> raw_device,
|
||||
DeviceIndex index);
|
||||
|
@ -25,7 +25,7 @@ using namespace logid::backend;
|
||||
using namespace logid::backend::hidpp10;
|
||||
|
||||
Device::Device(const std::string &path, hidpp::DeviceIndex index,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
double io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq) :
|
||||
hidpp::Device(path, index, io_timeout, wq)
|
||||
{
|
||||
|
@ -29,7 +29,7 @@ namespace hidpp10
|
||||
{
|
||||
public:
|
||||
Device(const std::string& path, hidpp::DeviceIndex index,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
double io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq);
|
||||
Device(std::shared_ptr<raw::RawDevice> raw_dev,
|
||||
hidpp::DeviceIndex index);
|
||||
|
@ -24,8 +24,7 @@
|
||||
using namespace logid::backend::hidpp20;
|
||||
|
||||
Device::Device(std::string path, hidpp::DeviceIndex index,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq) :
|
||||
double io_timeout, const std::shared_ptr<workqueue>& wq) :
|
||||
hidpp::Device(path, index, io_timeout, wq)
|
||||
{
|
||||
assert(std::get<0>(version()) >= 2);
|
||||
|
@ -29,8 +29,7 @@ namespace hidpp20 {
|
||||
{
|
||||
public:
|
||||
Device(std::string path, hidpp::DeviceIndex index,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq);
|
||||
double io_timeout, const std::shared_ptr<workqueue>& wq);
|
||||
Device(std::shared_ptr<raw::RawDevice> raw_device, hidpp::DeviceIndex index);
|
||||
Device(std::shared_ptr<dj::Receiver> receiver, hidpp::DeviceIndex
|
||||
index);
|
||||
|
@ -65,11 +65,12 @@ bool RawDevice::supportedReport(uint8_t id, uint8_t length)
|
||||
}
|
||||
|
||||
RawDevice::RawDevice(std::string path,
|
||||
const milliseconds& io_timeout,
|
||||
double io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq) :
|
||||
_path (std::move(path)),
|
||||
_continue_listen (false), _continue_respond (false),
|
||||
_io_timeout (io_timeout),
|
||||
_io_timeout (duration_cast<milliseconds>(
|
||||
duration<double, std::milli>(io_timeout))),
|
||||
_workqueue (wq)
|
||||
{
|
||||
int ret;
|
||||
|
@ -42,7 +42,7 @@ namespace raw
|
||||
static bool supportedReport(uint8_t id, uint8_t length);
|
||||
|
||||
explicit RawDevice(std::string path,
|
||||
const std::chrono::milliseconds& io_timeout,
|
||||
double io_timeout,
|
||||
const std::shared_ptr<workqueue>& wq);
|
||||
~RawDevice();
|
||||
std::string hidrawPath() const;
|
||||
|
206
src/logid/config/group.h
Normal file
206
src/logid/config/group.h
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2022 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_CONFIG_GROUP_H
|
||||
#define LOGID_CONFIG_GROUP_H
|
||||
|
||||
#include <libconfig.h++>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include "../util/log.h"
|
||||
|
||||
namespace logid::config {
|
||||
template <typename T>
|
||||
void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const T& t);
|
||||
|
||||
template <typename T>
|
||||
void set(libconfig::Setting& parent, const T& t);
|
||||
|
||||
template <typename T>
|
||||
T get(const libconfig::Setting& parent, const std::string& name);
|
||||
|
||||
template <typename T>
|
||||
void append(libconfig::Setting& list, const T& t);
|
||||
|
||||
namespace {
|
||||
template <typename T, typename... M>
|
||||
struct group_io { };
|
||||
|
||||
template <typename T>
|
||||
struct group_io<T> {
|
||||
static void get(
|
||||
[[maybe_unused]] const libconfig::Setting& s,
|
||||
[[maybe_unused]] T* t,
|
||||
[[maybe_unused]] const std::vector<std::string>& names,
|
||||
[[maybe_unused]] const std::size_t index) { }
|
||||
static void set(
|
||||
[[maybe_unused]] libconfig::Setting& s,
|
||||
[[maybe_unused]] const T* t,
|
||||
[[maybe_unused]] const std::vector<std::string>& names,
|
||||
[[maybe_unused]] const std::size_t index) { }
|
||||
};
|
||||
|
||||
template <typename T, typename A, typename... M>
|
||||
struct group_io<T, A, M...> {
|
||||
static void get(
|
||||
const libconfig::Setting& s, T* t,
|
||||
const std::vector<std::string>& names,
|
||||
const std::size_t index, A T::* arg, M T::*... rest) {
|
||||
auto& x = t->*(arg);
|
||||
A old {x};
|
||||
try {
|
||||
x = config::get<A>(s, names[index]);
|
||||
group_io<T, M...>::get(s, t, names, index+1, rest...);
|
||||
} catch(libconfig::SettingTypeException& e) {
|
||||
x = old;
|
||||
throw;
|
||||
} catch(libconfig::SettingException& e) {
|
||||
x = old;
|
||||
throw libconfig::SettingTypeException(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void set(
|
||||
libconfig::Setting& s, const T* t,
|
||||
const std::vector<std::string>& names,
|
||||
const std::size_t index, A T::* arg, M T::*... rest) {
|
||||
config::set(s, names[index], t->*(arg));
|
||||
group_io<T, M...>::set(s, t, names, index+1, rest...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Sign>
|
||||
struct signed_group;
|
||||
|
||||
struct group {
|
||||
private:
|
||||
const std::vector<std::string> _names;
|
||||
const std::function<void(const libconfig::Setting&, group*,
|
||||
const std::vector<std::string>&)> _getter;
|
||||
const std::function<void(libconfig::Setting&, const group*,
|
||||
const std::vector<std::string>&)> _setter;
|
||||
|
||||
template <typename Sign>
|
||||
friend struct signed_group;
|
||||
protected:
|
||||
template <typename T, typename... M>
|
||||
group(const std::array<std::string, sizeof...(M)>& names,
|
||||
M T::*... args) :
|
||||
_names (names.begin(), names.end()),
|
||||
_getter ([args...](const libconfig::Setting& s, group* g,
|
||||
const std::vector<std::string>& names) {
|
||||
T* t = dynamic_cast<T*>(g);
|
||||
group_io<T, M...>::get(s, t, names, 0, args...);
|
||||
}),
|
||||
_setter ([args...](libconfig::Setting& s, const group* g,
|
||||
const std::vector<std::string>& names) {
|
||||
const T* t = dynamic_cast<const T*>(g);;
|
||||
group_io<T, M...>::set(s, t, names, 0, args...);
|
||||
}) {
|
||||
static_assert(std::is_base_of<group, T>::value);
|
||||
}
|
||||
|
||||
group() : _getter ([](const libconfig::Setting&, group*,
|
||||
const std::vector<std::string>&) { }),
|
||||
_setter ([](libconfig::Setting&, const group*,
|
||||
const std::vector<std::string>&) { }) { }
|
||||
|
||||
public:
|
||||
group(const group& o) = default;
|
||||
group(group&& o) noexcept = default;
|
||||
group& operator=(const group&) {
|
||||
return *this;
|
||||
}
|
||||
group& operator=(group&&) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~group() = default;
|
||||
|
||||
virtual void _save(libconfig::Setting& setting) const {
|
||||
_setter(setting, this, _names);
|
||||
}
|
||||
|
||||
virtual void _load(const libconfig::Setting& setting) {
|
||||
_getter(setting, this, _names);
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
struct normalize_signature {
|
||||
static const T& make(const T& ret) { return ret; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct normalize_signature<std::string> {
|
||||
static std::string make(const std::string& data) {
|
||||
std::string ret = data;
|
||||
std::transform(ret.begin(), ret.end(),
|
||||
ret.begin(), ::tolower);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Sign>
|
||||
struct signed_group : public group {
|
||||
private:
|
||||
const std::string _sig_field;
|
||||
const Sign _signature;
|
||||
protected:
|
||||
signed_group(std::string sign_name, const Sign& sign_data) :
|
||||
group(), _sig_field (std::move(sign_name)),
|
||||
_signature (normalize_signature<Sign>::make(sign_data)) { }
|
||||
|
||||
template <typename T, typename... M>
|
||||
signed_group(
|
||||
std::string sign_name, const Sign& sign_data,
|
||||
const std::array<std::string, sizeof...(M)>& names,
|
||||
M T::*... args) : group(names, args...),
|
||||
_sig_field (std::move(sign_name)),
|
||||
_signature (normalize_signature<Sign>::make(sign_data)) { }
|
||||
public:
|
||||
signed_group(const signed_group& o) = default;
|
||||
signed_group(signed_group&& o) noexcept = default;
|
||||
signed_group& operator=(const signed_group&) {
|
||||
return *this;
|
||||
}
|
||||
signed_group& operator=(signed_group&&) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
void _save(libconfig::Setting& setting) const override {
|
||||
set(setting, _signature, _sig_field);
|
||||
_setter(setting, this, _names);
|
||||
}
|
||||
|
||||
void _load(const libconfig::Setting& setting) override {
|
||||
if(normalize_signature<Sign>::make(get<Sign>(setting, _sig_field))
|
||||
!= _signature)
|
||||
throw libconfig::SettingTypeException(setting);
|
||||
_getter(setting, this, _names);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LOGID_CONFIG_GROUP_H
|
46
src/logid/config/map.h
Normal file
46
src/logid/config/map.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2022 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_CONFIG_MAP_H
|
||||
#define LOGID_CONFIG_MAP_H
|
||||
|
||||
#include "group.h"
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
namespace logid::config {
|
||||
template<size_t N>
|
||||
struct string_literal {
|
||||
constexpr string_literal(const char (&str)[N]) {
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
|
||||
char value[N];
|
||||
};
|
||||
|
||||
// Warning: map must be a variant of groups or a group
|
||||
template <typename K, typename V, string_literal KeyName>
|
||||
class map : public std::map<K, V> {
|
||||
public:
|
||||
template <typename... Args>
|
||||
map(Args... args) :
|
||||
std::map<K, V>(std::forward<Args>(args)...) { }
|
||||
};
|
||||
}
|
||||
|
||||
#endif //LOGID_CONFIG_MAP_H
|
288
src/logid/config/schema.h
Normal file
288
src/logid/config/schema.h
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright 2022 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_CONFIG_SCHEMA_H
|
||||
#define LOGID_CONFIG_SCHEMA_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace logid::actions {
|
||||
class ChangeDPI;
|
||||
class ChangeHostAction;
|
||||
class CycleDPI;
|
||||
class GestureAction;
|
||||
class KeypressAction;
|
||||
class NullAction;
|
||||
class ToggleHiresScroll;
|
||||
class ToggleSmartShift;
|
||||
|
||||
class AxisGesture;
|
||||
class IntervalGesture;
|
||||
class NullGesture;
|
||||
class ReleaseGesture;
|
||||
class ThresholdGesture;
|
||||
}
|
||||
|
||||
namespace logid::config {
|
||||
|
||||
struct NoAction : public signed_group<std::string> {
|
||||
typedef actions::NullAction action;
|
||||
NoAction() : signed_group<std::string>("type", "None") { }
|
||||
};
|
||||
|
||||
struct KeypressAction : public signed_group<std::string> {
|
||||
typedef actions::KeypressAction action;
|
||||
std::variant<std::string, uint,
|
||||
std::list<std::variant<uint, std::string>>> keys;
|
||||
KeypressAction() : signed_group<std::string>(
|
||||
"type", "Keypress",
|
||||
{"keys"}, &KeypressAction::keys) { }
|
||||
};
|
||||
|
||||
struct ToggleSmartShift : public signed_group<std::string> {
|
||||
typedef actions::ToggleSmartShift action;
|
||||
ToggleSmartShift() :
|
||||
signed_group<std::string>("type", "ToggleSmartShift") { }
|
||||
};
|
||||
|
||||
struct ToggleHiresScroll : public signed_group<std::string> {
|
||||
typedef actions::ToggleHiresScroll action;
|
||||
ToggleHiresScroll() :
|
||||
signed_group<std::string>("type", "ToggleHiresScroll") { }
|
||||
};
|
||||
|
||||
struct CycleDPI : public signed_group<std::string> {
|
||||
typedef actions::CycleDPI action;
|
||||
std::list<int> dpis;
|
||||
std::optional<int> sensor;
|
||||
CycleDPI() : signed_group<std::string>(
|
||||
"type", "CycleDPI",
|
||||
{"dpis", "sensor"},
|
||||
&CycleDPI::dpis,
|
||||
&CycleDPI::sensor) { }
|
||||
};
|
||||
|
||||
struct ChangeDPI : public signed_group<std::string> {
|
||||
typedef actions::ChangeDPI action;
|
||||
int inc;
|
||||
std::optional<int> sensor;
|
||||
ChangeDPI() : signed_group<std::string>(
|
||||
"type", "ChangeDPI",
|
||||
{"inc", "sensor"},
|
||||
&ChangeDPI::inc,
|
||||
&ChangeDPI::sensor) { }
|
||||
};
|
||||
|
||||
struct ChangeHost : public signed_group<std::string> {
|
||||
typedef actions::ChangeHostAction action;
|
||||
std::variant<int, std::string> host;
|
||||
ChangeHost() : signed_group<std::string>(
|
||||
"type", "ChangeHost",
|
||||
{"host"}, &ChangeHost::host) { }
|
||||
};
|
||||
|
||||
typedef std::variant<
|
||||
NoAction,
|
||||
KeypressAction,
|
||||
ToggleSmartShift,
|
||||
ToggleHiresScroll,
|
||||
CycleDPI,
|
||||
ChangeDPI,
|
||||
ChangeHost
|
||||
> BasicAction;
|
||||
|
||||
struct AxisGesture : public signed_group<std::string> {
|
||||
typedef actions::AxisGesture gesture;
|
||||
std::optional<int> threshold;
|
||||
std::variant<std::string, uint> axis;
|
||||
std::optional<double> axis_multiplier;
|
||||
|
||||
AxisGesture() : signed_group("mode", "Axis",
|
||||
{"threshold", "axis", "axis_multiplier"},
|
||||
&AxisGesture::threshold,
|
||||
&AxisGesture::axis,
|
||||
&AxisGesture::axis_multiplier) { }
|
||||
};
|
||||
|
||||
struct IntervalGesture : public signed_group<std::string> {
|
||||
typedef actions::IntervalGesture gesture;
|
||||
std::optional<int> threshold;
|
||||
std::optional<BasicAction> action;
|
||||
int interval;
|
||||
protected:
|
||||
IntervalGesture(const std::string& name) : signed_group(
|
||||
"mode", name,
|
||||
{"threshold", "action", "interval"},
|
||||
&IntervalGesture::threshold,
|
||||
&IntervalGesture::action,
|
||||
&IntervalGesture::interval) { }
|
||||
public:
|
||||
IntervalGesture() : IntervalGesture("OnInterval") { }
|
||||
};
|
||||
|
||||
struct FewPixelsGesture : public IntervalGesture {
|
||||
FewPixelsGesture() : IntervalGesture("OnFewPixels") { }
|
||||
};
|
||||
|
||||
struct ReleaseGesture : public signed_group<std::string> {
|
||||
typedef actions::ReleaseGesture gesture;
|
||||
std::optional<int> threshold;
|
||||
std::optional<BasicAction> action;
|
||||
|
||||
ReleaseGesture() : signed_group("mode", "OnRelease",
|
||||
{"threshold", "action"},
|
||||
&ReleaseGesture::threshold,
|
||||
&ReleaseGesture::action) { }
|
||||
};
|
||||
|
||||
struct ThresholdGesture : public signed_group<std::string> {
|
||||
typedef actions::ThresholdGesture gesture;
|
||||
std::optional<int> threshold;
|
||||
std::optional<BasicAction> action;
|
||||
|
||||
ThresholdGesture() : signed_group("mode", "OnThreshold",
|
||||
{"threshold", "action"},
|
||||
&ThresholdGesture::threshold,
|
||||
&ThresholdGesture::action) { }
|
||||
};
|
||||
|
||||
struct NoGesture : public signed_group<std::string> {
|
||||
typedef actions::NullGesture gesture;
|
||||
std::optional<int> threshold;
|
||||
NoGesture() : signed_group("mode", "NoPress",
|
||||
{"threshold"},
|
||||
&NoGesture::threshold) { }
|
||||
};
|
||||
|
||||
typedef std::variant<
|
||||
NoGesture,
|
||||
AxisGesture,
|
||||
IntervalGesture,
|
||||
FewPixelsGesture,
|
||||
ReleaseGesture,
|
||||
ThresholdGesture
|
||||
> Gesture;
|
||||
|
||||
|
||||
struct GestureAction : public signed_group<std::string> {
|
||||
typedef actions::GestureAction action;
|
||||
std::optional<map<std::string, Gesture, "direction">> gestures;
|
||||
|
||||
GestureAction() : signed_group<std::string>(
|
||||
"type", "Gestures",
|
||||
{"gestures"},
|
||||
&GestureAction::gestures) { }
|
||||
};
|
||||
|
||||
typedef std::variant<
|
||||
NoAction,
|
||||
KeypressAction,
|
||||
ToggleSmartShift,
|
||||
ToggleHiresScroll,
|
||||
CycleDPI,
|
||||
ChangeDPI,
|
||||
ChangeHost,
|
||||
GestureAction
|
||||
> Action;
|
||||
|
||||
struct Button : public group {
|
||||
std::optional<Action> action;
|
||||
Button() : group({"action"},
|
||||
&Button::action) { }
|
||||
};
|
||||
|
||||
struct SmartShift : public group {
|
||||
std::optional<bool> on;
|
||||
std::optional<unsigned int> threshold;
|
||||
SmartShift() : group({"on", "threshold"},
|
||||
&SmartShift::on, &SmartShift::threshold) { }
|
||||
};
|
||||
|
||||
|
||||
struct HiresScroll : public group {
|
||||
std::optional<bool> hires;
|
||||
std::optional<bool> invert;
|
||||
std::optional<bool> target;
|
||||
std::optional<Gesture> up;
|
||||
std::optional<Gesture> down;
|
||||
HiresScroll() : group({"hires", "invert", "target", "up", "down"},
|
||||
&HiresScroll::hires,
|
||||
&HiresScroll::invert,
|
||||
&HiresScroll::target,
|
||||
&HiresScroll::up,
|
||||
&HiresScroll::down) { }
|
||||
};
|
||||
|
||||
typedef std::variant<int, std::list<int>> DPI;
|
||||
|
||||
struct ThumbWheel : public group {
|
||||
std::optional<bool> divert;
|
||||
std::optional<bool> invert;
|
||||
std::optional<Gesture> left;
|
||||
std::optional<Gesture> right;
|
||||
std::optional<BasicAction> proxy;
|
||||
std::optional<BasicAction> touch;
|
||||
std::optional<BasicAction> tap;
|
||||
|
||||
ThumbWheel() : group({"divert", "invert", "left", "right",
|
||||
"proxy", "touch", "tap" },
|
||||
&ThumbWheel::divert, &ThumbWheel::invert,
|
||||
&ThumbWheel::left, &ThumbWheel::right,
|
||||
&ThumbWheel::proxy, &ThumbWheel::touch,
|
||||
&ThumbWheel::tap) { }
|
||||
};
|
||||
|
||||
typedef map<uint16_t, Button, "cid"> RemapButton;
|
||||
|
||||
struct Profile : public group {
|
||||
std::optional<DPI> dpi;
|
||||
std::optional<SmartShift> smartshift;
|
||||
std::optional<std::variant<bool, HiresScroll>> hiresscroll;
|
||||
std::optional<ThumbWheel> thumbwheel;
|
||||
std::optional<RemapButton> buttons;
|
||||
|
||||
Profile() : group({"dpi", "smartshift", "hiresscroll",
|
||||
"buttons", "thumbwheel"},
|
||||
&Profile::dpi, &Profile::smartshift,
|
||||
&Profile::hiresscroll, &Profile::buttons,
|
||||
&Profile::thumbwheel) { }
|
||||
};
|
||||
|
||||
struct Device : public group {
|
||||
std::string default_profile;
|
||||
map<std::string, Profile, "name"> profiles;
|
||||
|
||||
Device() : group({"default_profile", "profiles"},
|
||||
&Device::default_profile,
|
||||
&Device::profiles) { }
|
||||
};
|
||||
|
||||
struct Config : public group {
|
||||
std::optional<map<std::string,
|
||||
std::variant<Device, Profile>, "name">> devices;
|
||||
std::optional<std::set<uint16_t>> ignore;
|
||||
std::optional<double> io_timeout;
|
||||
std::optional<int> workers;
|
||||
Config() : group({"devices", "ignore", "io_timeout", "workers"},
|
||||
&Config::devices,
|
||||
&Config::ignore,
|
||||
&Config::io_timeout,
|
||||
&Config::workers) { }
|
||||
};
|
||||
}
|
||||
|
||||
#endif //LOGID_CONFIG_SCHEMA_H
|
446
src/logid/config/types.h
Normal file
446
src/logid/config/types.h
Normal file
@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Copyright 2022 PixlOne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef LOGID_CONFIG_PRIMITIVE_H
|
||||
#define LOGID_CONFIG_PRIMITIVE_H
|
||||
|
||||
#include <libconfig.h++>
|
||||
#include <type_traits>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include "group.h"
|
||||
#include "map.h"
|
||||
#include "../util/log.h"
|
||||
|
||||
/// TODO: A single element failing should not cause the container to be invalid.
|
||||
|
||||
// Containers are chosen specifically so that no iterator is invalidated.
|
||||
|
||||
namespace logid::config {
|
||||
namespace {
|
||||
template <typename T>
|
||||
struct config_io {
|
||||
static_assert(std::is_base_of<group, T>::value);
|
||||
|
||||
static T get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
T t {};
|
||||
t._load(parent.lookup(name));
|
||||
return t;
|
||||
}
|
||||
|
||||
static T get(const libconfig::Setting& setting) {
|
||||
T t {};
|
||||
t._load(setting);
|
||||
return t;
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const T& t) {
|
||||
if(!parent.exists(name)) {
|
||||
parent.add(name, libconfig::Setting::TypeGroup);
|
||||
} else if(parent.lookup(name).getType()
|
||||
!= libconfig::Setting::TypeGroup) {
|
||||
parent.remove(name);
|
||||
parent.add(name, libconfig::Setting::TypeGroup);
|
||||
}
|
||||
t._save(parent.lookup(name));
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& setting, const T& t) {
|
||||
t._save(setting);
|
||||
}
|
||||
|
||||
static void append(libconfig::Setting& list, const T& t) {
|
||||
auto& x = list.add(libconfig::Setting::TypeGroup);
|
||||
set(x, t);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, libconfig::Setting::Type TypeEnum>
|
||||
struct primitive_io {
|
||||
static T get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
return parent.lookup(name);
|
||||
}
|
||||
|
||||
static T get(const libconfig::Setting& setting) {
|
||||
return setting;
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const T& t) {
|
||||
if(!parent.exists(name)) {
|
||||
parent.add(name, TypeEnum);
|
||||
} else if(parent.lookup(name).getType() != TypeEnum) {
|
||||
parent.remove(name);
|
||||
parent.add(name, TypeEnum);
|
||||
}
|
||||
set(parent.lookup(name), t);
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& setting, const T& t) {
|
||||
setting = t;
|
||||
}
|
||||
|
||||
static void append(libconfig::Setting& list, const T& t) {
|
||||
auto& x = list.add(TypeEnum);
|
||||
set(x, t);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename O, libconfig::Setting::Type TypeEnum>
|
||||
struct reinterpret_io {
|
||||
static T get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
return static_cast<T>(primitive_io<O, TypeEnum>::get(parent, name));
|
||||
}
|
||||
|
||||
static T get(const libconfig::Setting& setting) {
|
||||
return static_cast<T>(primitive_io<O, TypeEnum>::get(setting));
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const T& t) {
|
||||
primitive_io<O, TypeEnum>::set(parent, name,
|
||||
static_cast<O>(t));
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& setting, const T& t) {
|
||||
primitive_io<O, TypeEnum>::set(setting,
|
||||
static_cast<O>(t));
|
||||
}
|
||||
|
||||
static void append(libconfig::Setting& list, const T& t) {
|
||||
primitive_io<O, TypeEnum>::append(list,
|
||||
static_cast<O>(t));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct config_io<bool> : public primitive_io<bool,
|
||||
libconfig::Setting::TypeBoolean> { };
|
||||
template <>
|
||||
struct config_io<int8_t> : public reinterpret_io<int8_t, int,
|
||||
libconfig::Setting::TypeInt> { };
|
||||
template <>
|
||||
struct config_io<uint8_t> : public reinterpret_io<uint8_t, int,
|
||||
libconfig::Setting::TypeInt> { };
|
||||
template <>
|
||||
struct config_io<short> : public reinterpret_io<short, int,
|
||||
libconfig::Setting::TypeInt> { };
|
||||
template <>
|
||||
struct config_io<unsigned short> : public reinterpret_io<unsigned short,
|
||||
int, libconfig::Setting::TypeInt> { };
|
||||
template <>
|
||||
struct config_io<int> : public primitive_io<int,
|
||||
libconfig::Setting::TypeInt> { };
|
||||
template <>
|
||||
struct config_io<unsigned int> : public reinterpret_io<unsigned int,
|
||||
int, libconfig::Setting::TypeInt> { };
|
||||
template <>
|
||||
struct config_io<long> : public reinterpret_io<long, long long,
|
||||
libconfig::Setting::TypeInt64> { };
|
||||
template <>
|
||||
struct config_io<unsigned long> : public reinterpret_io<unsigned long,
|
||||
long long, libconfig::Setting::TypeInt64> { };
|
||||
template <>
|
||||
struct config_io<long long> : public primitive_io<long long,
|
||||
libconfig::Setting::TypeInt64> { };
|
||||
template <>
|
||||
struct config_io<unsigned long long> :
|
||||
public reinterpret_io<unsigned long long, long long,
|
||||
libconfig::Setting::TypeInt64> { };
|
||||
template <>
|
||||
struct config_io<float> : public primitive_io<float,
|
||||
libconfig::Setting::TypeFloat> { };
|
||||
template <>
|
||||
struct config_io<double> : public primitive_io<double,
|
||||
libconfig::Setting::TypeFloat> { };
|
||||
template <>
|
||||
struct config_io<std::string> : public primitive_io<std::string,
|
||||
libconfig::Setting::TypeString> { };
|
||||
|
||||
template <typename... T>
|
||||
struct config_io<std::variant<T...>> {
|
||||
private:
|
||||
template <typename Singleton>
|
||||
static std::variant<T...> try_each(
|
||||
const libconfig::Setting& setting) {
|
||||
try {
|
||||
return config_io<Singleton>::get(setting);
|
||||
} catch(libconfig::SettingTypeException& e) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename First, typename Next, typename... Rest>
|
||||
static std::variant<T...> try_each(
|
||||
const libconfig::Setting& setting) {
|
||||
try {
|
||||
return config_io<First>::get(setting);
|
||||
} catch(libconfig::SettingException& e) {
|
||||
return try_each<Next, Rest...>(setting);
|
||||
}
|
||||
}
|
||||
public:
|
||||
static std::variant<T...> get(const libconfig::Setting& setting) {
|
||||
return try_each<T...>(setting);
|
||||
}
|
||||
|
||||
static std::variant<T...> get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
return get(parent.lookup(name));
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& setting,
|
||||
const std::variant<T...>& t) {
|
||||
std::visit([&setting](auto&& arg){
|
||||
config::set(setting, arg);
|
||||
}, t);
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const std::variant<T...>& t) {
|
||||
std::visit([&parent, &name](auto&& arg){
|
||||
config::set(parent, name, arg);
|
||||
}, t);
|
||||
}
|
||||
|
||||
static void append(libconfig::Setting& list,
|
||||
const std::variant<T...>& t) {
|
||||
std::visit([&list](auto&& arg){
|
||||
config::append(list, arg);
|
||||
}, t);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct config_io<std::list<T>> {
|
||||
static std::list<T> get(const libconfig::Setting& setting) {
|
||||
const auto size = setting.getLength();
|
||||
std::list<T> t {};
|
||||
for(int i = 0; i < size; ++i) {
|
||||
try {
|
||||
t.emplace_back(config_io<T>::get(setting[i]));
|
||||
} catch(libconfig::SettingException& e) { }
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static std::list<T> get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
return get(parent.lookup(name));
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& setting,
|
||||
const std::list<T>& t) {
|
||||
while(setting.getLength() != 0)
|
||||
setting.remove((int)0);
|
||||
for(auto& x : t) {
|
||||
config_io<T>::append(setting, x);
|
||||
}
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const std::list<T>& t) {
|
||||
if (!parent.exists(name)) {
|
||||
parent.add(name, libconfig::Setting::TypeList);
|
||||
} else if(!parent.lookup(name).isList()) {
|
||||
parent.remove(name);
|
||||
parent.add(name, libconfig::Setting::TypeList);
|
||||
}
|
||||
set(parent.lookup(name), t);
|
||||
}
|
||||
|
||||
static void append(libconfig::Setting& list,
|
||||
const std::list<T>& t) {
|
||||
auto& s = list.add(libconfig::Setting::TypeList);
|
||||
set(s, t);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct config_io<std::set<T>> {
|
||||
static std::set<T> get(const libconfig::Setting& setting) {
|
||||
const auto size = setting.getLength();
|
||||
std::set<T> t;
|
||||
for(int i = 0; i < size; ++i) {
|
||||
try {
|
||||
t.emplace(config_io<T>::get(setting[i]));
|
||||
} catch(libconfig::SettingException& e) { }
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static std::set<T> get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
return get(parent.lookup(name));
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& setting,
|
||||
const std::set<T>& t) {
|
||||
while(setting.getLength() != 0)
|
||||
setting.remove((int)0);
|
||||
for(auto& x : t) {
|
||||
auto& s = setting.add(libconfig::Setting::TypeGroup);
|
||||
config_io<T>::set(s, x);
|
||||
}
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const std::set<T>& t) {
|
||||
if (!parent.exists(name)) {
|
||||
parent.add(name, libconfig::Setting::TypeList);
|
||||
} else if(!parent.lookup(name).isArray()) {
|
||||
parent.remove(name);
|
||||
parent.add(name, libconfig::Setting::TypeList);
|
||||
}
|
||||
set(parent.lookup(name), t);
|
||||
}
|
||||
|
||||
static void append(libconfig::Setting& list,
|
||||
const std::set<T>& t) {
|
||||
auto& s = list.add(libconfig::Setting::TypeList);
|
||||
set(s, t);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename K, typename V, string_literal KeyName>
|
||||
struct config_io<map<K, V, KeyName>> {
|
||||
static map<K, V, KeyName> get(const libconfig::Setting& setting) {
|
||||
const auto size = setting.getLength();
|
||||
map<K, V, KeyName> t;
|
||||
for(int i = 0; i < size; ++i) {
|
||||
auto& s = setting[i];
|
||||
try {
|
||||
t.emplace(config_io<K>::get(s.lookup(KeyName.value)),
|
||||
config_io<V>::get(s));
|
||||
} catch(libconfig::SettingException& e) { }
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static map<K, V, KeyName> get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
return get(parent.lookup(name));
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& setting,
|
||||
const map<K, V, KeyName>& t) {
|
||||
while(setting.getLength() != 0)
|
||||
setting.remove((int)0);
|
||||
for(auto& x : t) {
|
||||
auto& s = setting.add(libconfig::Setting::TypeGroup);
|
||||
config_io<V>::set(s, x.second);
|
||||
config_io<K>::set(s, KeyName.value, x.first);
|
||||
}
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const map<K, V, KeyName>& t) {
|
||||
if (!parent.exists(name)) {
|
||||
parent.add(name, libconfig::Setting::TypeList);
|
||||
} else if(!parent.lookup(name).isArray()) {
|
||||
parent.remove(name);
|
||||
parent.add(name, libconfig::Setting::TypeList);
|
||||
}
|
||||
set(parent.lookup(name), t);
|
||||
}
|
||||
|
||||
static void append(libconfig::Setting& list,
|
||||
const map<K, V, KeyName>& t) {
|
||||
auto& s = list.add(libconfig::Setting::TypeList);
|
||||
set(s, t);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct config_io<std::optional<T>> {
|
||||
static std::optional<T> get(const libconfig::Setting& parent,
|
||||
const std::string& name) {
|
||||
try {
|
||||
return config_io<T>::get(parent.lookup(name));
|
||||
} catch(libconfig::SettingException& e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const std::optional<T>& t) {
|
||||
if (t.has_value())
|
||||
config_io<T>::set(parent, name, t.value());
|
||||
}
|
||||
};
|
||||
|
||||
// Optionals may not appear as part of a list or array
|
||||
template <typename T, typename... Rest>
|
||||
struct config_io<std::variant<std::optional<T>, Rest...>> {
|
||||
static_assert(!sizeof(std::optional<T>), "Invalid type");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct config_io<std::list<std::optional<T>>> {
|
||||
static_assert(!sizeof(std::optional<T>), "Invalid type");
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct config_io<std::optional<std::optional<T>>> {
|
||||
static_assert(!sizeof(std::optional<T>), "Invalid type");
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set(libconfig::Setting& parent,
|
||||
const std::string& name,
|
||||
const T& t) {
|
||||
config_io<T>::set(parent, name, t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set(libconfig::Setting& setting, const T& t) {
|
||||
config_io<T>::set(setting, t);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void append(libconfig::Setting& list, const T& t) {
|
||||
config_io<T>::set(list, t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get(const libconfig::Setting& setting) {
|
||||
return config_io<T>::get(setting);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get(const libconfig::Setting& parent, const std::string& name) {
|
||||
return config_io<T>::get(parent, name);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //LOGID_CONFIG_PRIMITIVE_H
|
@ -19,12 +19,11 @@
|
||||
#include <cmath>
|
||||
#include "DPI.h"
|
||||
#include "../Device.h"
|
||||
#include "../util/log.h"
|
||||
|
||||
using namespace logid::features;
|
||||
using namespace logid::backend;
|
||||
|
||||
uint16_t getClosestDPI(hidpp20::AdjustableDPI::SensorDPIList& dpi_list,
|
||||
uint16_t getClosestDPI(const hidpp20::AdjustableDPI::SensorDPIList& dpi_list,
|
||||
uint16_t dpi)
|
||||
{
|
||||
if(dpi_list.isRange) {
|
||||
@ -58,7 +57,8 @@ uint16_t getClosestDPI(hidpp20::AdjustableDPI::SensorDPIList& dpi_list,
|
||||
}
|
||||
}
|
||||
|
||||
DPI::DPI(Device* device) : DeviceFeature(device), _config (device)
|
||||
DPI::DPI(Device* device) : DeviceFeature(device),
|
||||
_config (device->activeProfile().dpi)
|
||||
{
|
||||
try {
|
||||
_adjustable_dpi = std::make_shared<hidpp20::AdjustableDPI>
|
||||
@ -70,20 +70,23 @@ DPI::DPI(Device* device) : DeviceFeature(device), _config (device)
|
||||
|
||||
void DPI::configure()
|
||||
{
|
||||
const uint8_t sensors = _adjustable_dpi->getSensorCount();
|
||||
for(uint8_t i = 0; i < _config.getSensorCount(); i++) {
|
||||
hidpp20::AdjustableDPI::SensorDPIList dpi_list;
|
||||
if(_dpi_lists.size() <= i) {
|
||||
dpi_list = _adjustable_dpi->getSensorDPIList(i);
|
||||
_dpi_lists.push_back(dpi_list);
|
||||
if(_config.has_value()) {
|
||||
const auto& config = _config.value();
|
||||
if(std::holds_alternative<int>(config)) {
|
||||
const auto& dpi = std::get<int>(config);
|
||||
_adjustable_dpi->setSensorDPI(
|
||||
0,
|
||||
getClosestDPI(_adjustable_dpi->getSensorDPIList(0),
|
||||
dpi) );
|
||||
} else {
|
||||
dpi_list = _dpi_lists[i];
|
||||
}
|
||||
if(i < sensors) {
|
||||
auto dpi = _config.getDPI(i);
|
||||
if(dpi) {
|
||||
_adjustable_dpi->setSensorDPI(i, getClosestDPI(dpi_list,
|
||||
dpi));
|
||||
const auto& dpis = std::get<std::list<int>>(config);
|
||||
int i = 0;
|
||||
for(const auto& dpi : dpis) {
|
||||
_adjustable_dpi->setSensorDPI(
|
||||
i,
|
||||
getClosestDPI(_adjustable_dpi->getSensorDPIList(i),
|
||||
dpi) );
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -111,36 +114,3 @@ void DPI::setDPI(uint16_t dpi, uint8_t sensor)
|
||||
dpi_list = _dpi_lists[sensor];
|
||||
_adjustable_dpi->setSensorDPI(sensor, getClosestDPI(dpi_list, dpi));
|
||||
}
|
||||
|
||||
/* Some devices have multiple sensors, but an older config format
|
||||
* only supports a single DPI. The dpi setting can be an array or
|
||||
* an integer.
|
||||
*/
|
||||
DPI::Config::Config(Device *dev) : DeviceFeature::Config(dev)
|
||||
{
|
||||
try {
|
||||
auto& config_root = dev->config().getSetting("dpi");
|
||||
if(config_root.isNumber()) {
|
||||
int dpi = config_root;
|
||||
_dpis.push_back(dpi);
|
||||
} else if(config_root.isArray()) {
|
||||
for(int i = 0; i < config_root.getLength(); i++)
|
||||
_dpis.push_back((int)config_root[i]);
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: dpi is improperly formatted",
|
||||
config_root.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// DPI not configured, use default
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t DPI::Config::getSensorCount()
|
||||
{
|
||||
return _dpis.size();
|
||||
}
|
||||
|
||||
uint16_t DPI::Config::getDPI(uint8_t sensor)
|
||||
{
|
||||
return _dpis[sensor];
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "../backend/hidpp20/features/AdjustableDPI.h"
|
||||
#include "DeviceFeature.h"
|
||||
#include "../config/schema.h"
|
||||
|
||||
namespace logid {
|
||||
namespace features
|
||||
@ -33,18 +34,8 @@ namespace features
|
||||
|
||||
uint16_t getDPI(uint8_t sensor=0);
|
||||
void setDPI(uint16_t dpi, uint8_t sensor=0);
|
||||
|
||||
class Config : public DeviceFeature::Config
|
||||
{
|
||||
public:
|
||||
explicit Config(Device* dev);
|
||||
uint16_t getDPI(uint8_t sensor);
|
||||
uint8_t getSensorCount();
|
||||
protected:
|
||||
std::vector<uint16_t> _dpis;
|
||||
};
|
||||
private:
|
||||
Config _config;
|
||||
std::optional<config::DPI>& _config;
|
||||
std::shared_ptr<backend::hidpp20::AdjustableDPI> _adjustable_dpi;
|
||||
std::vector<backend::hidpp20::AdjustableDPI::SensorDPIList> _dpi_lists;
|
||||
};
|
||||
|
@ -44,15 +44,6 @@ namespace features
|
||||
virtual void configure() = 0;
|
||||
virtual void listen() = 0;
|
||||
virtual ~DeviceFeature() = default;
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
explicit Config(Device* dev) : _device (dev)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
Device* _device;
|
||||
};
|
||||
|
||||
protected:
|
||||
Device* _device;
|
||||
|
@ -34,7 +34,7 @@ DeviceStatus::DeviceStatus(logid::Device *dev) : DeviceFeature(dev)
|
||||
throw UnsupportedFeature();
|
||||
|
||||
try {
|
||||
_wireless_device_status =std::make_shared<
|
||||
_wireless_device_status = std::make_shared<
|
||||
hidpp20::WirelessDeviceStatus>(&dev->hidpp20());
|
||||
} catch(hidpp20::UnsupportedFeature& e) {
|
||||
throw UnsupportedFeature();
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "HiresScroll.h"
|
||||
#include "../Device.h"
|
||||
#include "../InputDevice.h"
|
||||
#include "../actions/gesture/Gesture.h"
|
||||
#include "../actions/gesture/AxisGesture.h"
|
||||
|
||||
using namespace logid::features;
|
||||
@ -26,38 +25,45 @@ using namespace logid::backend;
|
||||
|
||||
#define MOVE_EVENTHANDLER_NAME "HIRES_SCROLL"
|
||||
|
||||
HiresScroll::HiresScroll(Device *dev) : DeviceFeature(dev), _config(dev)
|
||||
HiresScroll::HiresScroll(Device *dev) : DeviceFeature(dev),
|
||||
_config (dev->activeProfile().hiresscroll), _mode (0), _mask (0)
|
||||
{
|
||||
if(_config.has_value()) {
|
||||
if(std::holds_alternative<bool>(_config.value())) {
|
||||
config::HiresScroll conf {};
|
||||
conf.hires = std::get<bool>(_config.value());
|
||||
conf.invert = false;
|
||||
conf.target = false;
|
||||
_mask |= hidpp20::HiresScroll::Mode::HiRes;
|
||||
_config.value() = conf;
|
||||
}
|
||||
auto& conf = std::get<config::HiresScroll>(_config.value());
|
||||
if(conf.hires.has_value()) {
|
||||
_mask |= hidpp20::HiresScroll::Mode::HiRes;
|
||||
if(conf.hires.value())
|
||||
_mode |= hidpp20::HiresScroll::Mode::HiRes;
|
||||
}
|
||||
if(conf.invert.has_value()) {
|
||||
_mask |= hidpp20::HiresScroll::Mode::Inverted;
|
||||
if(conf.invert.value())
|
||||
_mode |= hidpp20::HiresScroll::Mode::Inverted;
|
||||
}
|
||||
if(conf.target.has_value()) {
|
||||
_mask |= hidpp20::HiresScroll::Mode::Target;
|
||||
if(conf.target.value())
|
||||
_mode |= hidpp20::HiresScroll::Mode::Target;
|
||||
}
|
||||
|
||||
_makeAction(_up_action, conf.up);
|
||||
_makeAction(_down_action, conf.down);
|
||||
}
|
||||
|
||||
try {
|
||||
_hires_scroll = std::make_shared<hidpp20::HiresScroll>(&dev->hidpp20());
|
||||
} catch(hidpp20::UnsupportedFeature& e) {
|
||||
throw UnsupportedFeature();
|
||||
}
|
||||
|
||||
if(_config.upAction()) {
|
||||
try {
|
||||
auto up_axis = std::dynamic_pointer_cast<actions::AxisGesture>(
|
||||
_config.upAction());
|
||||
if(up_axis)
|
||||
up_axis->setHiresMultiplier(
|
||||
_hires_scroll->getCapabilities().multiplier);
|
||||
} catch(std::bad_cast& e) { }
|
||||
|
||||
_config.upAction()->press(true);
|
||||
}
|
||||
|
||||
if(_config.downAction()) {
|
||||
try {
|
||||
auto down_axis = std::dynamic_pointer_cast<actions::AxisGesture>(
|
||||
_config.downAction());
|
||||
if(down_axis)
|
||||
down_axis->setHiresMultiplier(
|
||||
_hires_scroll->getCapabilities().multiplier);
|
||||
} catch(std::bad_cast& e) { }
|
||||
|
||||
_config.downAction()->press(true);
|
||||
}
|
||||
|
||||
_last_scroll = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
@ -69,8 +75,8 @@ HiresScroll::~HiresScroll()
|
||||
void HiresScroll::configure()
|
||||
{
|
||||
auto mode = _hires_scroll->getMode();
|
||||
mode &= ~_config.getMask();
|
||||
mode |= (_config.getMode() & _config.getMask());
|
||||
mode &= ~_mask;
|
||||
mode |= (_mode & _mask);
|
||||
_hires_scroll->setMode(mode);
|
||||
}
|
||||
|
||||
@ -103,18 +109,37 @@ void HiresScroll::setMode(uint8_t mode)
|
||||
_hires_scroll->setMode(mode);
|
||||
}
|
||||
|
||||
void HiresScroll::_makeAction(std::shared_ptr<actions::Gesture> &gesture,
|
||||
std::optional<config::Gesture> &config)
|
||||
{
|
||||
if(config.has_value()) {
|
||||
gesture = actions::Gesture::makeGesture(_device, config.value());
|
||||
try {
|
||||
auto axis = std::dynamic_pointer_cast<actions::AxisGesture>(
|
||||
gesture);
|
||||
if(axis)
|
||||
axis->setHiresMultiplier(
|
||||
_hires_scroll->getCapabilities().multiplier);
|
||||
} catch(std::bad_cast& e) { }
|
||||
if(gesture)
|
||||
gesture->press(true);
|
||||
} else {
|
||||
gesture.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void HiresScroll::_handleScroll(hidpp20::HiresScroll::WheelStatus event)
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
if(std::chrono::duration_cast<std::chrono::seconds>(
|
||||
now - _last_scroll).count() >= 1) {
|
||||
if(_config.upAction()) {
|
||||
_config.upAction()->release();
|
||||
_config.upAction()->press(true);
|
||||
if(_up_action) {
|
||||
_up_action->release();
|
||||
_up_action->press(true);
|
||||
}
|
||||
if(_config.downAction()) {
|
||||
_config.downAction()->release();
|
||||
_config.downAction()->press(true);
|
||||
if(_down_action) {
|
||||
_down_action->release();
|
||||
_down_action->press(true);
|
||||
}
|
||||
|
||||
_last_direction = 0;
|
||||
@ -122,140 +147,25 @@ void HiresScroll::_handleScroll(hidpp20::HiresScroll::WheelStatus event)
|
||||
|
||||
if(event.deltaV > 0) {
|
||||
if(_last_direction == -1) {
|
||||
if(_config.downAction()){
|
||||
_config.downAction()->release();
|
||||
_config.downAction()->press(true);
|
||||
if(_down_action){
|
||||
_down_action->release();
|
||||
_down_action->press(true);
|
||||
}
|
||||
}
|
||||
if(_config.upAction())
|
||||
_config.upAction()->move(event.deltaV);
|
||||
if(_up_action)
|
||||
_up_action->move(event.deltaV);
|
||||
_last_direction = 1;
|
||||
} else if(event.deltaV < 0) {
|
||||
if(_last_direction == 1) {
|
||||
if(_config.upAction()){
|
||||
_config.upAction()->release();
|
||||
_config.upAction()->press(true);
|
||||
if(_up_action){
|
||||
_up_action->release();
|
||||
_up_action->press(true);
|
||||
}
|
||||
}
|
||||
if(_config.downAction())
|
||||
_config.downAction()->move(-event.deltaV);
|
||||
if(_down_action)
|
||||
_down_action->move(-event.deltaV);
|
||||
_last_direction = -1;
|
||||
}
|
||||
|
||||
_last_scroll = now;
|
||||
}
|
||||
|
||||
HiresScroll::Config::Config(Device *dev) : DeviceFeature::Config(dev)
|
||||
{
|
||||
try {
|
||||
auto& config_root = dev->config().getSetting("hiresscroll");
|
||||
if(!config_root.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: hiresscroll must be a group",
|
||||
config_root.getSourceLine());
|
||||
return;
|
||||
}
|
||||
_mode = 0;
|
||||
_mask = 0;
|
||||
try {
|
||||
auto& hires = config_root.lookup("hires");
|
||||
if(hires.getType() == libconfig::Setting::TypeBoolean) {
|
||||
_mask |= hidpp20::HiresScroll::Mode::HiRes;
|
||||
if(hires)
|
||||
_mode |= hidpp20::HiresScroll::Mode::HiRes;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: hires must be a boolean",
|
||||
hires.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) { }
|
||||
|
||||
try {
|
||||
auto& invert = config_root.lookup("invert");
|
||||
if(invert.getType() == libconfig::Setting::TypeBoolean) {
|
||||
_mask |= hidpp20::HiresScroll::Mode::Inverted;
|
||||
if(invert)
|
||||
_mode |= hidpp20::HiresScroll::Mode::Inverted;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: invert must be a boolean, ignoring.",
|
||||
invert.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) { }
|
||||
|
||||
try {
|
||||
auto& target = config_root.lookup("target");
|
||||
if(target.getType() == libconfig::Setting::TypeBoolean) {
|
||||
_mask |= hidpp20::HiresScroll::Mode::Target;
|
||||
if(target)
|
||||
_mode |= hidpp20::HiresScroll::Mode::Target;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: target must be a boolean, ignoring.",
|
||||
target.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) { }
|
||||
|
||||
if(_mode & hidpp20::HiresScroll::Mode::Target) {
|
||||
try {
|
||||
auto& up = config_root.lookup("up");
|
||||
try {
|
||||
auto g = actions::Gesture::makeGesture(dev, up);
|
||||
if(g->wheelCompatibility()) {
|
||||
_up_action = g;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: This gesture cannot be used"
|
||||
" as a scroll action.",
|
||||
up.getSourceLine());
|
||||
}
|
||||
} catch(actions::InvalidGesture& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid scroll action",
|
||||
up.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException&) {
|
||||
logPrintf(WARN, "Line %d: target is true but no up action was"
|
||||
" set", config_root.getSourceLine());
|
||||
}
|
||||
|
||||
try {
|
||||
auto& down = config_root.lookup("down");
|
||||
try {
|
||||
auto g = actions::Gesture::makeGesture(dev, down);
|
||||
if(g->wheelCompatibility()) {
|
||||
_down_action = g;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: This gesture cannot be used"
|
||||
" as a scroll action.",
|
||||
down.getSourceLine());
|
||||
}
|
||||
} catch(actions::InvalidGesture& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid scroll action",
|
||||
down.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException&) {
|
||||
logPrintf(WARN, "Line %d: target is true but no down action was"
|
||||
" set", config_root.getSourceLine());
|
||||
}
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// HiresScroll not configured, use default
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t HiresScroll::Config::getMode() const
|
||||
{
|
||||
return _mode;
|
||||
}
|
||||
|
||||
uint8_t HiresScroll::Config::getMask() const
|
||||
{
|
||||
return _mask;
|
||||
}
|
||||
|
||||
const std::shared_ptr<logid::actions::Gesture>&
|
||||
HiresScroll::Config::upAction() const
|
||||
{
|
||||
return _up_action;
|
||||
}
|
||||
|
||||
const std::shared_ptr<logid::actions::Gesture>&
|
||||
HiresScroll::Config::downAction() const
|
||||
{
|
||||
return _down_action;
|
||||
}
|
@ -35,29 +35,22 @@ namespace features
|
||||
|
||||
uint8_t getMode();
|
||||
void setMode(uint8_t mode);
|
||||
|
||||
class Config : public DeviceFeature::Config
|
||||
{
|
||||
public:
|
||||
explicit Config(Device* dev);
|
||||
uint8_t getMode() const;
|
||||
uint8_t getMask() const;
|
||||
|
||||
const std::shared_ptr<actions::Gesture>& upAction() const;
|
||||
const std::shared_ptr<actions::Gesture>& downAction() const;
|
||||
protected:
|
||||
uint8_t _mode;
|
||||
uint8_t _mask;
|
||||
|
||||
std::shared_ptr<actions::Gesture> _up_action;
|
||||
std::shared_ptr<actions::Gesture> _down_action;
|
||||
};
|
||||
private:
|
||||
void _makeAction(std::shared_ptr<actions::Gesture>& gesture,
|
||||
std::optional<config::Gesture>& config);
|
||||
|
||||
void _handleScroll(backend::hidpp20::HiresScroll::WheelStatus event);
|
||||
std::shared_ptr<backend::hidpp20::HiresScroll> _hires_scroll;
|
||||
std::chrono::time_point<std::chrono::system_clock> _last_scroll;
|
||||
int16_t _last_direction = 0;
|
||||
Config _config;
|
||||
|
||||
std::optional<std::variant<bool, config::HiresScroll>>& _config;
|
||||
|
||||
uint8_t _mode;
|
||||
uint8_t _mask;
|
||||
|
||||
std::shared_ptr<actions::Gesture> _up_action;
|
||||
std::shared_ptr<actions::Gesture> _down_action;
|
||||
};
|
||||
}}
|
||||
|
||||
|
@ -29,9 +29,26 @@ using namespace logid::actions;
|
||||
|
||||
#define EVENTHANDLER_NAME "REMAP_BUTTON"
|
||||
|
||||
RemapButton::RemapButton(Device *dev): DeviceFeature(dev), _config (dev),
|
||||
RemapButton::RemapButton(Device *dev): DeviceFeature(dev),
|
||||
_config (dev->activeProfile().buttons),
|
||||
_ipc_node (dev->ipcNode()->make_child("buttons"))
|
||||
{
|
||||
if(_config.has_value()) {
|
||||
for(auto& button : _config.value()) {
|
||||
if(button.second.action.has_value()) {
|
||||
try {
|
||||
_buttons.emplace(
|
||||
button.first,
|
||||
Action::makeAction(
|
||||
dev,button.second.action.value()));
|
||||
} catch(std::exception& e) {
|
||||
logPrintf(WARN, "Error creating button action: %s",
|
||||
e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
_reprog_controls = hidpp20::ReprogControls::autoVersion(
|
||||
&dev->hidpp20());
|
||||
@ -42,10 +59,10 @@ RemapButton::RemapButton(Device *dev): DeviceFeature(dev), _config (dev),
|
||||
_reprog_controls->initCidMap();
|
||||
|
||||
if(global_loglevel <= DEBUG) {
|
||||
#define FLAG(x) control.second.flags & hidpp20::ReprogControls::x ? \
|
||||
"YES" : ""
|
||||
#define ADDITIONAL_FLAG(x) control.second.additionalFlags & \
|
||||
hidpp20::ReprogControls::x ? "YES" : ""
|
||||
#define FLAG(x) (control.second.flags & hidpp20::ReprogControls::x ? \
|
||||
"YES" : "")
|
||||
#define ADDITIONAL_FLAG(x) (control.second.additionalFlags & \
|
||||
hidpp20::ReprogControls::x ? "YES" : "")
|
||||
|
||||
// Print CIDs, originally by zv0n
|
||||
logPrintf(DEBUG, "%s:%d remappable buttons:",
|
||||
@ -57,6 +74,7 @@ RemapButton::RemapButton(Device *dev): DeviceFeature(dev), _config (dev),
|
||||
logPrintf(DEBUG, "0x%02x | %-7s | %-7s | %-10s | %s",
|
||||
control.first, FLAG(TemporaryDivertable), FLAG(FKey),
|
||||
FLAG(MouseButton), ADDITIONAL_FLAG(RawXY));
|
||||
#undef ADDITIONAL_FLAG
|
||||
#undef FLAG
|
||||
}
|
||||
|
||||
@ -77,7 +95,7 @@ RemapButton::~RemapButton()
|
||||
void RemapButton::configure()
|
||||
{
|
||||
///TODO: DJ reporting trickery if cannot be remapped
|
||||
for(const auto& i : _config.buttons()) {
|
||||
for(const auto& i : _buttons) {
|
||||
hidpp20::ReprogControls::ControlInfo info{};
|
||||
try {
|
||||
info = _reprog_controls->getControlIdInfo(i.first);
|
||||
@ -123,7 +141,7 @@ void RemapButton::listen()
|
||||
report));
|
||||
else { // RawXY
|
||||
auto divertedXY = _reprog_controls->divertedRawXYEvent(report);
|
||||
for(const auto& button : this->_config.buttons())
|
||||
for(const auto& button : this->_buttons)
|
||||
if(button.second->pressed())
|
||||
button.second->move(divertedXY.x, divertedXY.y);
|
||||
}
|
||||
@ -144,80 +162,22 @@ void RemapButton::_buttonEvent(const std::set<uint16_t>& new_state)
|
||||
if(old_i != _pressed_buttons.end()) {
|
||||
_pressed_buttons.erase(old_i);
|
||||
} else {
|
||||
auto action = _config.buttons().find(i);
|
||||
if(action != _config.buttons().end())
|
||||
auto action = _buttons.find(i);
|
||||
if(action != _buttons.end())
|
||||
action->second->press();
|
||||
}
|
||||
}
|
||||
|
||||
// Release all removed buttons
|
||||
for(auto& i : _pressed_buttons) {
|
||||
auto action = _config.buttons().find(i);
|
||||
if(action != _config.buttons().end())
|
||||
auto action = _buttons.find(i);
|
||||
if(action != _buttons.end())
|
||||
action->second->release();
|
||||
}
|
||||
|
||||
_pressed_buttons = new_state;
|
||||
}
|
||||
|
||||
RemapButton::Config::Config(Device *dev) : DeviceFeature::Config(dev)
|
||||
{
|
||||
try {
|
||||
auto& config_root = dev->config().getSetting("buttons");
|
||||
if(!config_root.isList()) {
|
||||
logPrintf(WARN, "Line %d: buttons must be a list.",
|
||||
config_root.getSourceLine());
|
||||
return;
|
||||
}
|
||||
int button_count = config_root.getLength();
|
||||
for(int i = 0; i < button_count; i++)
|
||||
_parseButton(config_root[i]);
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// buttons not configured, use default
|
||||
}
|
||||
}
|
||||
|
||||
void RemapButton::Config::_parseButton(libconfig::Setting &setting)
|
||||
{
|
||||
if(!setting.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: button must be an object, ignoring.",
|
||||
setting.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t cid;
|
||||
try {
|
||||
auto& cid_setting = setting.lookup("cid");
|
||||
if(!cid_setting.isNumber()) {
|
||||
logPrintf(WARN, "Line %d: cid must be a number, ignoring.",
|
||||
cid_setting.getSourceLine());
|
||||
return;
|
||||
}
|
||||
cid = (int)cid_setting;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: cid is required, ignoring.",
|
||||
setting.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_buttons.emplace(cid, Action::makeAction(_device,
|
||||
setting.lookup("action")));
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
logPrintf(WARN, "Line %d: action is required, ignoring.",
|
||||
setting.getSourceLine());
|
||||
} catch(InvalidAction& e) {
|
||||
logPrintf(WARN, "Line %d: %s is not a valid action, ignoring.",
|
||||
setting["action"].getSourceLine(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<uint8_t, std::shared_ptr<Action>>& RemapButton::Config::buttons()
|
||||
{
|
||||
return _buttons;
|
||||
}
|
||||
|
||||
|
||||
RemapButton::ButtonIPC::ButtonIPC(
|
||||
RemapButton *parent,
|
||||
backend::hidpp20::ReprogControls::ControlInfo info) :
|
||||
@ -225,9 +185,11 @@ RemapButton::ButtonIPC::ButtonIPC(
|
||||
{"ControlID", ipcgull::property<uint16_t>(
|
||||
ipcgull::property_readable, info.controlID)},
|
||||
{"Remappable", ipcgull::property<bool>(
|
||||
ipcgull::property_readable, info.flags & hidpp20::ReprogControls::TemporaryDivertable)},
|
||||
ipcgull::property_readable,
|
||||
info.flags & hidpp20::ReprogControls::TemporaryDivertable)},
|
||||
{"GestureSupport", ipcgull::property<bool>(
|
||||
ipcgull::property_readable, (info.additionalFlags & hidpp20::ReprogControls::RawXY)
|
||||
ipcgull::property_readable,
|
||||
(info.additionalFlags & hidpp20::ReprogControls::RawXY)
|
||||
)}
|
||||
}, {})
|
||||
{
|
||||
|
@ -33,16 +33,6 @@ namespace features
|
||||
virtual void configure();
|
||||
virtual void listen();
|
||||
|
||||
class Config : public DeviceFeature::Config
|
||||
{
|
||||
public:
|
||||
explicit Config(Device* dev);
|
||||
const std::map<uint8_t, std::shared_ptr<actions::Action>>&
|
||||
buttons();
|
||||
protected:
|
||||
void _parseButton(libconfig::Setting& setting);
|
||||
std::map<uint8_t, std::shared_ptr<actions::Action>> _buttons;
|
||||
};
|
||||
private:
|
||||
class ButtonIPC : public ipcgull::interface
|
||||
{
|
||||
@ -52,11 +42,13 @@ namespace features
|
||||
};
|
||||
|
||||
void _buttonEvent(const std::set<uint16_t>& new_state);
|
||||
Config _config;
|
||||
std::shared_ptr<backend::hidpp20::ReprogControls> _reprog_controls;
|
||||
std::set<uint16_t> _pressed_buttons;
|
||||
std::mutex _button_lock;
|
||||
|
||||
std::optional<config::RemapButton>& _config;
|
||||
std::map<uint16_t, std::shared_ptr<actions::Action>> _buttons;
|
||||
|
||||
std::shared_ptr<ipcgull::node> _ipc_node;
|
||||
typedef std::pair<std::shared_ptr<ipcgull::node>,
|
||||
std::shared_ptr<ButtonIPC>> ButtonIPCPair;
|
||||
|
@ -22,8 +22,8 @@
|
||||
using namespace logid::features;
|
||||
using namespace logid::backend;
|
||||
|
||||
SmartShift::SmartShift(Device* device) : DeviceFeature(device), _config
|
||||
(device)
|
||||
SmartShift::SmartShift(Device* device) : DeviceFeature(device),
|
||||
_config (device->activeProfile().smartshift)
|
||||
{
|
||||
try {
|
||||
_smartshift = std::make_shared<hidpp20::SmartShift>(&device->hidpp20());
|
||||
@ -34,7 +34,18 @@ SmartShift::SmartShift(Device* device) : DeviceFeature(device), _config
|
||||
|
||||
void SmartShift::configure()
|
||||
{
|
||||
_smartshift->setStatus(_config.getSettings());
|
||||
if(_config.has_value()) {
|
||||
const auto& conf = _config.value();
|
||||
hidpp20::SmartShift::SmartshiftStatus settings {};
|
||||
settings.setActive = conf.on.has_value();
|
||||
if(settings.setActive)
|
||||
settings.active = conf.on.value();
|
||||
settings.setAutoDisengage = conf.threshold.has_value();
|
||||
if(settings.setAutoDisengage)
|
||||
settings.autoDisengage = conf.threshold.value();
|
||||
|
||||
_smartshift->setStatus(settings);
|
||||
}
|
||||
}
|
||||
|
||||
void SmartShift::listen()
|
||||
@ -51,31 +62,3 @@ void SmartShift::setStatus(backend::hidpp20::SmartShift::SmartshiftStatus
|
||||
{
|
||||
_smartshift->setStatus(status);
|
||||
}
|
||||
|
||||
SmartShift::Config::Config(Device *dev) : DeviceFeature::Config(dev), _status()
|
||||
{
|
||||
try {
|
||||
auto& config_root = dev->config().getSetting("smartshift");
|
||||
if(!config_root.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: smartshift must be an object",
|
||||
config_root.getSourceLine());
|
||||
return;
|
||||
}
|
||||
_status.setActive = config_root.lookupValue("on", _status.active);
|
||||
int tmp;
|
||||
_status.setAutoDisengage = config_root.lookupValue("threshold", tmp);
|
||||
if(_status.setAutoDisengage)
|
||||
_status.autoDisengage = tmp;
|
||||
_status.setDefaultAutoDisengage = config_root.lookupValue
|
||||
("default_threshold", tmp);
|
||||
if(_status.setDefaultAutoDisengage)
|
||||
_status.defaultAutoDisengage = tmp;
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// SmartShift not configured, use default
|
||||
}
|
||||
}
|
||||
|
||||
hidpp20::SmartShift::SmartshiftStatus SmartShift::Config::getSettings()
|
||||
{
|
||||
return _status;
|
||||
}
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "../backend/hidpp20/features/SmartShift.h"
|
||||
#include "DeviceFeature.h"
|
||||
#include "../config/schema.h"
|
||||
|
||||
namespace logid {
|
||||
namespace features
|
||||
@ -34,16 +35,8 @@ namespace features
|
||||
backend::hidpp20::SmartShift::SmartshiftStatus getStatus();
|
||||
void setStatus(backend::hidpp20::SmartShift::SmartshiftStatus status);
|
||||
|
||||
class Config : public DeviceFeature::Config
|
||||
{
|
||||
public:
|
||||
explicit Config(Device* dev);
|
||||
backend::hidpp20::SmartShift::SmartshiftStatus getSettings();
|
||||
protected:
|
||||
backend::hidpp20::SmartShift::SmartshiftStatus _status;
|
||||
};
|
||||
private:
|
||||
Config _config;
|
||||
std::optional<config::SmartShift>& _config;
|
||||
std::shared_ptr<backend::hidpp20::SmartShift> _smartshift;
|
||||
};
|
||||
}}
|
||||
|
@ -29,9 +29,46 @@ using namespace logid;
|
||||
|
||||
#define SCROLL_EVENTHANDLER_NAME "THUMB_WHEEL"
|
||||
|
||||
ThumbWheel::ThumbWheel(Device *dev) : DeviceFeature(dev), _wheel_info(),
|
||||
_config(dev)
|
||||
std::shared_ptr<actions::Action> _genAction(
|
||||
Device* dev, std::optional<config::BasicAction>& conf)
|
||||
{
|
||||
if(conf.has_value()) {
|
||||
try {
|
||||
return actions::Action::makeAction(dev, conf.value());
|
||||
} catch(actions::InvalidAction& e) {
|
||||
logPrintf(WARN, "Mapping thumb wheel to invalid action");
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<actions::Gesture> _genGesture(
|
||||
Device* dev, std::optional<config::Gesture>& conf)
|
||||
{
|
||||
if(conf.has_value()) {
|
||||
try {
|
||||
return actions::Gesture::makeGesture(dev, conf.value());
|
||||
} catch (actions::InvalidAction &e) {
|
||||
logPrintf(WARN, "Mapping thumb wheel to invalid gesture");
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ThumbWheel::ThumbWheel(Device *dev) : DeviceFeature(dev), _wheel_info(),
|
||||
_config (dev->activeProfile().thumbwheel)
|
||||
{
|
||||
if(_config.has_value()) {
|
||||
auto& conf = _config.value();
|
||||
_left_action = _genGesture(dev, conf.left);
|
||||
_right_action = _genGesture(dev, conf.right);
|
||||
_touch_action = _genAction(dev, conf.touch);
|
||||
_tap_action = _genAction(dev, conf.tap);
|
||||
_proxy_action = _genAction(dev, conf.proxy);
|
||||
}
|
||||
|
||||
try {
|
||||
_thumb_wheel = std::make_shared<hidpp20::ThumbWheel>(&dev->hidpp20());
|
||||
} catch(hidpp20::UnsupportedFeature& e) {
|
||||
@ -47,33 +84,41 @@ ThumbWheel::ThumbWheel(Device *dev) : DeviceFeature(dev), _wheel_info(),
|
||||
logPrintf(DEBUG, "Thumb wheel resolution: native (%d), diverted (%d)",
|
||||
_wheel_info.nativeRes, _wheel_info.divertedRes);
|
||||
|
||||
if(_config.leftAction()) {
|
||||
if(_left_action) {
|
||||
try {
|
||||
auto left_axis = std::dynamic_pointer_cast<actions::AxisGesture>(
|
||||
_config.leftAction());
|
||||
_left_action);
|
||||
// TODO: How do hires multipliers work on 0x2150 thumbwheels?
|
||||
if(left_axis)
|
||||
left_axis->setHiresMultiplier(_wheel_info.divertedRes);
|
||||
} catch(std::bad_cast& e) { }
|
||||
|
||||
_config.leftAction()->press(true);
|
||||
_left_action->press(true);
|
||||
}
|
||||
|
||||
if(_config.rightAction()) {
|
||||
if(_right_action) {
|
||||
try {
|
||||
auto right_axis = std::dynamic_pointer_cast<actions::AxisGesture>(
|
||||
_config.rightAction());
|
||||
_right_action);
|
||||
if(right_axis)
|
||||
right_axis->setHiresMultiplier(_wheel_info.divertedRes);
|
||||
} catch(std::bad_cast& e) { }
|
||||
|
||||
_config.rightAction()->press(true);
|
||||
_right_action->press(true);
|
||||
}
|
||||
}
|
||||
|
||||
ThumbWheel::~ThumbWheel() {
|
||||
_device->hidpp20().removeEventHandler(SCROLL_EVENTHANDLER_NAME);
|
||||
}
|
||||
|
||||
void ThumbWheel::configure()
|
||||
{
|
||||
_thumb_wheel->setStatus(_config.divert(), _config.invert());
|
||||
if(_config.has_value()) {
|
||||
const auto& config = _config.value();
|
||||
_thumb_wheel->setStatus(config.divert.value_or(false),
|
||||
config.invert.value_or(false));
|
||||
}
|
||||
}
|
||||
|
||||
void ThumbWheel::listen()
|
||||
@ -98,7 +143,7 @@ void ThumbWheel::listen()
|
||||
void ThumbWheel::_handleEvent(hidpp20::ThumbWheel::ThumbwheelEvent event)
|
||||
{
|
||||
if(event.flags & hidpp20::ThumbWheel::SingleTap) {
|
||||
auto action = _config.tapAction();
|
||||
auto action = _tap_action;
|
||||
if(action) {
|
||||
action->press();
|
||||
action->release();
|
||||
@ -107,23 +152,21 @@ void ThumbWheel::_handleEvent(hidpp20::ThumbWheel::ThumbwheelEvent event)
|
||||
|
||||
if((bool)(event.flags & hidpp20::ThumbWheel::Proxy) != _last_proxy) {
|
||||
_last_proxy = !_last_proxy;
|
||||
auto action = _config.proxyAction();
|
||||
if(action) {
|
||||
if(_proxy_action) {
|
||||
if(_last_proxy)
|
||||
action->press();
|
||||
_proxy_action->press();
|
||||
else
|
||||
action->release();
|
||||
_proxy_action->release();
|
||||
}
|
||||
}
|
||||
|
||||
if((bool)(event.flags & hidpp20::ThumbWheel::Touch) != _last_touch) {
|
||||
_last_touch = !_last_touch;
|
||||
auto action = _config.touchAction();
|
||||
if(action) {
|
||||
if(_touch_action) {
|
||||
if(_last_touch)
|
||||
action->press();
|
||||
_touch_action->press();
|
||||
else
|
||||
action->release();
|
||||
_touch_action->release();
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,10 +175,10 @@ void ThumbWheel::_handleEvent(hidpp20::ThumbWheel::ThumbwheelEvent event)
|
||||
event.rotation *= _wheel_info.defaultDirection;
|
||||
|
||||
if(event.rotationStatus == hidpp20::ThumbWheel::Start) {
|
||||
if(_config.rightAction())
|
||||
_config.rightAction()->press(true);
|
||||
if(_config.leftAction())
|
||||
_config.leftAction()->press(true);
|
||||
if(_right_action)
|
||||
_right_action->press(true);
|
||||
if(_left_action)
|
||||
_left_action->press(true);
|
||||
_last_direction = 0;
|
||||
}
|
||||
|
||||
@ -144,9 +187,9 @@ void ThumbWheel::_handleEvent(hidpp20::ThumbWheel::ThumbwheelEvent event)
|
||||
std::shared_ptr<actions::Gesture> scroll_action;
|
||||
|
||||
if(direction > 0)
|
||||
scroll_action = _config.rightAction();
|
||||
scroll_action = _right_action;
|
||||
else
|
||||
scroll_action = _config.leftAction();
|
||||
scroll_action = _left_action;
|
||||
|
||||
if(scroll_action) {
|
||||
scroll_action->press(true);
|
||||
@ -157,136 +200,10 @@ void ThumbWheel::_handleEvent(hidpp20::ThumbWheel::ThumbwheelEvent event)
|
||||
}
|
||||
|
||||
if(event.rotationStatus == hidpp20::ThumbWheel::Stop) {
|
||||
if(_config.rightAction())
|
||||
_config.rightAction()->release();
|
||||
if(_config.leftAction())
|
||||
_config.leftAction()->release();
|
||||
if(_right_action)
|
||||
_right_action->release();
|
||||
if(_left_action)
|
||||
_left_action->release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThumbWheel::Config::Config(Device* dev) : DeviceFeature::Config(dev)
|
||||
{
|
||||
try {
|
||||
auto& config_root = dev->config().getSetting("thumbwheel");
|
||||
if(!config_root.isGroup()) {
|
||||
logPrintf(WARN, "Line %d: thumbwheel must be a group",
|
||||
config_root.getSourceLine());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto& divert = config_root.lookup("divert");
|
||||
if(divert.getType() == libconfig::Setting::TypeBoolean) {
|
||||
_divert = divert;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: divert must be a boolean",
|
||||
divert.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) { }
|
||||
|
||||
try {
|
||||
auto& invert = config_root.lookup("invert");
|
||||
if(invert.getType() == libconfig::Setting::TypeBoolean) {
|
||||
_invert = invert;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: invert must be a boolean, ignoring.",
|
||||
invert.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) { }
|
||||
|
||||
if(_divert) {
|
||||
_left_action = _genGesture(dev, config_root, "left");
|
||||
if(!_left_action)
|
||||
logPrintf(WARN, "Line %d: divert is true but no left action "
|
||||
"was set", config_root.getSourceLine());
|
||||
|
||||
_right_action = _genGesture(dev, config_root, "right");
|
||||
if(!_right_action)
|
||||
logPrintf(WARN, "Line %d: divert is true but no right action "
|
||||
"was set", config_root.getSourceLine());
|
||||
}
|
||||
|
||||
_proxy_action = _genAction(dev, config_root, "proxy");
|
||||
_tap_action = _genAction(dev, config_root, "tap");
|
||||
_touch_action = _genAction(dev, config_root, "touch");
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
// ThumbWheel not configured, use default
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<actions::Action> ThumbWheel::Config::_genAction(Device* dev,
|
||||
libconfig::Setting& config_root, const std::string& name)
|
||||
{
|
||||
try {
|
||||
auto& a_group = config_root.lookup(name);
|
||||
try {
|
||||
return actions::Action::makeAction(dev, a_group);
|
||||
} catch(actions::InvalidAction& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid action",
|
||||
a_group.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<actions::Gesture> ThumbWheel::Config::_genGesture(Device* dev,
|
||||
libconfig::Setting& config_root, const std::string& name)
|
||||
{
|
||||
try {
|
||||
auto& g_group = config_root.lookup(name);
|
||||
try {
|
||||
auto g = actions::Gesture::makeGesture(dev, g_group);
|
||||
if(g->wheelCompatibility()) {
|
||||
return g;
|
||||
} else {
|
||||
logPrintf(WARN, "Line %d: This gesture cannot be used"
|
||||
" as a scroll action.",
|
||||
g_group.getSourceLine());
|
||||
}
|
||||
} catch(actions::InvalidGesture& e) {
|
||||
logPrintf(WARN, "Line %d: Invalid scroll action",
|
||||
g_group.getSourceLine());
|
||||
}
|
||||
} catch(libconfig::SettingNotFoundException& e) {
|
||||
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ThumbWheel::Config::divert() const
|
||||
{
|
||||
return _divert;
|
||||
}
|
||||
|
||||
bool ThumbWheel::Config::invert() const
|
||||
{
|
||||
return _invert;
|
||||
}
|
||||
|
||||
const std::shared_ptr<actions::Gesture>& ThumbWheel::Config::leftAction() const
|
||||
{
|
||||
return _left_action;
|
||||
}
|
||||
|
||||
const std::shared_ptr<actions::Gesture>& ThumbWheel::Config::rightAction() const
|
||||
{
|
||||
return _right_action;
|
||||
}
|
||||
|
||||
const std::shared_ptr<actions::Action>& ThumbWheel::Config::proxyAction() const
|
||||
{
|
||||
return _proxy_action;
|
||||
}
|
||||
|
||||
const std::shared_ptr<actions::Action>& ThumbWheel::Config::tapAction() const
|
||||
{
|
||||
return _tap_action;
|
||||
}
|
||||
|
||||
const std::shared_ptr<actions::Action>& ThumbWheel::Config::touchAction() const
|
||||
{
|
||||
return _touch_action;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "../backend/hidpp20/features/ThumbWheel.h"
|
||||
#include "DeviceFeature.h"
|
||||
#include "../actions/gesture/Gesture.h"
|
||||
#include "../config/schema.h"
|
||||
|
||||
namespace logid {
|
||||
namespace features
|
||||
@ -29,45 +30,26 @@ namespace features
|
||||
{
|
||||
public:
|
||||
explicit ThumbWheel(Device* dev);
|
||||
~ThumbWheel();
|
||||
virtual void configure();
|
||||
virtual void listen();
|
||||
|
||||
class Config : public DeviceFeature::Config
|
||||
{
|
||||
public:
|
||||
explicit Config(Device* dev);
|
||||
bool divert() const;
|
||||
bool invert() const;
|
||||
|
||||
const std::shared_ptr<actions::Gesture>& leftAction() const;
|
||||
const std::shared_ptr<actions::Gesture>& rightAction() const;
|
||||
const std::shared_ptr<actions::Action>& proxyAction() const;
|
||||
const std::shared_ptr<actions::Action>& tapAction() const;
|
||||
const std::shared_ptr<actions::Action>& touchAction() const;
|
||||
protected:
|
||||
bool _divert = false;
|
||||
bool _invert = false;
|
||||
|
||||
static std::shared_ptr<actions::Gesture> _genGesture(Device* dev,
|
||||
libconfig::Setting& setting, const std::string& name);
|
||||
static std::shared_ptr<actions::Action> _genAction(Device* dev,
|
||||
libconfig::Setting& setting, const std::string& name);
|
||||
|
||||
std::shared_ptr<actions::Gesture> _left_action;
|
||||
std::shared_ptr<actions::Gesture> _right_action;
|
||||
std::shared_ptr<actions::Action> _proxy_action;
|
||||
std::shared_ptr<actions::Action> _tap_action;
|
||||
std::shared_ptr<actions::Action> _touch_action;
|
||||
};
|
||||
private:
|
||||
void _handleEvent(backend::hidpp20::ThumbWheel::ThumbwheelEvent event);
|
||||
|
||||
std::shared_ptr<backend::hidpp20::ThumbWheel> _thumb_wheel;
|
||||
backend::hidpp20::ThumbWheel::ThumbwheelInfo _wheel_info;
|
||||
|
||||
std::shared_ptr<actions::Gesture> _left_action;
|
||||
std::shared_ptr<actions::Gesture> _right_action;
|
||||
std::shared_ptr<actions::Action> _proxy_action;
|
||||
std::shared_ptr<actions::Action> _tap_action;
|
||||
std::shared_ptr<actions::Action> _touch_action;
|
||||
|
||||
int8_t _last_direction = 0;
|
||||
bool _last_proxy = false;
|
||||
bool _last_touch = false;
|
||||
Config _config;
|
||||
std::optional<config::ThumbWheel>& _config;
|
||||
};
|
||||
}}
|
||||
|
||||
|
@ -26,7 +26,7 @@ template<typename data>
|
||||
class mutex_queue
|
||||
{
|
||||
public:
|
||||
mutex_queue<data>() = default;
|
||||
mutex_queue() = default;
|
||||
bool empty()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
|
Loading…
Reference in New Issue
Block a user