Implement RemapButton feature
Many changes were made here, too many to list here.
This commit is contained in:
parent
0b87d3c664
commit
5bf5dc75b5
|
@ -11,12 +11,16 @@ find_package(PkgConfig REQUIRED)
|
||||||
add_executable(logid
|
add_executable(logid
|
||||||
logid.cpp
|
logid.cpp
|
||||||
util/log.cpp
|
util/log.cpp
|
||||||
|
InputDevice.cpp
|
||||||
DeviceManager.cpp
|
DeviceManager.cpp
|
||||||
Device.cpp
|
Device.cpp
|
||||||
Receiver.cpp
|
Receiver.cpp
|
||||||
Configuration.cpp
|
Configuration.cpp
|
||||||
features/DPI.cpp
|
features/DPI.cpp
|
||||||
features/SmartShift.cpp
|
features/SmartShift.cpp
|
||||||
|
features/RemapButton.cpp
|
||||||
|
actions/Action.cpp
|
||||||
|
actions/KeypressAction.cpp
|
||||||
backend/Error.cpp
|
backend/Error.cpp
|
||||||
backend/raw/DeviceMonitor.cpp
|
backend/raw/DeviceMonitor.cpp
|
||||||
backend/raw/RawDevice.cpp
|
backend/raw/RawDevice.cpp
|
||||||
|
|
|
@ -86,7 +86,7 @@ Configuration::DeviceNotFound::DeviceNotFound(std::string name) :
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * Configuration::DeviceNotFound::what()
|
const char * Configuration::DeviceNotFound::what() const noexcept
|
||||||
{
|
{
|
||||||
return _name.c_str();
|
return _name.c_str();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace logid
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit DeviceNotFound(std::string name);
|
explicit DeviceNotFound(std::string name);
|
||||||
virtual const char* what();
|
const char* what() const noexcept override;
|
||||||
private:
|
private:
|
||||||
std::string _name;
|
std::string _name;
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "features/DPI.h"
|
#include "features/DPI.h"
|
||||||
#include "Device.h"
|
#include "Device.h"
|
||||||
#include "features/SmartShift.h"
|
#include "features/SmartShift.h"
|
||||||
|
#include "features/RemapButton.h"
|
||||||
|
|
||||||
using namespace logid;
|
using namespace logid;
|
||||||
using namespace logid::backend;
|
using namespace logid::backend;
|
||||||
|
@ -46,9 +47,14 @@ void Device::_init()
|
||||||
|
|
||||||
_addFeature<features::DPI>("dpi");
|
_addFeature<features::DPI>("dpi");
|
||||||
_addFeature<features::SmartShift>("smartshift");
|
_addFeature<features::SmartShift>("smartshift");
|
||||||
|
_addFeature<features::RemapButton>("remapbutton");
|
||||||
|
|
||||||
for(auto& feature: _features)
|
for(auto& feature: _features) {
|
||||||
feature.second->configure();
|
feature.second->configure();
|
||||||
|
feature.second->listen();
|
||||||
|
}
|
||||||
|
|
||||||
|
_hidpp20.listen();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Device::name()
|
std::string Device::name()
|
||||||
|
@ -69,6 +75,9 @@ void Device::sleep()
|
||||||
void Device::wakeup()
|
void Device::wakeup()
|
||||||
{
|
{
|
||||||
logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index);
|
logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
for(auto& feature: _features)
|
||||||
|
feature.second->configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceConfig& Device::config()
|
DeviceConfig& Device::config()
|
||||||
|
|
|
@ -16,19 +16,35 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <system_error>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "InputDevice.h"
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
#include <libevdev/libevdev.h>
|
#include <libevdev/libevdev.h>
|
||||||
#include <libevdev/libevdev-uinput.h>
|
#include <libevdev/libevdev-uinput.h>
|
||||||
#include <system_error>
|
};
|
||||||
|
|
||||||
#include "EvdevDevice.h"
|
|
||||||
|
|
||||||
using namespace logid;
|
using namespace logid;
|
||||||
|
|
||||||
EvdevDevice::EvdevDevice(const char* name)
|
InputDevice::InvalidEventCode::InvalidEventCode(std::string name)
|
||||||
|
{
|
||||||
|
_what = "Invalid event code " + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* InputDevice::InvalidEventCode::what() const noexcept
|
||||||
|
{
|
||||||
|
return _what.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputDevice::InputDevice(const char* name)
|
||||||
{
|
{
|
||||||
device = libevdev_new();
|
device = libevdev_new();
|
||||||
libevdev_set_name(device, name);
|
libevdev_set_name(device, name);
|
||||||
|
|
||||||
|
///TODO: Is it really a good idea to enable all events?
|
||||||
libevdev_enable_event_type(device, EV_KEY);
|
libevdev_enable_event_type(device, EV_KEY);
|
||||||
for(int i = 0; i < KEY_CNT; i++)
|
for(int i = 0; i < KEY_CNT; i++)
|
||||||
libevdev_enable_event_code(device, EV_KEY, i, nullptr);
|
libevdev_enable_event_code(device, EV_KEY, i, nullptr);
|
||||||
|
@ -36,26 +52,56 @@ EvdevDevice::EvdevDevice(const char* name)
|
||||||
for(int i = 0; i < REL_CNT; i++)
|
for(int i = 0; i < REL_CNT; i++)
|
||||||
libevdev_enable_event_code(device, EV_REL, i, nullptr);
|
libevdev_enable_event_code(device, EV_REL, i, nullptr);
|
||||||
|
|
||||||
int err = libevdev_uinput_create_from_device(device, LIBEVDEV_UINPUT_OPEN_MANAGED, &ui_device);
|
int err = libevdev_uinput_create_from_device(device,
|
||||||
|
LIBEVDEV_UINPUT_OPEN_MANAGED, &ui_device);
|
||||||
|
|
||||||
if(err != 0)
|
if(err != 0)
|
||||||
throw std::system_error(-err, std::generic_category());
|
throw std::system_error(-err, std::generic_category());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvdevDevice::moveAxis(unsigned int axis, int movement)
|
InputDevice::~InputDevice()
|
||||||
{
|
|
||||||
libevdev_uinput_write_event(ui_device, EV_REL, axis, movement);
|
|
||||||
libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EvdevDevice::sendEvent(unsigned int type, unsigned int code, int value)
|
|
||||||
{
|
|
||||||
libevdev_uinput_write_event(ui_device, type, code, value);
|
|
||||||
libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
EvdevDevice::~EvdevDevice()
|
|
||||||
{
|
{
|
||||||
libevdev_uinput_destroy(ui_device);
|
libevdev_uinput_destroy(ui_device);
|
||||||
libevdev_free(device);
|
libevdev_free(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputDevice::moveAxis(uint axis, int movement)
|
||||||
|
{
|
||||||
|
_sendEvent(EV_REL, axis, movement);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputDevice::pressKey(uint code)
|
||||||
|
{
|
||||||
|
_sendEvent(EV_KEY, code, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputDevice::releaseKey(uint code)
|
||||||
|
{
|
||||||
|
_sendEvent(EV_KEY, code, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint InputDevice::toKeyCode(std::string name)
|
||||||
|
{
|
||||||
|
return _toEventCode(EV_KEY, std::move(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint InputDevice::toAxisCode(std::string name)
|
||||||
|
{
|
||||||
|
return _toEventCode(EV_KEY, std::move(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint InputDevice::_toEventCode(uint type, const std::string& name)
|
||||||
|
{
|
||||||
|
int code = libevdev_event_code_from_name(type, name.c_str());
|
||||||
|
|
||||||
|
if(code == -1)
|
||||||
|
throw InvalidEventCode(name);
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputDevice::_sendEvent(uint type, uint code, int value)
|
||||||
|
{
|
||||||
|
libevdev_uinput_write_event(ui_device, type, code, value);
|
||||||
|
libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0);
|
||||||
|
}
|
66
src/logid/InputDevice.h
Normal file
66
src/logid/InputDevice.h
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2020 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_INPUTDEVICE_H
|
||||||
|
#define LOGID_INPUTDEVICE_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include <libevdev/libevdev.h>
|
||||||
|
#include <libevdev/libevdev-uinput.h>
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace logid
|
||||||
|
{
|
||||||
|
typedef uint keycode;
|
||||||
|
|
||||||
|
class InputDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class InvalidEventCode : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit InvalidEventCode(std::string name);
|
||||||
|
const char* what() const noexcept override;
|
||||||
|
private:
|
||||||
|
std::string _what;
|
||||||
|
};
|
||||||
|
explicit InputDevice(const char *name);
|
||||||
|
~InputDevice();
|
||||||
|
|
||||||
|
void moveAxis(uint axis, int movement);
|
||||||
|
void pressKey(uint code);
|
||||||
|
void releaseKey(uint code);
|
||||||
|
|
||||||
|
static uint toKeyCode(std::string name);
|
||||||
|
static uint toAxisCode(std::string name);
|
||||||
|
private:
|
||||||
|
void _sendEvent(uint type, uint code, int value);
|
||||||
|
|
||||||
|
static uint _toEventCode(uint type, const std::string& name);
|
||||||
|
|
||||||
|
libevdev* device;
|
||||||
|
libevdev_uinput* ui_device;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern std::unique_ptr<InputDevice> virtual_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LOGID_INPUTDEVICE_H
|
58
src/logid/actions/Action.cpp
Normal file
58
src/logid/actions/Action.cpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2020 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include "Action.h"
|
||||||
|
#include "../util/log.h"
|
||||||
|
#include "KeypressAction.h"
|
||||||
|
|
||||||
|
using namespace logid;
|
||||||
|
using namespace logid::actions;
|
||||||
|
|
||||||
|
std::shared_ptr<Action> Action::makeAction(Device *device, libconfig::Setting
|
||||||
|
&setting)
|
||||||
|
{
|
||||||
|
if(!setting.isGroup()) {
|
||||||
|
logPrintf(WARN, "Line %d: Action is not a group, ignoring.",
|
||||||
|
setting.getSourceLine());
|
||||||
|
throw InvalidAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
throw InvalidAction(type);
|
||||||
|
|
||||||
|
} catch(libconfig::SettingNotFoundException& e) {
|
||||||
|
logPrintf(WARN, "Line %d: Action type is missing, ignoring.",
|
||||||
|
setting.getSourceLine());
|
||||||
|
throw InvalidAction();
|
||||||
|
}
|
||||||
|
}
|
82
src/logid/actions/Action.h
Normal file
82
src/logid/actions/Action.h
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2020 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_ACTION_H
|
||||||
|
#define LOGID_ACTION_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <libconfig.h++>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace logid {
|
||||||
|
class Device;
|
||||||
|
namespace actions {
|
||||||
|
class InvalidAction : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InvalidAction()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit InvalidAction(std::string& action) : _action (action)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
const char* what() const noexcept override
|
||||||
|
{
|
||||||
|
return _action.c_str();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string _action;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<Action> makeAction(Device* device,
|
||||||
|
libconfig::Setting& setting);
|
||||||
|
|
||||||
|
virtual void press() = 0;
|
||||||
|
virtual void release() = 0;
|
||||||
|
virtual void move(int16_t x, int16_t y)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool pressed()
|
||||||
|
{
|
||||||
|
return _pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual uint8_t reprogFlags() const = 0;
|
||||||
|
|
||||||
|
class Config
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Config(Device* device) : _device (device)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
Device* _device;
|
||||||
|
};
|
||||||
|
protected:
|
||||||
|
explicit Action(Device* device) : _device (device), _pressed (false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Device* _device;
|
||||||
|
std::atomic<bool> _pressed;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LOGID_ACTION_H
|
87
src/logid/actions/KeypressAction.cpp
Normal file
87
src/logid/actions/KeypressAction.cpp
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2020 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "KeypressAction.h"
|
||||||
|
#include "../util/log.h"
|
||||||
|
#include "../InputDevice.h"
|
||||||
|
#include "../backend/hidpp20/features/ReprogControls.h"
|
||||||
|
|
||||||
|
using namespace logid::actions;
|
||||||
|
using namespace logid::backend;
|
||||||
|
|
||||||
|
KeypressAction::KeypressAction(Device *device, libconfig::Setting& config) :
|
||||||
|
Action(device), _config (device, config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeypressAction::press()
|
||||||
|
{
|
||||||
|
for(auto& key : _config.keys())
|
||||||
|
virtual_input->pressKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeypressAction::release()
|
||||||
|
{
|
||||||
|
for(auto& key : _config.keys())
|
||||||
|
virtual_input->releaseKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
} else if(key.getType() == libconfig::Setting::TypeString) {
|
||||||
|
try {
|
||||||
|
_keys.push_back(virtual_input->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;
|
||||||
|
}
|
|
@ -15,31 +15,36 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#ifndef LOGID_ACTION_KEYPRESS_H
|
||||||
|
#define LOGID_ACTION_KEYPRESS_H
|
||||||
|
|
||||||
#ifndef LOGID_EVDEVDEVICE_H
|
#include <vector>
|
||||||
#define LOGID_EVDEVDEVICE_H
|
#include <libconfig.h++>
|
||||||
|
#include "Action.h"
|
||||||
|
|
||||||
#include <libevdev/libevdev.h>
|
namespace logid {
|
||||||
#include <libevdev/libevdev-uinput.h>
|
namespace actions {
|
||||||
|
class KeypressAction : public Action
|
||||||
namespace logid
|
|
||||||
{
|
|
||||||
class EvdevDevice
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EvdevDevice(const char *name);
|
KeypressAction(Device* dev, libconfig::Setting& config);
|
||||||
|
|
||||||
~EvdevDevice();
|
virtual void press();
|
||||||
|
virtual void release();
|
||||||
|
|
||||||
void moveAxis(unsigned int axis, int movement);
|
virtual uint8_t reprogFlags() const;
|
||||||
|
|
||||||
void sendEvent(unsigned int type, unsigned int code, int value);
|
class Config : public Action::Config
|
||||||
|
{
|
||||||
libevdev *device;
|
public:
|
||||||
libevdev_uinput *ui_device;
|
explicit Config(Device* device, libconfig::Setting& root);
|
||||||
|
std::vector<uint>& keys();
|
||||||
|
protected:
|
||||||
|
std::vector<uint> _keys;
|
||||||
};
|
};
|
||||||
|
protected:
|
||||||
|
Config _config;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
extern EvdevDevice* global_evdev;
|
#endif //LOGID_ACTION_KEYPRESS_H
|
||||||
}
|
|
||||||
|
|
||||||
#endif //LOGID_EVDEVDEVICE_H
|
|
|
@ -92,6 +92,7 @@ std::tuple<uint8_t, uint8_t> Device::version() const
|
||||||
|
|
||||||
void Device::_init()
|
void Device::_init()
|
||||||
{
|
{
|
||||||
|
_listening = false;
|
||||||
_supported_reports = getSupportedReports(_raw_device->reportDescriptor());
|
_supported_reports = getSupportedReports(_raw_device->reportDescriptor());
|
||||||
if(!_supported_reports)
|
if(!_supported_reports)
|
||||||
throw InvalidDevice(InvalidDevice::NoHIDPPReport);
|
throw InvalidDevice(InvalidDevice::NoHIDPPReport);
|
||||||
|
@ -127,6 +128,7 @@ void Device::_init()
|
||||||
|
|
||||||
Device::~Device()
|
Device::~Device()
|
||||||
{
|
{
|
||||||
|
if(_listening)
|
||||||
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
|
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +146,12 @@ void Device::removeEventHandler(const std::string& nickname)
|
||||||
_event_handlers.erase(nickname);
|
_event_handlers.erase(nickname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::map<std::string, std::shared_ptr<EventHandler>>&
|
||||||
|
Device::eventHandlers()
|
||||||
|
{
|
||||||
|
return _event_handlers;
|
||||||
|
}
|
||||||
|
|
||||||
void Device::handleEvent(Report& report)
|
void Device::handleEvent(Report& report)
|
||||||
{
|
{
|
||||||
for(auto& handler : _event_handlers)
|
for(auto& handler : _event_handlers)
|
||||||
|
@ -192,32 +200,34 @@ uint16_t Device::pid() const
|
||||||
void Device::listen()
|
void Device::listen()
|
||||||
{
|
{
|
||||||
if(!_raw_device->isListening())
|
if(!_raw_device->isListening())
|
||||||
///TODO: Kill RawDevice?
|
|
||||||
thread::spawn({[raw=this->_raw_device]() {
|
thread::spawn({[raw=this->_raw_device]() {
|
||||||
raw->listen();
|
raw->listen();
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// Pass all HID++ events with device index to this device.
|
// Pass all HID++ events with device index to this device.
|
||||||
std::shared_ptr<raw::RawEventHandler> handler;
|
auto handler = std::make_shared<raw::RawEventHandler>();
|
||||||
handler->condition = [this](std::vector<uint8_t>& report)->bool
|
handler->condition = [index=this->_index](std::vector<uint8_t>& report)
|
||||||
{
|
->bool {
|
||||||
return (report[Offset::Type] == Report::Type::Short ||
|
return (report[Offset::Type] == Report::Type::Short ||
|
||||||
report[Offset::Type] == Report::Type::Long) &&
|
report[Offset::Type] == Report::Type::Long) &&
|
||||||
(report[Offset::DeviceIndex] == this->_index);
|
(report[Offset::DeviceIndex] == index);
|
||||||
};
|
};
|
||||||
handler->callback = [this](std::vector<uint8_t>& report)->void
|
handler->callback = [this](std::vector<uint8_t>& report)->void {
|
||||||
{
|
|
||||||
Report _report(report);
|
Report _report(report);
|
||||||
this->handleEvent(_report);
|
this->handleEvent(_report);
|
||||||
};
|
};
|
||||||
|
|
||||||
_raw_device->addEventHandler("DEV_" + std::to_string(_index), handler);
|
_raw_device->addEventHandler("DEV_" + std::to_string(_index), handler);
|
||||||
|
_listening = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::stopListening()
|
void Device::stopListening()
|
||||||
{
|
{
|
||||||
|
if(_listening)
|
||||||
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
|
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
|
||||||
|
|
||||||
|
_listening = false;
|
||||||
|
|
||||||
if(!_raw_device->eventHandlers().empty())
|
if(!_raw_device->eventHandlers().empty())
|
||||||
_raw_device->stopListener();
|
_raw_device->stopListener();
|
||||||
}
|
}
|
|
@ -55,7 +55,7 @@ namespace hidpp
|
||||||
Asleep
|
Asleep
|
||||||
};
|
};
|
||||||
InvalidDevice(Reason reason) : _reason (reason) {}
|
InvalidDevice(Reason reason) : _reason (reason) {}
|
||||||
virtual const char *what() const noexcept;
|
virtual const char* what() const noexcept;
|
||||||
virtual Reason code() const noexcept;
|
virtual Reason code() const noexcept;
|
||||||
private:
|
private:
|
||||||
Reason _reason;
|
Reason _reason;
|
||||||
|
@ -81,6 +81,8 @@ namespace hidpp
|
||||||
void addEventHandler(const std::string& nickname,
|
void addEventHandler(const std::string& nickname,
|
||||||
const std::shared_ptr<EventHandler>& handler);
|
const std::shared_ptr<EventHandler>& handler);
|
||||||
void removeEventHandler(const std::string& nickname);
|
void removeEventHandler(const std::string& nickname);
|
||||||
|
const std::map<std::string, std::shared_ptr<EventHandler>>&
|
||||||
|
eventHandlers();
|
||||||
|
|
||||||
Report sendReport(Report& report);
|
Report sendReport(Report& report);
|
||||||
|
|
||||||
|
@ -98,6 +100,8 @@ namespace hidpp
|
||||||
uint16_t _pid;
|
uint16_t _pid;
|
||||||
std::string _name;
|
std::string _name;
|
||||||
|
|
||||||
|
std::atomic<bool> _listening;
|
||||||
|
|
||||||
std::map<std::string, std::shared_ptr<EventHandler>> _event_handlers;
|
std::map<std::string, std::shared_ptr<EventHandler>> _event_handlers;
|
||||||
};
|
};
|
||||||
} } }
|
} } }
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
#define LOGID_HIDPP_SOFTWARE_ID 0
|
#define LOGID_HIDPP_SOFTWARE_ID 0
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace logid {
|
namespace logid {
|
||||||
namespace backend {
|
namespace backend {
|
||||||
namespace hidpp
|
namespace hidpp
|
||||||
|
|
|
@ -63,3 +63,8 @@ Feature::Feature(Device* dev, uint16_t _id) : _device (dev)
|
||||||
throw UnsupportedFeature(_id);
|
throw UnsupportedFeature(_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t Feature::featureIndex()
|
||||||
|
{
|
||||||
|
return _index;
|
||||||
|
}
|
|
@ -40,7 +40,7 @@ namespace hidpp20 {
|
||||||
public:
|
public:
|
||||||
static const uint16_t ID;
|
static const uint16_t ID;
|
||||||
virtual uint16_t getID() = 0;
|
virtual uint16_t getID() = 0;
|
||||||
|
uint8_t featureIndex();
|
||||||
protected:
|
protected:
|
||||||
explicit Feature(Device* dev, uint16_t _id);
|
explicit Feature(Device* dev, uint16_t _id);
|
||||||
std::vector<uint8_t> callFunction(uint8_t function_id,
|
std::vector<uint8_t> callFunction(uint8_t function_id,
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include "../Error.h"
|
||||||
#include "ReprogControls.h"
|
#include "ReprogControls.h"
|
||||||
|
|
||||||
using namespace logid::backend::hidpp20;
|
using namespace logid::backend::hidpp20;
|
||||||
|
@ -30,7 +31,7 @@ x::x(Device* dev, uint16_t _id) : base(dev, _id) \
|
||||||
|
|
||||||
#define MAKE_REPROG(x, dev) \
|
#define MAKE_REPROG(x, dev) \
|
||||||
try { \
|
try { \
|
||||||
return x(dev); \
|
return std::make_shared<x>(dev); \
|
||||||
} catch(UnsupportedFeature &e) {\
|
} catch(UnsupportedFeature &e) {\
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +42,7 @@ DEFINE_REPROG(ReprogControlsV2_2, ReprogControlsV2);
|
||||||
DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2);
|
DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2);
|
||||||
DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3);
|
DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3);
|
||||||
|
|
||||||
ReprogControls ReprogControls::autoVersion(Device *dev)
|
std::shared_ptr<ReprogControls> ReprogControls::autoVersion(Device *dev)
|
||||||
{
|
{
|
||||||
MAKE_REPROG(ReprogControlsV4, dev);
|
MAKE_REPROG(ReprogControlsV4, dev);
|
||||||
MAKE_REPROG(ReprogControlsV3, dev);
|
MAKE_REPROG(ReprogControlsV3, dev);
|
||||||
|
@ -49,7 +50,7 @@ ReprogControls ReprogControls::autoVersion(Device *dev)
|
||||||
MAKE_REPROG(ReprogControlsV2, dev);
|
MAKE_REPROG(ReprogControlsV2, dev);
|
||||||
|
|
||||||
// If base version cannot be made, throw error
|
// If base version cannot be made, throw error
|
||||||
return ReprogControls(dev);
|
return std::make_shared<ReprogControls>(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t ReprogControls::getControlCount()
|
uint8_t ReprogControls::getControlCount()
|
||||||
|
@ -78,29 +79,43 @@ ReprogControls::ControlInfo ReprogControls::getControlInfo(uint8_t index)
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid)
|
||||||
|
{
|
||||||
|
if(_cids.empty()) {
|
||||||
|
for(uint8_t i = 0; i < getControlCount(); i++) {
|
||||||
|
auto info = getControlInfo(i);
|
||||||
|
_cids.emplace(info.controlID, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = _cids.find(cid);
|
||||||
|
if(it == _cids.end())
|
||||||
|
throw Error(Error::InvalidArgument);
|
||||||
|
else
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
ReprogControls::ControlInfo ReprogControls::getControlReporting(uint16_t cid)
|
ReprogControls::ControlInfo ReprogControls::getControlReporting(uint16_t cid)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> params(2);
|
// Emulate this function, only Reprog controls v4 supports this
|
||||||
ControlInfo info{};
|
auto info = getControlIdInfo(cid);
|
||||||
params[0] = (cid >> 8) & 0xff;
|
|
||||||
params[1] = cid & 0xff;
|
|
||||||
auto response = callFunction(GetControlReporting, params);
|
|
||||||
|
|
||||||
info.controlID = response[1];
|
ControlInfo report{};
|
||||||
info.controlID |= response[0] << 8;
|
report.controlID = cid;
|
||||||
info.flags = response[2];
|
report.flags = 0;
|
||||||
return info;
|
if(info.flags & TemporaryDivertable)
|
||||||
|
report.flags |= TemporaryDiverted;
|
||||||
|
if(info.flags & PersisentlyDivertable)
|
||||||
|
report.flags |= PersistentlyDiverted;
|
||||||
|
if(info.additionalFlags & RawXY)
|
||||||
|
report.flags |= RawXYDiverted;
|
||||||
|
|
||||||
|
return report;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReprogControls::setControlReporting(uint8_t cid, ControlInfo info)
|
void ReprogControls::setControlReporting(uint8_t cid, ControlInfo info)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> params(5);
|
// This function does not exist pre-v4 and cannot be emulated, ignore.
|
||||||
params[0] = (cid >> 8) & 0xff;
|
|
||||||
params[1] = cid & 0xff;
|
|
||||||
params[2] = info.flags;
|
|
||||||
params[3] = (info.controlID >> 8) & 0xff;
|
|
||||||
params[4] = info.controlID & 0xff;
|
|
||||||
callFunction(SetControlReporting, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<uint16_t> ReprogControls::divertedButtonEvent(
|
std::set<uint16_t> ReprogControls::divertedButtonEvent(
|
||||||
|
@ -131,3 +146,28 @@ ReprogControls::Move ReprogControls::divertedRawXYEvent(const hidpp::Report
|
||||||
move.y |= report.paramBegin()[2] << 8;
|
move.y |= report.paramBegin()[2] << 8;
|
||||||
return move;
|
return move;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReprogControls::ControlInfo ReprogControlsV4::getControlReporting(uint16_t cid)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> params(2);
|
||||||
|
ControlInfo info{};
|
||||||
|
params[0] = (cid >> 8) & 0xff;
|
||||||
|
params[1] = cid & 0xff;
|
||||||
|
auto response = callFunction(GetControlReporting, params);
|
||||||
|
|
||||||
|
info.controlID = response[1];
|
||||||
|
info.controlID |= response[0] << 8;
|
||||||
|
info.flags = response[2];
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReprogControlsV4::setControlReporting(uint8_t cid, ControlInfo info)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> params(5);
|
||||||
|
params[0] = (cid >> 8) & 0xff;
|
||||||
|
params[1] = cid & 0xff;
|
||||||
|
params[2] = info.flags;
|
||||||
|
params[3] = (info.controlID >> 8) & 0xff;
|
||||||
|
params[4] = info.controlID & 0xff;
|
||||||
|
callFunction(SetControlReporting, params);
|
||||||
|
}
|
|
@ -18,6 +18,8 @@
|
||||||
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
|
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
|
||||||
#define LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
|
#define LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "../feature_defs.h"
|
#include "../feature_defs.h"
|
||||||
#include "../Feature.h"
|
#include "../Feature.h"
|
||||||
|
|
||||||
|
@ -28,27 +30,17 @@ namespace hidpp20
|
||||||
class ReprogControls : public Feature
|
class ReprogControls : public Feature
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const uint16_t ID = FeatureID::REPROG_CONTROLS;
|
|
||||||
virtual uint16_t getID() { return ID; }
|
|
||||||
|
|
||||||
virtual bool supportsRawXY() { return false; }
|
|
||||||
|
|
||||||
enum Function {
|
enum Function {
|
||||||
GetControlCount = 0,
|
GetControlCount = 0,
|
||||||
GetControlInfo = 1,
|
GetControlInfo = 1,
|
||||||
GetControlReporting = 2,
|
GetControlReporting = 2,
|
||||||
SetControlReporting = 3
|
SetControlReporting = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Event {
|
enum Event {
|
||||||
DivertedButtonEvent = 0,
|
DivertedButtonEvent = 0,
|
||||||
DivertedRawXYEvent = 1
|
DivertedRawXYEvent = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit ReprogControls(Device* dev);
|
|
||||||
|
|
||||||
virtual uint8_t getControlCount();
|
|
||||||
|
|
||||||
struct ControlInfo
|
struct ControlInfo
|
||||||
{
|
{
|
||||||
uint16_t controlID;
|
uint16_t controlID;
|
||||||
|
@ -68,24 +60,41 @@ namespace hidpp20
|
||||||
FnToggle = 1<<3,
|
FnToggle = 1<<3,
|
||||||
ReprogHint = 1<<4,
|
ReprogHint = 1<<4,
|
||||||
TemporaryDivertable = 1<<5,
|
TemporaryDivertable = 1<<5,
|
||||||
PerisentlyDiverable = 1<<6,
|
PersisentlyDivertable = 1<<6,
|
||||||
Virtual = 1<<7
|
Virtual = 1<<7
|
||||||
};
|
};
|
||||||
enum ControlInfoAdditionalFlags: uint8_t {
|
enum ControlInfoAdditionalFlags: uint8_t {
|
||||||
RawXY = 1<<0
|
RawXY = 1<<0
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ControlInfo getControlInfo(uint8_t index);
|
|
||||||
|
|
||||||
enum ControlReportingFlags: uint8_t {
|
enum ControlReportingFlags: uint8_t {
|
||||||
TemporaryDiverted = 1<<0,
|
TemporaryDiverted = 1<<0,
|
||||||
ChangeTemporaryDivert = 1<<1,
|
ChangeTemporaryDivert = 1<<1,
|
||||||
PersistentDiverted = 1<<2,
|
PersistentlyDiverted = 1<<2,
|
||||||
ChangePersistentDivert = 1<<3,
|
ChangePersistentDivert = 1<<3,
|
||||||
RawXYDiverted = 1<<4,
|
RawXYDiverted = 1<<4,
|
||||||
ChangeRawXYDivert = 1<<5
|
ChangeRawXYDivert = 1<<5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Move
|
||||||
|
{
|
||||||
|
int16_t x;
|
||||||
|
int16_t y;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint16_t ID = FeatureID::REPROG_CONTROLS;
|
||||||
|
virtual uint16_t getID() { return ID; }
|
||||||
|
|
||||||
|
virtual bool supportsRawXY() { return false; }
|
||||||
|
|
||||||
|
explicit ReprogControls(Device* dev);
|
||||||
|
|
||||||
|
virtual uint8_t getControlCount();
|
||||||
|
|
||||||
|
virtual ControlInfo getControlInfo(uint8_t cid);
|
||||||
|
|
||||||
|
virtual ControlInfo getControlIdInfo(uint16_t cid);
|
||||||
|
|
||||||
// Onlu controlId and flags will be set
|
// Onlu controlId and flags will be set
|
||||||
virtual ControlInfo getControlReporting(uint16_t cid);
|
virtual ControlInfo getControlReporting(uint16_t cid);
|
||||||
|
|
||||||
|
@ -95,24 +104,19 @@ namespace hidpp20
|
||||||
static std::set<uint16_t> divertedButtonEvent(const hidpp::Report&
|
static std::set<uint16_t> divertedButtonEvent(const hidpp::Report&
|
||||||
report);
|
report);
|
||||||
|
|
||||||
struct Move
|
|
||||||
{
|
|
||||||
int16_t x;
|
|
||||||
int16_t y;
|
|
||||||
};
|
|
||||||
|
|
||||||
static Move divertedRawXYEvent(const hidpp::Report& report);
|
static Move divertedRawXYEvent(const hidpp::Report& report);
|
||||||
|
|
||||||
static ReprogControls autoVersion(Device *dev);
|
static std::shared_ptr<ReprogControls> autoVersion(Device *dev);
|
||||||
protected:
|
protected:
|
||||||
ReprogControls(Device* dev, uint16_t _id);
|
ReprogControls(Device* dev, uint16_t _id);
|
||||||
|
std::map<uint16_t, ControlInfo> _cids;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ReprogControlsV2 : public ReprogControls
|
class ReprogControlsV2 : public ReprogControls
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2;
|
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2;
|
||||||
uint16_t getID() override { return ID; }
|
virtual uint16_t getID() { return ID; }
|
||||||
|
|
||||||
explicit ReprogControlsV2(Device* dev);
|
explicit ReprogControlsV2(Device* dev);
|
||||||
protected:
|
protected:
|
||||||
|
@ -123,7 +127,7 @@ namespace hidpp20
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2_2;
|
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2_2;
|
||||||
uint16_t getID() override { return ID; }
|
virtual uint16_t getID() { return ID; }
|
||||||
|
|
||||||
explicit ReprogControlsV2_2(Device* dev);
|
explicit ReprogControlsV2_2(Device* dev);
|
||||||
protected:
|
protected:
|
||||||
|
@ -134,7 +138,7 @@ namespace hidpp20
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V3;
|
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V3;
|
||||||
uint16_t getID() override { return ID; }
|
virtual uint16_t getID() { return ID; }
|
||||||
|
|
||||||
explicit ReprogControlsV3(Device* dev);
|
explicit ReprogControlsV3(Device* dev);
|
||||||
protected:
|
protected:
|
||||||
|
@ -145,10 +149,14 @@ namespace hidpp20
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V4;
|
static const uint16_t ID = FeatureID::REPROG_CONTROLS_V4;
|
||||||
uint16_t getID() override { return ID; }
|
virtual uint16_t getID() { return ID; }
|
||||||
|
|
||||||
bool supportsRawXY() override { return true; }
|
bool supportsRawXY() override { return true; }
|
||||||
|
|
||||||
|
ControlInfo getControlReporting(uint16_t cid) override;
|
||||||
|
|
||||||
|
void setControlReporting(uint8_t cid, ControlInfo info) override;
|
||||||
|
|
||||||
explicit ReprogControlsV4(Device* dev);
|
explicit ReprogControlsV4(Device* dev);
|
||||||
protected:
|
protected:
|
||||||
ReprogControlsV4(Device* dev, uint16_t _id);
|
ReprogControlsV4(Device* dev, uint16_t _id);
|
||||||
|
|
|
@ -354,6 +354,7 @@ void RawDevice::addEventHandler(const std::string& nickname,
|
||||||
{
|
{
|
||||||
auto it = _event_handlers.find(nickname);
|
auto it = _event_handlers.find(nickname);
|
||||||
assert(it == _event_handlers.end());
|
assert(it == _event_handlers.end());
|
||||||
|
assert(handler);
|
||||||
_event_handlers.emplace(nickname, handler);
|
_event_handlers.emplace(nickname, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
180
src/logid/features/RemapButton.cpp
Normal file
180
src/logid/features/RemapButton.cpp
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2020 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include <sstream>
|
||||||
|
#include "../Device.h"
|
||||||
|
#include "RemapButton.h"
|
||||||
|
#include "../backend/hidpp20/Error.h"
|
||||||
|
|
||||||
|
using namespace logid::features;
|
||||||
|
using namespace logid::backend;
|
||||||
|
using namespace logid::actions;
|
||||||
|
|
||||||
|
#define HIDPP20_REPROG_REBIND (hidpp20::ReprogControls::ChangeTemporaryDivert \
|
||||||
|
| hidpp20::ReprogControls::ChangeRawXYDivert)
|
||||||
|
|
||||||
|
#define EVENTHANDLER_NAME "REMAP_BUTTON"
|
||||||
|
|
||||||
|
RemapButton::RemapButton(Device *dev): DeviceFeature(dev), _config (dev),
|
||||||
|
_reprog_controls (hidpp20::ReprogControls::autoVersion(&dev->hidpp20()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RemapButton::~RemapButton()
|
||||||
|
{
|
||||||
|
_device->hidpp20().removeEventHandler(EVENTHANDLER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemapButton::configure()
|
||||||
|
{
|
||||||
|
///TODO: DJ reporting trickery if cannot be remapped
|
||||||
|
for(auto& i : _config.buttons()) {
|
||||||
|
hidpp20::ReprogControls::ControlInfo info{};
|
||||||
|
try {
|
||||||
|
info = _reprog_controls->getControlIdInfo(i.first);
|
||||||
|
} catch(hidpp20::Error& e) {
|
||||||
|
if(e.code() == hidpp20::Error::InvalidArgument) {
|
||||||
|
logPrintf(WARN, "%s: CID 0x%02x does not exist.",
|
||||||
|
_device->name().c_str(), i.first);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((i.second->reprogFlags() & hidpp20::ReprogControls::RawXYDiverted) &&
|
||||||
|
(!_reprog_controls->supportsRawXY() || (info.additionalFlags &
|
||||||
|
hidpp20::ReprogControls::RawXY)))
|
||||||
|
logPrintf(WARN, "%s: Cannot divert raw XY movements for CID "
|
||||||
|
"0x%02x", _device->name().c_str(), i.first);
|
||||||
|
|
||||||
|
hidpp20::ReprogControls::ControlInfo report{};
|
||||||
|
report.controlID = i.first;
|
||||||
|
report.flags = HIDPP20_REPROG_REBIND;
|
||||||
|
report.flags |= i.second->reprogFlags();
|
||||||
|
_reprog_controls->setControlReporting(i.first, report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemapButton::listen()
|
||||||
|
{
|
||||||
|
if(_device->hidpp20().eventHandlers().find(EVENTHANDLER_NAME) ==
|
||||||
|
_device->hidpp20().eventHandlers().end()) {
|
||||||
|
auto handler = std::make_shared<hidpp::EventHandler>();
|
||||||
|
handler->condition = [index=_reprog_controls->featureIndex()]
|
||||||
|
(hidpp::Report& report)->bool {
|
||||||
|
return (report.feature() == index) && ((report.function() ==
|
||||||
|
hidpp20::ReprogControls::DivertedButtonEvent) || (report
|
||||||
|
.function() == hidpp20::ReprogControls::DivertedRawXYEvent));
|
||||||
|
};
|
||||||
|
|
||||||
|
handler->callback = [this](hidpp::Report& report)->void {
|
||||||
|
if(report.function() ==
|
||||||
|
hidpp20::ReprogControls::DivertedButtonEvent)
|
||||||
|
this->_buttonEvent(_reprog_controls->divertedButtonEvent(
|
||||||
|
report));
|
||||||
|
else { // RawXY
|
||||||
|
auto divertedXY = _reprog_controls->divertedRawXYEvent(report);
|
||||||
|
for(auto& button : this->_config.buttons())
|
||||||
|
if(button.second->pressed())
|
||||||
|
button.second->move(divertedXY.x, divertedXY.y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_device->hidpp20().addEventHandler(EVENTHANDLER_NAME, handler);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemapButton::_buttonEvent(std::set<uint16_t> new_state)
|
||||||
|
{
|
||||||
|
// Ensure I/O doesn't occur while updating button state
|
||||||
|
std::lock_guard<std::mutex> lock(_button_lock);
|
||||||
|
|
||||||
|
// Press all added buttons
|
||||||
|
for(auto& i : new_state) {
|
||||||
|
auto old_i = _pressed_buttons.find(i);
|
||||||
|
if(old_i != _pressed_buttons.end()) {
|
||||||
|
_pressed_buttons.erase(old_i);
|
||||||
|
} else {
|
||||||
|
auto action = _config.buttons().find(i);
|
||||||
|
if(action != _config.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())
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::map<uint8_t, std::shared_ptr<Action>>& RemapButton::Config::buttons()
|
||||||
|
{
|
||||||
|
return _buttons;
|
||||||
|
}
|
55
src/logid/features/RemapButton.h
Normal file
55
src/logid/features/RemapButton.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019-2020 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_FEATURE_REMAPBUTTON_H
|
||||||
|
#define LOGID_FEATURE_REMAPBUTTON_H
|
||||||
|
|
||||||
|
#include "../backend/hidpp20/features/ReprogControls.h"
|
||||||
|
#include "DeviceFeature.h"
|
||||||
|
#include "../actions/Action.h"
|
||||||
|
|
||||||
|
namespace logid {
|
||||||
|
namespace features
|
||||||
|
{
|
||||||
|
class RemapButton : public DeviceFeature
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit RemapButton(Device* dev);
|
||||||
|
~RemapButton();
|
||||||
|
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:
|
||||||
|
void _buttonEvent(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;
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif //LOGID_FEATURE_REMAPBUTTON_H
|
|
@ -24,8 +24,9 @@
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "DeviceManager.h"
|
#include "DeviceManager.h"
|
||||||
#include "logid.h"
|
#include "logid.h"
|
||||||
|
#include "InputDevice.h"
|
||||||
|
|
||||||
#define evdev_name "logid"
|
#define LOGID_VIRTUAL_INPUT_NAME "LogiOps Virtual Input"
|
||||||
#define DEFAULT_CONFIG_FILE "/etc/logid.cfg"
|
#define DEFAULT_CONFIG_FILE "/etc/logid.cfg"
|
||||||
|
|
||||||
#ifndef LOGIOPS_VERSION
|
#ifndef LOGIOPS_VERSION
|
||||||
|
@ -40,6 +41,7 @@ std::string config_file = DEFAULT_CONFIG_FILE;
|
||||||
LogLevel logid::global_loglevel = INFO;
|
LogLevel logid::global_loglevel = INFO;
|
||||||
std::shared_ptr<Configuration> logid::global_config;
|
std::shared_ptr<Configuration> logid::global_config;
|
||||||
std::unique_ptr<DeviceManager> logid::device_manager;
|
std::unique_ptr<DeviceManager> logid::device_manager;
|
||||||
|
std::unique_ptr<InputDevice> logid::virtual_input;
|
||||||
|
|
||||||
bool logid::kill_logid = false;
|
bool logid::kill_logid = false;
|
||||||
std::mutex logid::device_manager_reload;
|
std::mutex logid::device_manager_reload;
|
||||||
|
@ -163,17 +165,14 @@ int main(int argc, char** argv)
|
||||||
global_config = std::make_shared<Configuration>();
|
global_config = std::make_shared<Configuration>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
//Create a virtual input device
|
||||||
//Create an evdev device called 'logid'
|
try {
|
||||||
try { global_evdev = new EvdevDevice(evdev_name); }
|
virtual_input = std::make_unique<InputDevice>(LOGID_VIRTUAL_INPUT_NAME);
|
||||||
catch(std::system_error& e)
|
} catch(std::system_error& e) {
|
||||||
{
|
logPrintf(ERROR, "Could not create input device: %s", e.what());
|
||||||
log_printf(ERROR, "Could not create evdev device: %s", e.what());
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Scan devices, create listeners, handlers, etc.
|
// Scan devices, create listeners, handlers, etc.
|
||||||
device_manager = std::make_unique<DeviceManager>();
|
device_manager = std::make_unique<DeviceManager>();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user