Implement RemapButton feature

Many changes were made here, too many to list here.
This commit is contained in:
pixl 2020-07-05 02:55:46 -04:00
parent 0b87d3c664
commit 5bf5dc75b5
No known key found for this signature in database
GPG Key ID: 1866C148CD593B6E
21 changed files with 765 additions and 104 deletions

View File

@ -11,12 +11,16 @@ find_package(PkgConfig REQUIRED)
add_executable(logid
logid.cpp
util/log.cpp
InputDevice.cpp
DeviceManager.cpp
Device.cpp
Receiver.cpp
Configuration.cpp
features/DPI.cpp
features/SmartShift.cpp
features/RemapButton.cpp
actions/Action.cpp
actions/KeypressAction.cpp
backend/Error.cpp
backend/raw/DeviceMonitor.cpp
backend/raw/RawDevice.cpp

View File

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

View File

@ -37,7 +37,7 @@ namespace logid
{
public:
explicit DeviceNotFound(std::string name);
virtual const char* what();
const char* what() const noexcept override;
private:
std::string _name;
};

View File

@ -20,6 +20,7 @@
#include "features/DPI.h"
#include "Device.h"
#include "features/SmartShift.h"
#include "features/RemapButton.h"
using namespace logid;
using namespace logid::backend;
@ -46,9 +47,14 @@ void Device::_init()
_addFeature<features::DPI>("dpi");
_addFeature<features::SmartShift>("smartshift");
_addFeature<features::RemapButton>("remapbutton");
for(auto& feature: _features)
for(auto& feature: _features) {
feature.second->configure();
feature.second->listen();
}
_hidpp20.listen();
}
std::string Device::name()
@ -69,6 +75,9 @@ void Device::sleep()
void Device::wakeup()
{
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()

View File

@ -16,19 +16,35 @@
*
*/
#include <system_error>
#include <utility>
#include "InputDevice.h"
extern "C"
{
#include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
#include <system_error>
#include "EvdevDevice.h"
};
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();
libevdev_set_name(device, name);
///TODO: Is it really a good idea to enable all events?
libevdev_enable_event_type(device, EV_KEY);
for(int i = 0; i < KEY_CNT; i++)
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++)
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)
throw std::system_error(-err, std::generic_category());
}
void EvdevDevice::moveAxis(unsigned int axis, int movement)
{
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()
InputDevice::~InputDevice()
{
libevdev_uinput_destroy(ui_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
View 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

View 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();
}
}

View 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

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

View File

@ -15,31 +15,36 @@
* 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
#define LOGID_EVDEVDEVICE_H
#include <vector>
#include <libconfig.h++>
#include "Action.h"
#include <libevdev/libevdev.h>
#include <libevdev/libevdev-uinput.h>
namespace logid
{
class EvdevDevice
namespace logid {
namespace actions {
class KeypressAction : public Action
{
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);
libevdev *device;
libevdev_uinput *ui_device;
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;
};
}}
extern EvdevDevice* global_evdev;
}
#endif //LOGID_EVDEVDEVICE_H
#endif //LOGID_ACTION_KEYPRESS_H

View File

