From e40da5f0c09846d164617e3d05e47c1405b0f0c6 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 21 Jun 2020 05:33:33 -0400 Subject: [PATCH] Implement receiver HID++ connect/disconnect events Many changes were made here but that was the biggest one. There's currently a bug where std::system_error: Broken pipe is thrown after launching the daemon with a receiver connector. A workaround for this bug is to simply shake the mouse while starting the daemon. I will investigate this soon. --- src/logid/CMakeLists.txt | 2 + src/logid/Device.cpp | 8 + src/logid/Device.h | 2 + src/logid/DeviceManager.cpp | 4 + src/logid/Receiver.cpp | 43 +++- src/logid/Receiver.h | 9 +- src/logid/backend/dj/Receiver.cpp | 185 ++++++++++++------ src/logid/backend/dj/Receiver.h | 64 ++++-- src/logid/backend/dj/ReceiverMonitor.cpp | 50 ++++- src/logid/backend/dj/ReceiverMonitor.h | 6 +- src/logid/backend/hidpp/Device.cpp | 110 +++++++---- src/logid/backend/hidpp/Device.h | 27 ++- src/logid/backend/hidpp/Report.cpp | 10 + src/logid/backend/hidpp/Report.h | 8 +- src/logid/backend/hidpp10/Device.cpp | 11 +- src/logid/backend/hidpp10/Device.h | 4 +- .../backend/hidpp20/EssentialFeature.cpp | 44 +++++ src/logid/backend/hidpp20/EssentialFeature.h | 32 +++ .../backend/hidpp20/features/DeviceName.cpp | 68 +++++++ .../backend/hidpp20/features/DeviceName.h | 43 ++++ src/logid/backend/hidpp20/features/Root.cpp | 42 +++- src/logid/backend/hidpp20/features/Root.h | 15 +- src/logid/backend/raw/RawDevice.cpp | 66 ++++--- src/logid/backend/raw/RawDevice.h | 17 +- 24 files changed, 689 insertions(+), 181 deletions(-) create mode 100644 src/logid/backend/hidpp20/EssentialFeature.cpp create mode 100644 src/logid/backend/hidpp20/EssentialFeature.h create mode 100644 src/logid/backend/hidpp20/features/DeviceName.cpp create mode 100644 src/logid/backend/hidpp20/features/DeviceName.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 5be8549..a51ed2b 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -27,7 +27,9 @@ add_executable(logid backend/hidpp20/Device.cpp backend/hidpp20/Error.cpp backend/hidpp20/Feature.cpp + backend/hidpp20/EssentialFeature.cpp backend/hidpp20/features/Root.cpp + backend/hidpp20/features/DeviceName.cpp backend/dj/Report.cpp util/mutex_queue.h) diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 96a61db..3550c09 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -2,6 +2,7 @@ #include "Device.h" using namespace logid; +using namespace logid::backend; Device::Device(std::string path, backend::hidpp::DeviceIndex index) : _hidpp20 (path, index), _path (path), _index (index) @@ -9,6 +10,13 @@ Device::Device(std::string path, backend::hidpp::DeviceIndex index) : log_printf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index); } +Device::Device(const std::shared_ptr& raw_device, + hidpp::DeviceIndex index) : _hidpp20(raw_device, index), _path + (raw_device->hidrawPath()), _index (index) +{ + log_printf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index); +} + void Device::sleep() { log_printf(INFO, "%s:%d fell asleep.", _path.c_str(), _index); diff --git a/src/logid/Device.h b/src/logid/Device.h index a9f22da..ccc78a1 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -14,6 +14,8 @@ namespace logid { public: Device(std::string path, backend::hidpp::DeviceIndex index); + Device(const std::shared_ptr& raw_device, + backend::hidpp::DeviceIndex index); void wakeup(); void sleep(); private: diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index 58418d7..1ee5854 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -33,6 +33,7 @@ void DeviceManager::addDevice(std::string path) if(isReceiver) { log_printf(INFO, "Detected receiver at %s", path.c_str()); auto receiver = std::make_shared(path); + receiver->run(); _receivers.emplace(path, receiver); } else { /* TODO: Error check? @@ -48,6 +49,9 @@ void DeviceManager::addDevice(std::string path) } catch(hidpp10::Error &e) { if(e.code() != hidpp10::Error::UnknownDevice) throw; + else + log_printf(WARN, "HID++ 1.0 error while trying to initialize" + " %s: %s", path.c_str(), e.what()); } catch(hidpp::Device::InvalidDevice &e) { // Ignore } catch(std::system_error &e) { // This error should have been thrown previously diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index 77d34c8..c2b6e1e 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -1,9 +1,50 @@ +#include #include "Receiver.h" #include "util.h" +#include "backend/hidpp10/Error.h" +#include "backend/hidpp20/Error.h" using namespace logid; +using namespace logid::backend; -Receiver::Receiver(std::string path) : _path (path) +Receiver::Receiver(std::string path) : dj::ReceiverMonitor(path) { log_printf(DEBUG, "logid::Receiver created on %s", path.c_str()); +} + +void Receiver::addDevice(hidpp::DeviceConnectionEvent event) +{ + try { + if(!event.linkEstablished) + return; // Device is probably asleep, wait until it wakes up + + hidpp::Device hidpp_device(receiver(), event); + + auto version = hidpp_device.version(); + + if(std::get<0>(version) < 2) { + log_printf(INFO, "Unsupported HID++ 1.0 device on %s:%d connected.", + _path.c_str(), event.index); + return; + } + + std::shared_ptr device = std::make_shared( + receiver()->rawDevice(), event.index); + + assert(_devices.find(event.index) == _devices.end()); + + _devices.emplace(event.index, device); + + } catch(hidpp10::Error &e) { + log_printf(ERROR, "Caught HID++ 1.0 error while trying to initialize " + "%s:%d: %s", _path.c_str(), event.index, e.what()); + } catch(hidpp20::Error &e) { + log_printf(ERROR, "Caught HID++ 2.0 error while trying to initialize " + "%s:%d: %s", _path.c_str(), event.index, e.what()); + } +} + +void Receiver::removeDevice(hidpp::DeviceIndex index) +{ + _devices.erase(index); } \ No newline at end of file diff --git a/src/logid/Receiver.h b/src/logid/Receiver.h index a01f37f..99aeebe 100644 --- a/src/logid/Receiver.h +++ b/src/logid/Receiver.h @@ -2,14 +2,21 @@ #define LOGID_RECEIVER_H #include +#include "backend/dj/ReceiverMonitor.h" +#include "Device.h" namespace logid { - class Receiver + class Receiver : public backend::dj::ReceiverMonitor { public: Receiver(std::string path); + + protected: + virtual void addDevice(backend::hidpp::DeviceConnectionEvent event); + virtual void removeDevice(backend::hidpp::DeviceIndex index); private: + std::map> _devices; std::string _path; }; } diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp index d579a32..847fe53 100644 --- a/src/logid/backend/dj/Receiver.cpp +++ b/src/logid/backend/dj/Receiver.cpp @@ -32,47 +32,17 @@ Receiver::Receiver(std::string path) : { if(!supportsDjReports(raw_device->reportDescriptor())) throw InvalidReceiver(InvalidReceiver::NoDJReports); - - - // Pass all HID++ events on DefaultDevice to handleHidppEvent - RawEventHandler hidppRawEventHandler; - hidppRawEventHandler.condition = [this](std::vector& report)->bool - { - return (report[hidpp::Offset::Type] == hidpp::Report::Type::Short || - report[hidpp::Offset::Type] == hidpp::Report::Type::Long) && - (report[hidpp::Offset::DeviceIndex] == hidpp::DefaultDevice); - }; - hidppRawEventHandler.callback = [this](std::vector& report)->void - { - hidpp::Report _report(report); - this->handleHidppEvent(_report); - }; - - // Pass all DJ events with device index to handleHidppEvent - RawEventHandler djRawEventHandler; - djRawEventHandler.condition = [this](std::vector& report)->bool - { - return (report[Offset::Type] == Report::Type::Short || - report[Offset::Type] == Report::Type::Long); - }; - djRawEventHandler.callback = [this](std::vector& report)->void - { - Report _report(report); - this->handleDjEvent(_report); - }; - - raw_device->addEventHandler("RECV_HIDPP", hidppRawEventHandler); - raw_device->addEventHandler("RECV_DJ", djRawEventHandler); } -void Receiver::enumerate() +void Receiver::enumerateDj() { sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{}); } Receiver::notification_flags Receiver::getHidppNotifications() { - auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {}); + auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {}, + hidpp::ReportType::Short); notification_flags flags{}; flags.device_battery_status = response[0] & (1 << 4); @@ -89,17 +59,28 @@ void Receiver::enableHidppNotifications(notification_flags flags) if(flags.device_battery_status) request[0] |= (1 << 4); if(flags.receiver_wireless_notifications) - request[1] |= (1 << 0); + request[1] |= 1; if(flags.receiver_software_present) request[1] |= (1 << 3); - _hidpp10_device.setRegister(EnableHidppNotifications, request); + _hidpp10_device.setRegister(EnableHidppNotifications, request, + hidpp::ReportType::Short); +} + +void Receiver::enumerateHidpp() +{ + /* This isn't in the documentation but this is how solaar does it + * All I know is that when (p0 & 2), devices are enumerated + */ + _hidpp10_device.setRegister(ConnectionState, {2}, + hidpp::ReportType::Short); } ///TODO: Investigate usage uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index) { - auto response = _hidpp10_device.setRegister(ConnectionState, {index}); + auto response = _hidpp10_device.getRegister(ConnectionState, {index}, + hidpp::ReportType::Short); return response[0]; } @@ -113,7 +94,8 @@ void Receiver::startPairing(uint8_t timeout) request[1] = hidpp::DefaultDevice; request[2] = timeout; - _hidpp10_device.setRegister(DevicePairing, request); + _hidpp10_device.setRegister(DevicePairing, request, + hidpp::ReportType::Short); } void Receiver::stopPairing() @@ -124,7 +106,8 @@ void Receiver::stopPairing() request[0] = 2; request[1] = hidpp::DefaultDevice; - _hidpp10_device.setRegister(DevicePairing, request); + _hidpp10_device.setRegister(DevicePairing, request, + hidpp::ReportType::Short); } void Receiver::disconnect(hidpp::DeviceIndex index) @@ -135,12 +118,14 @@ void Receiver::disconnect(hidpp::DeviceIndex index) request[0] = 3; request[1] = index; - _hidpp10_device.setRegister(DevicePairing, request); + _hidpp10_device.setRegister(DevicePairing, request, + hidpp::ReportType::Short); } std::map Receiver::getDeviceActivity() { - auto response = _hidpp10_device.getRegister(DeviceActivity, {}); + auto response = _hidpp10_device.getRegister(DeviceActivity, {}, + hidpp::ReportType::Long); std::map device_activity; for(uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++) @@ -156,7 +141,8 @@ struct Receiver::PairingInfo request[0] = index; request[0] += 0x19; - auto response = _hidpp10_device.getRegister(PairingInfo, request); + auto response = _hidpp10_device.getRegister(PairingInfo, request, + hidpp::ReportType::Long); struct PairingInfo info{}; info.destinationId = response[0]; @@ -175,7 +161,8 @@ struct Receiver::ExtendedPairingInfo request[0] = index; request[0] += 0x29; - auto response = _hidpp10_device.getRegister(PairingInfo, request); + auto response = _hidpp10_device.getRegister(PairingInfo, request, + hidpp::ReportType::Long); ExtendedPairingInfo info{}; @@ -197,7 +184,8 @@ std::string Receiver::getDeviceName(hidpp::DeviceIndex index) request[0] = index; request[0] += 0x39; - auto response = _hidpp10_device.getRegister(PairingInfo, request); + auto response = _hidpp10_device.getRegister(PairingInfo, request, + hidpp::ReportType::Long); uint8_t size = response[0]; assert(size <= 14); @@ -215,12 +203,12 @@ hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(hidpp::Report& report) return report.deviceIndex(); } -Receiver::DeviceConnectionEvent Receiver::deviceConnectionEvent( +hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent( hidpp::Report &report) { assert(report.subId() == DeviceConnection); - DeviceConnectionEvent event{}; + hidpp::DeviceConnectionEvent event{}; event.index = report.deviceIndex(); event.unifying = ((report.paramBegin()[0] & 0b111) == 0x04); @@ -240,23 +228,54 @@ Receiver::DeviceConnectionEvent Receiver::deviceConnectionEvent( void Receiver::handleDjEvent(Report& report) { - if(report.feature() == DeviceConnection || - report.feature() == DeviceDisconnection || - report.feature() == ConnectionStatus) - { - printf("%s DJ IN: ", raw_device->hidrawPath().c_str()); - for(auto &i: report.rawData()) - printf("%02x ", i); - printf("\n"); - } + for(auto& handler : dj_event_handlers) + if(handler.second->condition(report)) + handler.second->callback(report); } void Receiver::handleHidppEvent(hidpp::Report &report) { - printf("%s HID++ IN: ", raw_device->hidrawPath().c_str()); - for(auto &i: report.rawReport()) - printf("%02x ", i); - printf("\n"); + for(auto& handler : hidpp_event_handlers) + if(handler.second->condition(report)) + handler.second->callback(report); +} + +void Receiver::addDjEventHandler(const std::string& nickname, + const std::shared_ptr& handler) +{ + auto it = dj_event_handlers.find(nickname); + assert(it == dj_event_handlers.end()); + dj_event_handlers.emplace(nickname, handler); +} + +void Receiver::removeDjEventHandler(const std::string &nickname) +{ + dj_event_handlers.erase(nickname); +} + +const std::map>& +Receiver::djEventHandlers() +{ + return dj_event_handlers; +} + +void Receiver::addHidppEventHandler(const std::string& nickname, + const std::shared_ptr& handler) +{ + auto it = hidpp_event_handlers.find(nickname); + assert(it == hidpp_event_handlers.end()); + hidpp_event_handlers.emplace(nickname, handler); +} + +void Receiver::removeHidppEventHandler(const std::string &nickname) +{ + hidpp_event_handlers.erase(nickname); +} + +const std::map>& +Receiver::hidppEventHandlers() +{ + return hidpp_event_handlers; } void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function, @@ -276,10 +295,56 @@ void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function, void Receiver::listen() { - std::thread{[=]() { raw_device->listen(); }}.detach(); + if(!raw_device->isListening()) + std::thread{[=]() { raw_device->listen(); }}.detach(); + + if(raw_device->eventHandlers().find("RECV_HIDPP") == + raw_device->eventHandlers().end()) { + // Pass all HID++ events on DefaultDevice to handleHidppEvent + std::shared_ptr hidppRawEventHandler = + std::make_shared(); + hidppRawEventHandler->condition = [](std::vector& report)->bool + { + return (report[hidpp::Offset::Type] == hidpp::Report::Type::Short || + report[hidpp::Offset::Type] == hidpp::Report::Type::Long); + }; + hidppRawEventHandler->callback = [this](std::vector& report)->void + { + hidpp::Report _report(report); + this->handleHidppEvent(_report); + }; + raw_device->addEventHandler("RECV_HIDPP", hidppRawEventHandler); + } + + if(raw_device->eventHandlers().find("RECV_DJ") == + raw_device->eventHandlers().end()) { + // Pass all DJ events with device index to handleHidppEvent + std::shared_ptr djRawEventHandler = + std::make_shared(); + djRawEventHandler->condition = [](std::vector& report)->bool + { + return (report[Offset::Type] == Report::Type::Short || + report[Offset::Type] == Report::Type::Long); + }; + djRawEventHandler->callback = [this](std::vector& report)->void + { + Report _report(report); + this->handleDjEvent(_report); + }; + raw_device->addEventHandler("RECV_DJ", djRawEventHandler); + } } void Receiver::stopListening() { - raw_device->stopListener(); + raw_device->removeEventHandler("RECV_HIDPP"); + raw_device->removeEventHandler("RECV_DJ"); + + if(raw_device->eventHandlers().empty()) + raw_device->stopListener(); +} + +std::shared_ptr Receiver::rawDevice() const +{ + return raw_device; } \ No newline at end of file diff --git a/src/logid/backend/dj/Receiver.h b/src/logid/backend/dj/Receiver.h index 3954a20..0214e17 100644 --- a/src/logid/backend/dj/Receiver.h +++ b/src/logid/backend/dj/Receiver.h @@ -11,6 +11,12 @@ namespace logid { namespace backend { namespace dj { + struct EventHandler + { + std::function condition; + std::function callback; + }; + class InvalidReceiver : public std::exception { public: @@ -43,7 +49,7 @@ namespace dj GetPairedDevices = 0x81 }; - void enumerate(); + void enumerateDj(); /* The following functions deal with HID++ 1.0 features. * While these are not technically DJ functions, it is redundant @@ -77,7 +83,7 @@ namespace dj notification_flags getHidppNotifications(); void enableHidppNotifications(notification_flags flags); - ///TODO: Understand output of this function + void enumerateHidpp(); uint8_t getConnectionState(hidpp::DeviceIndex index); void startPairing(uint8_t timeout = 0); @@ -107,35 +113,57 @@ namespace dj std::string getDeviceName(hidpp::DeviceIndex index); - struct DeviceConnectionEvent - { - hidpp::DeviceIndex index; - uint16_t pid; - DeviceType::DeviceType deviceType; - bool unifying; - bool softwarePresent; - bool encrypted; - bool linkEstablished; - bool withPayload; - }; - static hidpp::DeviceIndex deviceDisconnectionEvent( hidpp::Report& report); - static DeviceConnectionEvent deviceConnectionEvent( + static hidpp::DeviceConnectionEvent deviceConnectionEvent( hidpp::Report& report); - void handleDjEvent(dj::Report& report); - void handleHidppEvent(hidpp::Report& report); - void listen(); void stopListening(); + + void addDjEventHandler(const std::string& nickname, + const std::shared_ptr& handler); + void removeDjEventHandler(const std::string& nickname); + const std::map>& + djEventHandlers(); + + void addHidppEventHandler(const std::string& nickname, + const std::shared_ptr& handler); + void removeHidppEventHandler(const std::string& nickname); + const std::map>& + hidppEventHandlers(); + + std::shared_ptr rawDevice() const; private: void sendDjRequest(hidpp::DeviceIndex index, uint8_t function, const std::vector&& params); + void handleDjEvent(dj::Report& report); + void handleHidppEvent(hidpp::Report& report); + + std::map> + dj_event_handlers; + std::map> + hidpp_event_handlers; + std::shared_ptr raw_device; hidpp10::Device _hidpp10_device; }; +} + +namespace hidpp +{ + struct DeviceConnectionEvent + { + hidpp::DeviceIndex index; + uint16_t pid; + dj::DeviceType::DeviceType deviceType; + bool unifying; + bool softwarePresent; + bool encrypted; + bool linkEstablished; + bool withPayload; + }; }}} #endif //LOGID_BACKEND_DJ_RECEIVER_H \ No newline at end of file diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp index f7302b2..bdc8366 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/dj/ReceiverMonitor.cpp @@ -1,30 +1,70 @@ #include "ReceiverMonitor.h" #include +#include using namespace logid::backend::dj; -ReceiverMonitor::ReceiverMonitor(std::string path) : _reciever (std::move(path)) +ReceiverMonitor::ReceiverMonitor(std::string path) : _receiver ( + std::make_shared(std::move(path))) { + assert(_receiver->hidppEventHandlers().find("RECVMON") == + _receiver->hidppEventHandlers().end()); + assert(_receiver->djEventHandlers().find("RECVMON") == + _receiver->djEventHandlers().end()); + Receiver::notification_flags notification_flags{ true, true, true}; - _reciever.enableHidppNotifications(notification_flags); + _receiver->enableHidppNotifications(notification_flags); } void ReceiverMonitor::run() { - _reciever.listen(); + _receiver->listen(); + + if(_receiver->hidppEventHandlers().find("RECVMON") == + _receiver->hidppEventHandlers().end()) + { + std::shared_ptr eventHandler = + std::make_shared(); + eventHandler->condition = [](hidpp::Report &report) -> bool { + return (report.subId() == Receiver::DeviceConnection || + report.subId() == Receiver::DeviceDisconnection); + }; + + eventHandler->callback = [this](hidpp::Report &report) -> void { + /* Running in a new thread prevents deadlocks since the + * receiver may be enumerating. + */ + std::thread{[this](hidpp::Report report) { + if (report.subId() == Receiver::DeviceConnection) + this->addDevice(this->_receiver->deviceConnectionEvent( + report)); + else if (report.subId() == Receiver::DeviceDisconnection) + this->removeDevice(this->_receiver-> + deviceDisconnectionEvent(report)); + }, report}.detach(); + }; + + _receiver->addHidppEventHandler("RECVMON", eventHandler); + } + enumerate(); } void ReceiverMonitor::stop() { - _reciever.stopListening(); + _receiver->stopListening(); } void ReceiverMonitor::enumerate() { - _reciever.enumerate(); + _receiver->enumerateHidpp(); +} + +std::shared_ptr ReceiverMonitor::receiver() const +{ + return _receiver; } \ No newline at end of file diff --git a/src/logid/backend/dj/ReceiverMonitor.h b/src/logid/backend/dj/ReceiverMonitor.h index 5625294..b2223c1 100644 --- a/src/logid/backend/dj/ReceiverMonitor.h +++ b/src/logid/backend/dj/ReceiverMonitor.h @@ -21,7 +21,7 @@ namespace dj void stop(); protected: - virtual void addDevice(hidpp::DeviceIndex index, uint16_t pid) = 0; + virtual void addDevice(hidpp::DeviceConnectionEvent event) = 0; virtual void removeDevice(hidpp::DeviceIndex index) = 0; // Internal methods for derived class @@ -29,8 +29,10 @@ namespace dj void _stopPairing(); void _unpair(); + + std::shared_ptr receiver() const; private: - Receiver _reciever; + std::shared_ptr _receiver; }; }}} diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index d6cbdc8..ea2d196 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -1,9 +1,12 @@ -#include +#include +#include #include "Device.h" #include "Report.h" #include "../hidpp20/features/Root.h" +#include "../hidpp20/features/DeviceName.h" #include "../hidpp20/Error.h" #include "../hidpp10/Error.h" +#include "../dj/Receiver.h" using namespace logid::backend; using namespace logid::backend::hidpp; @@ -16,6 +19,10 @@ const char* Device::InvalidDevice::what() const noexcept return "Invalid HID++ device"; case InvalidRawDevice: return "Invalid raw device"; + case Asleep: + return "Device asleep"; + default: + return "Invalid device"; } } @@ -26,36 +33,41 @@ Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept /// TODO: Initialize a single RawDevice for each path. Device::Device(const std::string& path, DeviceIndex index): - raw_device (std::make_shared(path)), path (path), - _index (index) + _raw_device (std::make_shared(path)), _receiver (nullptr), + _path (path), _index (index) { _init(); } Device::Device(std::shared_ptr raw_device, DeviceIndex index) : - raw_device (raw_device), _index (index) + _raw_device (std::move(raw_device)), _receiver (nullptr), + _path (_raw_device->hidrawPath()), _index (index) { _init(); } +Device::Device(std::shared_ptr receiver, + hidpp::DeviceConnectionEvent event) : + _raw_device (receiver->rawDevice()), _index (event.index) +{ + // Device will throw an error soon, just do it now + if(!event.linkEstablished) + throw InvalidDevice(InvalidDevice::Asleep); + + _pid = event.pid; + _init(); +} + void Device::_init() { - supported_reports = getSupportedReports(raw_device->reportDescriptor()); + supported_reports = getSupportedReports(_raw_device->reportDescriptor()); if(!supported_reports) throw InvalidDevice(InvalidDevice::NoHIDPPReport); - try - { - Report versionRequest(Report::Type::Short, _index, - hidpp20::FeatureID::ROOT,hidpp20::Root::Ping, - LOGID_HIDPP_SOFTWARE_ID); - - auto versionResponse = sendReport(versionRequest); - auto versionResponse_params = versionResponse.paramBegin(); - _version = std::make_tuple(versionResponse_params[0], versionResponse_params[1]); - } - catch(hidpp10::Error &e) - { + try { + hidpp20::EssentialRoot root(this); + _version = root.getVersion(); + } catch(hidpp10::Error &e) { // Valid HID++ 1.0 devices should send an InvalidSubID error if(e.code() != hidpp10::Error::InvalidSubID) throw; @@ -64,29 +76,30 @@ void Device::_init() _version = std::make_tuple(1, 0); } - // Pass all HID++ events with device index to this device. - RawEventHandler rawEventHandler; - rawEventHandler.condition = [this](std::vector& report)->bool - { - return (report[Offset::Type] == Report::Type::Short || - report[Offset::Type] == Report::Type::Long) && - (report[Offset::DeviceIndex] == this->_index); - }; - rawEventHandler.callback = [this](std::vector& report)->void - { - Report _report(report); - this->handleEvent(_report); - }; - - raw_device->addEventHandler("DEV_" + std::to_string(_index), rawEventHandler); + if(!_receiver) { + _pid = _raw_device->productId(); + if(std::get<0>(_version) >= 2) { + try { + hidpp20::EssentialDeviceName deviceName(this); + _name = deviceName.getName(); + } catch(hidpp20::UnsupportedFeature &e) { + _name = _raw_device->name(); + } + } else { + _name = _raw_device->name(); + } + } else { + _name = _receiver->getDeviceName(_index); + } } Device::~Device() { - raw_device->removeEventHandler("DEV_" + std::to_string(_index)); + _raw_device->removeEventHandler("DEV_" + std::to_string(_index)); } -void Device::addEventHandler(const std::string& nickname, const std::shared_ptr& handler) +void Device::addEventHandler(const std::string& nickname, + const std::shared_ptr& handler) { auto it = event_handlers.find(nickname); assert(it == event_handlers.end()); @@ -119,7 +132,7 @@ Report Device::sendReport(Report& report) assert(supported_reports & HIDPP_REPORT_LONG_SUPPORTED); } - auto raw_response = raw_device->sendReport(report.rawReport()); + auto raw_response = _raw_device->sendReport(report.rawReport()); Report response(raw_response); @@ -136,5 +149,30 @@ Report Device::sendReport(Report& report) void Device::listen() { - raw_device->listen(); + if(!_raw_device->isListening()) + std::thread{[=]() { _raw_device->listen(); }}.detach(); + + // Pass all HID++ events with device index to this device. + std::shared_ptr rawEventHandler; + rawEventHandler->condition = [this](std::vector& report)->bool + { + return (report[Offset::Type] == Report::Type::Short || + report[Offset::Type] == Report::Type::Long) && + (report[Offset::DeviceIndex] == this->_index); + }; + rawEventHandler->callback = [this](std::vector& report)->void + { + Report _report(report); + this->handleEvent(_report); + }; + + _raw_device->addEventHandler("DEV_" + std::to_string(_index), rawEventHandler); } + +void Device::stopListening() +{ + _raw_device->removeEventHandler("DEV_" + std::to_string(_index)); + + if(!_raw_device->eventHandlers().empty()) + _raw_device->stopListener(); +} \ No newline at end of file diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index fc5215d..460e65b 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -11,8 +11,14 @@ namespace logid { namespace backend { +namespace dj +{ + // Need to define here for a constructor + class Receiver; +} namespace hidpp { + struct DeviceConnectionEvent; struct EventHandler { std::function condition; @@ -27,7 +33,8 @@ namespace hidpp enum Reason { NoHIDPPReport, - InvalidRawDevice + InvalidRawDevice, + Asleep }; InvalidDevice(Reason reason) : _reason (reason) {} virtual const char *what() const noexcept; @@ -36,18 +43,21 @@ namespace hidpp Reason _reason; }; - Device(const std::string& path, DeviceIndex index); - Device(std::shared_ptr raw_device, DeviceIndex index); + explicit Device(const std::string& path, DeviceIndex index); + explicit Device(std::shared_ptr raw_device, DeviceIndex index); + explicit Device(std::shared_ptr receiver, + hidpp::DeviceConnectionEvent event); ~Device(); - std::string devicePath() const { return path; } + std::string devicePath() const { return _path; } DeviceIndex deviceIndex() const { return _index; } std::tuple version() const { return _version; } void listen(); // Runs asynchronously void stopListening(); - void addEventHandler(const std::string& nickname, const std::shared_ptr& handler); + void addEventHandler(const std::string& nickname, + const std::shared_ptr& handler); void removeEventHandler(const std::string& nickname); Report sendReport(Report& report); @@ -56,12 +66,15 @@ namespace hidpp private: void _init(); - std::shared_ptr raw_device; - std::string path; + std::shared_ptr _raw_device; + std::shared_ptr _receiver; + std::string _path; DeviceIndex _index; uint8_t supported_reports; std::tuple _version; + uint16_t _pid; + std::string _name; std::map> event_handlers; }; diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index d68ce1e..b928bc4 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -180,6 +180,16 @@ void Report::setType(Report::Type type) _data[Offset::Type] = type; } +hidpp::DeviceIndex Report::deviceIndex() +{ + return static_cast(_data[Offset::DeviceIndex]); +} + +void Report::setDeviceIndex(hidpp::DeviceIndex index) +{ + _data[Offset::DeviceIndex] = index; +} + uint8_t Report::feature() const { return _data[Offset::Feature]; diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index 8945cf1..82292c5 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -60,6 +60,9 @@ namespace logid::backend::hidpp Report::Type type() const; void setType(Report::Type type); + logid::backend::hidpp::DeviceIndex deviceIndex(); + void setDeviceIndex(hidpp::DeviceIndex index); + uint8_t feature() const; void setFeature(uint8_t feature); @@ -96,11 +99,6 @@ namespace logid::backend::hidpp bool isError20(hidpp20_error* error); - logid::backend::hidpp::DeviceIndex deviceIndex() - { - return static_cast(_data[Offset::DeviceIndex]); - } - std::vector rawReport () const { return _data; } private: static constexpr std::size_t HeaderLength = 4; diff --git a/src/logid/backend/hidpp10/Device.cpp b/src/logid/backend/hidpp10/Device.cpp index b69e579..e1f2d15 100644 --- a/src/logid/backend/hidpp10/Device.cpp +++ b/src/logid/backend/hidpp10/Device.cpp @@ -19,13 +19,10 @@ Device::Device(std::shared_ptr raw_dev, } std::vector Device::getRegister(uint8_t address, - const std::vector& params) + const std::vector& params, hidpp::Report::Type type) { assert(params.size() <= hidpp::LongParamLength); - hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? - hidpp::Report::Type::Short : hidpp::Report::Type::Long; - uint8_t sub_id = type == hidpp::Report::Type::Short ? GetRegisterShort : GetRegisterLong; @@ -33,13 +30,11 @@ std::vector Device::getRegister(uint8_t address, } std::vector Device::setRegister(uint8_t address, - const std::vector& params) + const std::vector& params, + hidpp::Report::Type type) { assert(params.size() <= hidpp::LongParamLength); - hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? - hidpp::Report::Type::Short : hidpp::Report::Type::Long; - uint8_t sub_id = type == hidpp::Report::Type::Short ? SetRegisterShort : SetRegisterLong; diff --git a/src/logid/backend/hidpp10/Device.h b/src/logid/backend/hidpp10/Device.h index 76da044..688bab8 100644 --- a/src/logid/backend/hidpp10/Device.h +++ b/src/logid/backend/hidpp10/Device.h @@ -15,10 +15,10 @@ namespace hidpp10 hidpp::DeviceIndex index); std::vector getRegister(uint8_t address, - const std::vector& params); + const std::vector& params, hidpp::Report::Type type); std::vector setRegister(uint8_t address, - const std::vector& params); + const std::vector& params, hidpp::Report::Type type); private: std::vector accessRegister(uint8_t sub_id, uint8_t address, const std::vector& params); diff --git a/src/logid/backend/hidpp20/EssentialFeature.cpp b/src/logid/backend/hidpp20/EssentialFeature.cpp new file mode 100644 index 0000000..3634cc5 --- /dev/null +++ b/src/logid/backend/hidpp20/EssentialFeature.cpp @@ -0,0 +1,44 @@ +#include +#include "EssentialFeature.h" +#include "feature_defs.h" +#include "features/Root.h" + +using namespace logid::backend::hidpp20; + +std::vector EssentialFeature::callFunction(uint8_t function_id, + std::vector& params) +{ + hidpp::Report::Type type; + + assert(params.size() <= hidpp::LongParamLength); + if(params.size() <= hidpp::ShortParamLength) + type = hidpp::Report::Type::Short; + else if(params.size() <= hidpp::LongParamLength) + type = hidpp::Report::Type::Long; + + hidpp::Report request(type, _device->deviceIndex(), _index, function_id, + LOGID_HIDPP_SOFTWARE_ID); + std::copy(params.begin(), params.end(), request.paramBegin()); + + auto response = _device->sendReport(request); + return std::vector(response.paramBegin(), response.paramEnd()); +} + +EssentialFeature::EssentialFeature(hidpp::Device* dev, uint16_t _id) : + _device (dev) +{ + _index = hidpp20::FeatureID::ROOT; + + if(_id) + { + std::vector getFunc_req(2); + getFunc_req[0] = (_id >> 8) & 0xff; + getFunc_req[1] = _id & 0xff; + auto getFunc_resp = this->callFunction(Root::GetFeature, getFunc_req); + _index = getFunc_resp[0]; + + // 0 if not found + if(!_index) + throw UnsupportedFeature(_id); + } +} diff --git a/src/logid/backend/hidpp20/EssentialFeature.h b/src/logid/backend/hidpp20/EssentialFeature.h new file mode 100644 index 0000000..4e25165 --- /dev/null +++ b/src/logid/backend/hidpp20/EssentialFeature.h @@ -0,0 +1,32 @@ +#ifndef LOGID_HIDPP20_ESSENTIAL_FEATURE_H +#define LOGID_HIDPP20_ESSENTIAL_FEATURE_H + +// WARNING: UNSAFE + +/* This class is only meant to provide essential HID++ 2.0 features to the + * hidpp::Device class. No version checks are provided here + */ + +#include "Device.h" + +namespace logid { +namespace backend { +namespace hidpp20 +{ + class EssentialFeature + { + public: + static const uint16_t ID; + virtual uint16_t getID() = 0; + + protected: + EssentialFeature(hidpp::Device* dev, uint16_t _id); + std::vector callFunction(uint8_t function_id, + std::vector& params); + private: + hidpp::Device* _device; + uint8_t _index; + }; +}}} + +#endif //LOGID_HIDPP20_ESSENTIAL_FEATURE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/DeviceName.cpp b/src/logid/backend/hidpp20/features/DeviceName.cpp new file mode 100644 index 0000000..bc133e7 --- /dev/null +++ b/src/logid/backend/hidpp20/features/DeviceName.cpp @@ -0,0 +1,68 @@ +#include +#include "DeviceName.h" + +using namespace logid::backend; +using namespace logid::backend::hidpp20; + +DeviceName::DeviceName(Device* dev) : Feature(dev, ID) +{ +} + +uint8_t DeviceName::getNameLength() +{ + std::vector params(0); + + auto response = this->callFunction(Function::GetLength, params); + return response[0]; +} + +std::string _getName(uint8_t length, + const std::function(std::vector)>& fcall) +{ + uint8_t function_calls = length/hidpp::LongParamLength; + if(length % hidpp::LongParamLength) + function_calls++; + std::vector params(1); + std::string name; + + for(uint8_t i = 0; i < function_calls; i++) { + params[0] = i*hidpp::LongParamLength; + auto name_section = fcall(params); + for(std::size_t j = 0; j < hidpp::LongParamLength; j++) { + if(params[0] + j >= length) + return name; + name += name_section[j]; + } + } + + return name; +} + +std::string DeviceName::getName() +{ + return _getName(getNameLength(), [this] + (std::vector params)->std::vector { + return this->callFunction(Function::GetDeviceName, params); + }); +} + +EssentialDeviceName::EssentialDeviceName(hidpp::Device* dev) : + EssentialFeature(dev, ID) +{ +} + +uint8_t EssentialDeviceName::getNameLength() +{ + std::vector params(0); + + auto response = this->callFunction(DeviceName::Function::GetLength, params); + return response[0]; +} + +std::string EssentialDeviceName::getName() +{ + return _getName(getNameLength(), [this] + (std::vector params)->std::vector { + return this->callFunction(DeviceName::Function::GetDeviceName, params); + }); +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/DeviceName.h b/src/logid/backend/hidpp20/features/DeviceName.h new file mode 100644 index 0000000..39b7cca --- /dev/null +++ b/src/logid/backend/hidpp20/features/DeviceName.h @@ -0,0 +1,43 @@ +#ifndef LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H +#define LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H + +#include "../Feature.h" +#include "../feature_defs.h" +#include "../EssentialFeature.h" + +namespace logid { +namespace backend { +namespace hidpp20 +{ + class DeviceName : public Feature + { + public: + static const uint16_t ID = FeatureID::DEVICE_NAME; + virtual uint16_t getID() { return ID; } + + enum Function : uint8_t + { + GetLength = 0, + GetDeviceName = 1 + }; + + DeviceName(Device* device); + + uint8_t getNameLength(); + std::string getName(); + }; + + class EssentialDeviceName : public EssentialFeature + { + public: + static const uint16_t ID = FeatureID::DEVICE_NAME; + virtual uint16_t getID() { return ID; } + + EssentialDeviceName(hidpp::Device* device); + + uint8_t getNameLength(); + std::string getName(); + }; +}}} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/Root.cpp b/src/logid/backend/hidpp20/features/Root.cpp index 7f44da8..b5a6203 100644 --- a/src/logid/backend/hidpp20/features/Root.cpp +++ b/src/logid/backend/hidpp20/features/Root.cpp @@ -6,31 +6,59 @@ Root::Root(Device* dev) : Feature(dev, ID) { } -feature_info Root::getFeature(uint16_t feature_id) +std::vector _genGetFeatureParams(uint16_t feature_id) { - feature_info info{}; std::vector params(2); params[0] = feature_id & 0xff; params[1] = (feature_id >> 8) & 0xff; + return params; +} - auto response = this->callFunction(Function::GetFeature, params); - +feature_info _genGetFeatureInfo(uint16_t feature_id, std::vector response) +{ + feature_info info{}; info.feature_id = response[0]; if(!info.feature_id) throw UnsupportedFeature(feature_id); - info.hidden = response[1] & FeatureFlag::Hidden; - info.obsolete = response[1] & FeatureFlag::Obsolete; - info.internal = response[1] & FeatureFlag::Internal; + info.hidden = response[1] & Root::FeatureFlag::Hidden; + info.obsolete = response[1] & Root::FeatureFlag::Obsolete; + info.internal = response[1] & Root::FeatureFlag::Internal; return info; } +feature_info Root::getFeature(uint16_t feature_id) +{ + auto params = _genGetFeatureParams(feature_id); + auto response = this->callFunction(Function::GetFeature, params); + return _genGetFeatureInfo(feature_id, response); +} + std::tuple Root::getVersion() { std::vector params(0); auto response = this->callFunction(Function::Ping, params); + return std::make_tuple(response[0], response[1]); +} + +EssentialRoot::EssentialRoot(hidpp::Device* dev) : EssentialFeature(dev, ID) +{ +} + +feature_info EssentialRoot::getFeature(uint16_t feature_id) +{ + auto params = _genGetFeatureParams(feature_id); + auto response = this->callFunction(Root::Function::GetFeature, params); + return _genGetFeatureInfo(feature_id, response); +} + +std::tuple EssentialRoot::getVersion() +{ + std::vector params(0); + auto response = this->callFunction(Root::Function::Ping, params); + return std::make_tuple(response[0], response[1]); } \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/Root.h b/src/logid/backend/hidpp20/features/Root.h index b6f0fb2..9882aa0 100644 --- a/src/logid/backend/hidpp20/features/Root.h +++ b/src/logid/backend/hidpp20/features/Root.h @@ -2,6 +2,7 @@ #define LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H #include "../Feature.h" +#include "../EssentialFeature.h" #include "../feature_defs.h" namespace logid { @@ -24,7 +25,7 @@ namespace hidpp20 feature_info getFeature (uint16_t feature_id); std::tuple getVersion(); - private: + enum FeatureFlag : uint8_t { Obsolete = 1<<7, @@ -32,6 +33,18 @@ namespace hidpp20 Internal = 1<<5 }; }; + + class EssentialRoot : public EssentialFeature + { + public: + static const uint16_t ID = FeatureID::ROOT; + virtual uint16_t getID() { return ID; } + + EssentialRoot(hidpp::Device* device); + + feature_info getFeature (uint16_t feature_id); + std::tuple getVersion(); + }; }}} #endif //LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H \ No newline at end of file diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index ef553cb..00582d2 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -24,8 +24,8 @@ using namespace std::chrono; bool RawDevice::supportedReportID(uint8_t id) { - return (hidpp::ReportType::Short == id) || (hidpp::ReportType::Long == id) || - (dj::ReportType::Short == id) || (dj::ReportType::Long == id); + return (hidpp::ReportType::Short == id) || (hidpp::ReportType::Long == id) + || (dj::ReportType::Short == id) || (dj::ReportType::Long == id); } RawDevice::RawDevice(std::string path) : path (path), continue_listen (false) @@ -34,14 +34,16 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false) fd = ::open(path.c_str(), O_RDWR); if (fd == -1) - throw std::system_error(errno, std::system_category(), "RawDevice open failed"); + throw std::system_error(errno, std::system_category(), + "RawDevice open failed"); hidraw_devinfo devinfo{}; if (-1 == ::ioctl(fd, HIDIOCGRAWINFO, &devinfo)) { int err = errno; ::close(fd); - throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWINFO failed"); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRAWINFO failed"); } vid = devinfo.vendor; pid = devinfo.product; @@ -51,22 +53,25 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false) { int err = errno; ::close(fd); - throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWNAME failed"); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRAWNAME failed"); } - name.assign(name_buf, ret - 1); + _name.assign(name_buf, ret - 1); hidraw_report_descriptor _rdesc{}; if (-1 == ::ioctl(fd, HIDIOCGRDESCSIZE, &_rdesc.size)) { int err = errno; ::close(fd); - throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESCSIZE failed"); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRDESCSIZE failed"); } if (-1 == ::ioctl(fd, HIDIOCGRDESC, &_rdesc)) { int err = errno; ::close(fd); - throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESC failed"); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRDESC failed"); } rdesc = std::vector(_rdesc.value, _rdesc.value + _rdesc.size); @@ -74,7 +79,8 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false) { int err = errno; close(fd); - throw std::system_error(err, std::system_category(), "RawDevice pipe open failed"); + throw std::system_error(err, std::system_category(), + "RawDevice pipe open failed"); } continue_listen = false; @@ -92,8 +98,6 @@ RawDevice::~RawDevice() std::vector RawDevice::sendReport(const std::vector& report) { - assert(supportedReportID(report[0])); - /* If the listener will stop, handle I/O manually. * Otherwise, push to queue and wait for result. */ if(continue_listen) @@ -189,11 +193,6 @@ std::vector RawDevice::_respondToReport int RawDevice::_sendReport(const std::vector& report) { - std::lock_guard lock(dev_io); - int ret = ::write(fd, report.data(), report.size()); - if(ret == -1) - throw std::system_error(errno, std::system_category(), "_sendReport write failed"); - if(logid::global_verbosity == LogLevel::RAWREPORT) { printf("[RAWREPORT] %s OUT: ", path.c_str()); for(auto &i : report) @@ -201,6 +200,14 @@ int RawDevice::_sendReport(const std::vector& report) printf("\n"); } + assert(supportedReportID(report[0])); + + std::lock_guard lock(dev_io); + int ret = ::write(fd, report.data(), report.size()); + if(ret == -1) + throw std::system_error(errno, std::system_category(), + "_sendReport write failed"); + return ret; } @@ -225,13 +232,15 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng } while(ret == -1 && errno == EINTR); if(ret == -1) - throw std::system_error(errno, std::system_category(), "_readReport select failed"); + throw std::system_error(errno, std::system_category(), + "_readReport select failed"); if(FD_ISSET(fd, &fds)) { ret = read(fd, report.data(), report.size()); if(ret == -1) - throw std::system_error(errno, std::system_category(), "_readReport read failed"); + throw std::system_error(errno, std::system_category(), + "_readReport read failed"); report.resize(ret); } @@ -240,7 +249,8 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng char c; ret = read(dev_pipe[0], &c, sizeof(char)); if(ret == -1) - throw std::system_error(errno, std::system_category(), "_readReport read pipe failed"); + throw std::system_error(errno, std::system_category(), + "_readReport read pipe failed"); } if(0 == ret) @@ -260,7 +270,8 @@ void RawDevice::interruptRead() { char c = 0; if(-1 == write(dev_pipe[1], &c, sizeof(char))) - throw std::system_error(errno, std::system_category(), "interruptRead write pipe failed"); + throw std::system_error(errno, std::system_category(), + "interruptRead write pipe failed"); // Ensure I/O has halted std::lock_guard lock(dev_io); @@ -302,7 +313,8 @@ void RawDevice::stopListener() interruptRead(); } -void RawDevice::addEventHandler(const std::string& nickname, RawEventHandler& handler) +void RawDevice::addEventHandler(const std::string& nickname, + const std::shared_ptr& handler) { auto it = event_handlers.find(nickname); assert(it == event_handlers.end()); @@ -314,11 +326,17 @@ void RawDevice::removeEventHandler(const std::string &nickname) event_handlers.erase(nickname); } +const std::map>& +RawDevice::eventHandlers() +{ + return event_handlers; +} + void RawDevice::handleEvent(std::vector &report) { for(auto& handler : event_handlers) - if(handler.second.condition(report)) - handler.second.callback(report); + if(handler.second->condition(report)) + handler.second->callback(report); } bool RawDevice::isListening() @@ -328,5 +346,5 @@ bool RawDevice::isListening() if(ret) listening.unlock(); - return ret; + return !ret; } diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index 1eebeb0..e03b800 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -23,9 +23,14 @@ namespace raw public: static bool supportedReportID(uint8_t id); - RawDevice(std::string path); + explicit RawDevice(std::string path); ~RawDevice(); std::string hidrawPath() const { return path; } + + std::string name() const { return _name; } + uint16_t vendorId() const { return vid; } + uint16_t productId() const { return pid; } + std::vector reportDescriptor() const { return rdesc; } std::vector sendReport(const std::vector& report); @@ -36,8 +41,11 @@ namespace raw void stopListener(); bool isListening(); - void addEventHandler(const std::string& nickname, RawEventHandler& handler); + void addEventHandler(const std::string& nickname, + const std::shared_ptr& handler); void removeEventHandler(const std::string& nickname); + const std::map>& + eventHandlers(); private: std::mutex dev_io, listening; @@ -46,12 +54,13 @@ namespace raw int dev_pipe[2]; uint16_t vid; uint16_t pid; - std::string name; + std::string _name; std::vector rdesc; std::atomic continue_listen; - std::map event_handlers; + std::map> + event_handlers; void handleEvent(std::vector& report); /* These will only be used internally and processed with a queue */