Merge pull request #292 from PixlOne/config_rework

Rework config interface
This commit is contained in:
pixl 2022-01-20 01:33:09 -05:00 committed by GitHub
commit 6614702929
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1654 additions and 1639 deletions

@ -1 +1 @@
Subproject commit ac4cd8f52eb6d632e885c2b1ed3eb4b310897800
Subproject commit 22559236d8c6e30aac640f79b8d4983467ba5cc8

View File

@ -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")

View File

@ -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;
}
}

View File

@ -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;
};

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;
};
}}

View File

@ -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;
}
}

View File

@ -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;
};
}}

View File

@ -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;
}

View File

@ -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;
};
}}

View File

@ -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;
}

View File

@ -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;
};
}}

View File

@ -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;
}

View File

@ -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;
};
}}

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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;
}

View File

@ -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;
};
}}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
};
}}

View File

@ -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);
}

View File

@ -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;
};
}}

View File

@ -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);
}

View File

@ -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;
};
}}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();

View File

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

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

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

View File

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

View File

@ -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];
}

View File

@ -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;
};

View File

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

View File

@ -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();

View File

@ -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;
}

View File

@ -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;
};
}}

View File

@ -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)
)}
}, {})
{

View File

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

View File

@ -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;
}

View File

@ -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;
};
}}

View File

@ -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;
}

View File

@ -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;
};
}}

View File

@ -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);