@ -92,6 +92,7 @@ std::tuple<uint8_t, uint8_t> Device::version() const
void Device::_init()
{
_listening = false;
_supported_reports = getSupportedReports(_raw_device->reportDescriptor());
if(!_supported_reports)
throw InvalidDevice(InvalidDevice::NoHIDPPReport);
@ -127,7 +128,8 @@ void Device::_init()
Device::~Device()
{
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
if(_listening)
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
}
void Device::addEventHandler(const std::string& nickname,
@ -144,6 +146,12 @@ void Device::removeEventHandler(const std::string& nickname)
_event_handlers.erase(nickname);
}
const std::map<std::string, std::shared_ptr<EventHandler>>&
Device::eventHandlers()
{
return _event_handlers;
}
void Device::handleEvent(Report& report)
{
for(auto& handler : _event_handlers)
@ -192,31 +200,33 @@ uint16_t Device::pid() const
void Device::listen()
{
if(!_raw_device->isListening())
///TODO: Kill RawDevice?
thread::spawn({[raw=this->_raw_device]() {
raw->listen();
}});
// Pass all HID++ events with device index to this device.
std::shared_ptr<raw::RawEventHandler> handler;
handler->condition = [this](std::vector<uint8_t>& report)->bool
{
auto handler = std::make_shared<raw::RawEventHandler>();
handler->condition = [index=this->_index](std::vector<uint8_t>& report)
->bool {
return (report[Offset::Type] == Report::Type::Short ||
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);
this->handleEvent(_report);
};
_raw_device->addEventHandler("DEV_" + std::to_string(_index), handler);
_listening = true;
}
void Device::stopListening()
{
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
if(_listening)
_raw_device->removeEventHandler("DEV_" + std::to_string(_index));
_listening = false;
if(!_raw_device->eventHandlers().empty())
_raw_device->stopListener();

View File

@ -55,7 +55,7 @@ namespace hidpp
Asleep
};
InvalidDevice(Reason reason) : _reason (reason) {}
virtual const char *what() const noexcept;
virtual const char* what() const noexcept;
virtual Reason code() const noexcept;
private:
Reason _reason;
@ -81,6 +81,8 @@ namespace hidpp
void addEventHandler(const std::string& nickname,
const std::shared_ptr<EventHandler>& handler);
void removeEventHandler(const std::string& nickname);
const std::map<std::string, std::shared_ptr<EventHandler>>&
eventHandlers();
Report sendReport(Report& report);
@ -98,6 +100,8 @@ namespace hidpp
uint16_t _pid;
std::string _name;
std::atomic<bool> _listening;
std::map<std::string, std::shared_ptr<EventHandler>> _event_handlers;
};
} } }

View File

@ -21,6 +21,8 @@
#define LOGID_HIDPP_SOFTWARE_ID 0
#include <cstdint>
namespace logid {
namespace backend {
namespace hidpp

View File

@ -63,3 +63,8 @@ Feature::Feature(Device* dev, uint16_t _id) : _device (dev)
throw UnsupportedFeature(_id);
}
}
uint8_t Feature::featureIndex()
{
return _index;
}

View File

@ -40,7 +40,7 @@ namespace hidpp20 {
public:
static const uint16_t ID;
virtual uint16_t getID() = 0;
uint8_t featureIndex();
protected:
explicit Feature(Device* dev, uint16_t _id);
std::vector<uint8_t> callFunction(uint8_t function_id,

View File

@ -16,6 +16,7 @@
*
*/
#include <cassert>
#include "../Error.h"
#include "ReprogControls.h"
using namespace logid::backend::hidpp20;
@ -30,7 +31,7 @@ x::x(Device* dev, uint16_t _id) : base(dev, _id) \
#define MAKE_REPROG(x, dev) \
try { \
return x(dev); \
return std::make_shared<x>(dev); \
} catch(UnsupportedFeature &e) {\
}
@ -41,7 +42,7 @@ DEFINE_REPROG(ReprogControlsV2_2, ReprogControlsV2);
DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2);
DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3);
ReprogControls ReprogControls::autoVersion(Device *dev)
std::shared_ptr<ReprogControls> ReprogControls::autoVersion(Device *dev)
{
MAKE_REPROG(ReprogControlsV4, dev);
MAKE_REPROG(ReprogControlsV3, dev);
@ -49,7 +50,7 @@ ReprogControls ReprogControls::autoVersion(Device *dev)
MAKE_REPROG(ReprogControlsV2, dev);
// If base version cannot be made, throw error
return ReprogControls(dev);
return std::make_shared<ReprogControls>(dev);
}
uint8_t ReprogControls::getControlCount()
@ -78,29 +79,43 @@ ReprogControls::ControlInfo ReprogControls::getControlInfo(uint8_t index)
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)
{
std::vector<uint8_t> params(2);
ControlInfo info{};
params[0] = (cid >> 8) & 0xff;
params[1] = cid & 0xff;
auto response = callFunction(GetControlReporting, params);
// Emulate this function, only Reprog controls v4 supports this
auto info = getControlIdInfo(cid);
info.controlID = response[1];
info.controlID |= response[0] << 8;
info.flags = response[2];
return info;
ControlInfo report{};
report.controlID = cid;
report.flags = 0;
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)
{
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);
// This function does not exist pre-v4 and cannot be emulated, ignore.
}
std::set<uint16_t> ReprogControls::divertedButtonEvent(
@ -131,3 +146,28 @@ ReprogControls::Move ReprogControls::divertedRawXYEvent(const hidpp::Report
move.y |= report.paramBegin()[2] << 8;
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);
}

