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

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(); return _name.c_str();
} }

View File

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

View File

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

View File

@ -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
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/>. * 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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