View File

@ -18,6 +18,8 @@
#ifndef LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
#define LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H
#include <map>
#include "../feature_defs.h"
#include "../Feature.h"
@ -28,27 +30,17 @@ namespace hidpp20
class ReprogControls : public Feature
{
public:
static const uint16_t ID = FeatureID::REPROG_CONTROLS;
virtual uint16_t getID() { return ID; }
virtual bool supportsRawXY() { return false; }
enum Function {
GetControlCount = 0,
GetControlInfo = 1,
GetControlReporting = 2,
SetControlReporting = 3
};
enum Event {
DivertedButtonEvent = 0,
DivertedRawXYEvent = 1
};
explicit ReprogControls(Device* dev);
virtual uint8_t getControlCount();
struct ControlInfo
{
uint16_t controlID;
@ -68,24 +60,41 @@ namespace hidpp20
FnToggle = 1<<3,
ReprogHint = 1<<4,
TemporaryDivertable = 1<<5,
PerisentlyDiverable = 1<<6,
PersisentlyDivertable = 1<<6,
Virtual = 1<<7
};
enum ControlInfoAdditionalFlags: uint8_t {
RawXY = 1<<0
};
virtual ControlInfo getControlInfo(uint8_t index);
enum ControlReportingFlags: uint8_t {
TemporaryDiverted = 1<<0,
ChangeTemporaryDivert = 1<<1,
PersistentDiverted = 1<<2,
PersistentlyDiverted = 1<<2,
ChangePersistentDivert = 1<<3,
RawXYDiverted = 1<<4,
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
virtual ControlInfo getControlReporting(uint16_t cid);
@ -95,24 +104,19 @@ namespace hidpp20
static std::set<uint16_t> divertedButtonEvent(const hidpp::Report&
report);
struct Move
{
int16_t x;
int16_t y;
};
static Move divertedRawXYEvent(const hidpp::Report& report);
static ReprogControls autoVersion(Device *dev);
static std::shared_ptr<ReprogControls> autoVersion(Device *dev);
protected:
ReprogControls(Device* dev, uint16_t _id);
std::map<uint16_t, ControlInfo> _cids;
};
class ReprogControlsV2 : public ReprogControls
{
public:
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);
protected:
@ -123,7 +127,7 @@ namespace hidpp20
{
public:
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);
protected:
@ -134,7 +138,7 @@ namespace hidpp20
{
public:
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);
protected:
@ -145,10 +149,14 @@ namespace hidpp20
{
public:
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; }
ControlInfo getControlReporting(uint16_t cid) override;
void setControlReporting(uint8_t cid, ControlInfo info) override;
explicit ReprogControlsV4(Device* dev);
protected:
ReprogControlsV4(Device* dev, uint16_t _id);

View File

@ -354,6 +354,7 @@ void RawDevice::addEventHandler(const std::string& nickname,
{
auto it = _event_handlers.find(nickname);
assert(it == _event_handlers.end());
assert(handler);
_event_handlers.emplace(nickname, handler);
}

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

View 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

View File

@ -24,8 +24,9 @@
#include "util/log.h"
#include "DeviceManager.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"
#ifndef LOGIOPS_VERSION
@ -40,6 +41,7 @@ std::string config_file = DEFAULT_CONFIG_FILE;
LogLevel logid::global_loglevel = INFO;
std::shared_ptr<Configuration> logid::global_config;
std::unique_ptr<DeviceManager> logid::device_manager;
std::unique_ptr<InputDevice> logid::virtual_input;
bool logid::kill_logid = false;
std::mutex logid::device_manager_reload;
@ -163,17 +165,14 @@ int main(int argc, char** argv)
global_config = std::make_shared<Configuration>();
}
/*
//Create an evdev device called 'logid'
try { global_evdev = new EvdevDevice(evdev_name); }
catch(std::system_error& e)
{
log_printf(ERROR, "Could not create evdev device: %s", e.what());
//Create a virtual input device
try {
virtual_input = std::make_unique<InputDevice>(LOGID_VIRTUAL_INPUT_NAME);
} catch(std::system_error& e) {
logPrintf(ERROR, "Could not create input device: %s", e.what());
return EXIT_FAILURE;
}
*/
// Scan devices, create listeners, handlers, etc.
device_manager = std::make_unique<DeviceManager>();