diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 73ecfee7..20b679d0 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -184,7 +184,6 @@ void Device::sleep() { void Device::wakeup() { std::lock_guard lock(_state_lock); - logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index); reconfigure(); @@ -192,6 +191,8 @@ void Device::wakeup() { _awake = true; _ipc_interface->notifyStatus(); } + + logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index); } void Device::reconfigure() { diff --git a/src/logid/backend/EventHandlerList.h b/src/logid/backend/EventHandlerList.h index edad88c7..be671438 100644 --- a/src/logid/backend/EventHandlerList.h +++ b/src/logid/backend/EventHandlerList.h @@ -24,21 +24,64 @@ #include #include #include +#include template class EventHandlerLock; template -struct EventHandlerList { - typedef std::list list_t; - typedef typename list_t::const_iterator iterator_t; +class EventHandlerList { +public: + typedef std::list> list_t; + typedef typename list_t::iterator iterator_t; +private: + list_t list; + std::shared_mutex mutex; + std::shared_mutex add_mutex; + + void cleanup() { + std::unique_lock lock(mutex, std::try_to_lock); + if (lock.owns_lock()) { + std::list to_remove; + for (auto it = list.begin(); it != list.end(); ++it) { + if (!it->second) + to_remove.push_back(it); + } - std::list list; - mutable std::shared_mutex mutex; + for(auto& it : to_remove) + list.erase(it); + } + } +public: + iterator_t add(typename T::EventHandler handler) { + std::unique_lock add_lock(add_mutex); + list.emplace_front(std::move(handler), true); + return list.begin(); + } void remove(iterator_t iterator) { - std::unique_lock lock(mutex); - list.erase(iterator); + std::unique_lock lock(mutex, std::try_to_lock); + if (lock.owns_lock()) { + std::unique_lock add_lock(add_mutex); + list.erase(iterator); + } else { + iterator->second = false; + } + } + + template + void run_all(Arg arg) { + cleanup(); + std::shared_lock lock(mutex); + std::shared_lock add_lock(add_mutex); + for (auto& handler : list) { + add_lock.unlock(); + if (handler.second) { + if (handler.first.condition(arg)) + handler.first.callback(arg); + } + add_lock.lock(); + } } }; diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index f0da17c5..1faee337 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -114,35 +114,8 @@ void Device::_setupReportsAndInit() { /* hid_logitech_dj creates virtual /dev/hidraw nodes for receiver * devices. We should ignore these devices. */ - if (_index == hidpp::DefaultDevice) { - _raw_handler = _raw_device->addEventHandler( - {[](const std::vector& report) -> bool { - return (report[Offset::Type] == Report::Type::Short || - report[Offset::Type] == Report::Type::Long); - }, - [self_weak = _self](const std::vector& report) -> void { - Report _report(report); - if(auto self = self_weak.lock()) - self->handleEvent(_report); - }}); - - try { - auto rsp = sendReport({ReportType::Short, _index, - hidpp20::FeatureID::ROOT, hidpp20::Root::Ping, - hidpp::softwareID}); - if (rsp.deviceIndex() != _index) - throw InvalidDevice(InvalidDevice::VirtualNode); - } catch (hidpp10::Error& e) { - if (e.deviceIndex() != _index) - throw InvalidDevice(InvalidDevice::VirtualNode); - } catch (hidpp20::Error& e) { - /* This shouldn't happen, the device must not be ready */ - if (e.deviceIndex() != _index) - throw InvalidDevice(InvalidDevice::VirtualNode); - else - throw DeviceNotReady(); - } - } + if (_raw_device->isSubDevice()) + throw InvalidDevice(InvalidDevice::VirtualNode); _raw_handler = _raw_device->addEventHandler( {[index = _index]( @@ -214,25 +187,21 @@ void Device::_init() { } EventHandlerLock Device::addEventHandler(EventHandler handler) { - std::unique_lock lock(_event_handlers->mutex); - _event_handlers->list.emplace_front(std::move(handler)); - return {_event_handlers, _event_handlers->list.cbegin()}; + return {_event_handlers, _event_handlers->add(std::move(handler))}; } void Device::handleEvent(Report& report) { if (responseReport(report)) return; - std::shared_lock lock(_event_handlers->mutex); - for (auto& handler: _event_handlers->list) - if (handler.condition(report)) - handler.callback(report); + _event_handlers->run_all(report); } Report Device::sendReport(const Report& report) { /* Must complete transaction before next send */ std::lock_guard send_lock(_send_mutex); _sent_sub_id = report.subId(); + _sent_address = report.address(); std::unique_lock lock(_response_mutex); _sendReport(report); @@ -249,6 +218,7 @@ Report Device::sendReport(const Report& report) { Response response = _response.value(); _response.reset(); _sent_sub_id.reset(); + _sent_address.reset(); if (std::holds_alternative(response)) { return std::get(response); @@ -268,20 +238,23 @@ bool Device::responseReport(const Report& report) { std::lock_guard lock(_response_mutex); Response response = report; uint8_t sub_id; + uint8_t address; Report::Hidpp10Error hidpp10_error{}; Report::Hidpp20Error hidpp20_error{}; if (report.isError10(hidpp10_error)) { sub_id = hidpp10_error.sub_id; + address = hidpp10_error.address; response = hidpp10_error; } else if (report.isError20(hidpp20_error)) { sub_id = hidpp20_error.feature_index; - response = hidpp20_error; + address = (hidpp20_error.function << 4) | (hidpp20_error.software_id & 0x0f); } else { sub_id = report.subId(); + address = report.address(); } - if (sub_id == _sent_sub_id) { + if (sub_id == _sent_sub_id && address == _sent_address) { _response = response; _response_cv.notify_all(); return true; diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 838b9d67..38997c0f 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -158,6 +158,7 @@ namespace logid::backend::hidpp { std::optional _response; std::optional _sent_sub_id{}; + std::optional _sent_address{}; std::shared_ptr> _event_handlers; diff --git a/src/logid/backend/hidpp/defs.h b/src/logid/backend/hidpp/defs.h index 253f3c00..da15c1cf 100644 --- a/src/logid/backend/hidpp/defs.h +++ b/src/logid/backend/hidpp/defs.h @@ -42,7 +42,7 @@ namespace logid::backend::hidpp { static constexpr uint8_t softwareID = 2; /* For sending reports with no response, use a different SW ID */ - static constexpr uint8_t noAckSoftwareID = 1; + static constexpr uint8_t noAckSoftwareID = 3; static constexpr std::size_t ShortParamLength = 3; static constexpr std::size_t LongParamLength = 16; diff --git a/src/logid/backend/hidpp10/Device.cpp b/src/logid/backend/hidpp10/Device.cpp index f2972c99..f6611077 100644 --- a/src/logid/backend/hidpp10/Device.cpp +++ b/src/logid/backend/hidpp10/Device.cpp @@ -24,6 +24,23 @@ using namespace logid::backend; using namespace logid::backend::hidpp10; +hidpp::Report setupRegReport(hidpp::DeviceIndex index, + uint8_t sub_id, uint8_t address, + const std::vector& params) { + hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? + hidpp::Report::Type::Short : hidpp::Report::Type::Long; + + if (sub_id == SetRegisterLong) { + // When setting a long register, the report must be long. + type = hidpp::Report::Type::Long; + } + + hidpp::Report request(type, index, sub_id, address); + std::copy(params.begin(), params.end(), request.paramBegin()); + + return request; +} + Device::Device(const std::string& path, hidpp::DeviceIndex index, const std::shared_ptr& monitor, double timeout) : hidpp::Device(path, index, monitor, timeout) { @@ -124,22 +141,24 @@ std::vector Device::setRegister(uint8_t address, return accessRegister(sub_id, address, params); } -std::vector Device::accessRegister(uint8_t sub_id, uint8_t address, - const std::vector& params) { - hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? - hidpp::Report::Type::Short : hidpp::Report::Type::Long; +void Device::setRegisterNoResponse(uint8_t address, + const std::vector& params, + hidpp::Report::Type type) { + assert(params.size() <= hidpp::LongParamLength); - if (sub_id == SetRegisterLong) { - // When setting a long register, the report must be long. - type = hidpp::Report::Type::Long; - } + uint8_t sub_id = type == hidpp::Report::Type::Short ? + SetRegisterShort : SetRegisterLong; - hidpp::Report request(type, deviceIndex(), sub_id, address); - std::copy(params.begin(), params.end(), request.paramBegin()); + return accessRegisterNoResponse(sub_id, address, params); +} - auto response = sendReport(request); +std::vector Device::accessRegister(uint8_t sub_id, uint8_t address, + const std::vector& params) { + auto response = sendReport(setupRegReport(deviceIndex(), sub_id, address, params)); return {response.paramBegin(), response.paramEnd()}; } - - +void Device::accessRegisterNoResponse(uint8_t sub_id, uint8_t address, + const std::vector& params) { + sendReportNoACK(setupRegReport(deviceIndex(), sub_id, address, params)); +} diff --git a/src/logid/backend/hidpp10/Device.h b/src/logid/backend/hidpp10/Device.h index 5ae3fd68..0876d549 100644 --- a/src/logid/backend/hidpp10/Device.h +++ b/src/logid/backend/hidpp10/Device.h @@ -39,6 +39,9 @@ namespace logid::backend::hidpp10 { const std::vector& params, hidpp::Report::Type type); + void setRegisterNoResponse(uint8_t address, const std::vector& params, + hidpp::Report::Type type); + protected: Device(const std::string& path, hidpp::DeviceIndex index, const std::shared_ptr& monitor, double timeout); @@ -53,18 +56,24 @@ namespace logid::backend::hidpp10 { private: typedef std::variant Response; + struct ResponseSlot { std::optional response; std::optional sub_id; + void reset(); }; + std::array _responses; std::vector accessRegister( uint8_t sub_id, uint8_t address, const std::vector& params); + void accessRegisterNoResponse( + uint8_t sub_id, uint8_t address, const std::vector& params); + protected: - template + template static std::shared_ptr makeDerived(Args... args) { auto device = hidpp::Device::makeDerived(std::forward(args)...); @@ -73,8 +82,9 @@ namespace logid::backend::hidpp10 { return device; } + public: - template + template static std::shared_ptr make(Args... args) { return makeDerived(std::forward(args)...); } diff --git a/src/logid/backend/hidpp10/Receiver.cpp b/src/logid/backend/hidpp10/Receiver.cpp index 65ee05cc..9cefc760 100644 --- a/src/logid/backend/hidpp10/Receiver.cpp +++ b/src/logid/backend/hidpp10/Receiver.cpp @@ -68,7 +68,7 @@ void Receiver::setNotifications(NotificationFlags flags) { } void Receiver::enumerate() { - setRegister(ConnectionState, {2}, hidpp::ReportType::Short); + setRegisterNoResponse(ConnectionState, {2}, hidpp::ReportType::Short); } ///TODO: Investigate usage diff --git a/src/logid/backend/hidpp10/ReceiverMonitor.cpp b/src/logid/backend/hidpp10/ReceiverMonitor.cpp index 9faef84e..026f296c 100644 --- a/src/logid/backend/hidpp10/ReceiverMonitor.cpp +++ b/src/logid/backend/hidpp10/ReceiverMonitor.cpp @@ -158,28 +158,31 @@ void ReceiverMonitor::enumerate() { } void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) { - auto handler_id = std::make_shared>(); - - *handler_id = _receiver->rawDevice()->addEventHandler( - {[index](const std::vector& report) -> bool { - return report[Offset::DeviceIndex] == index; - }, - [self_weak = _self, index, handler_id]( - [[maybe_unused]] const std::vector& report) { - hidpp::DeviceConnectionEvent event{}; - event.withPayload = false; - event.linkEstablished = true; - event.index = index; - event.fromTimeoutCheck = true; - - run_task([self_weak, event, handler_id]() { - *handler_id = {}; - if (auto self = self_weak.lock()) { - self->_addHandler(event); - } - }); - } - }); + const std::lock_guard lock(_wait_mutex); + if (!_waiters.count(index)) { + _waiters.emplace(index, _receiver->rawDevice()->addEventHandler( + {[index](const std::vector& report) -> bool { + /* Connection events should be handled by connect_ev_handler */ + auto sub_id = report[Offset::SubID]; + return report[Offset::DeviceIndex] == index && + sub_id != Receiver::DeviceConnection && + sub_id != Receiver::DeviceDisconnection; + }, + [self_weak = _self, index]( + [[maybe_unused]] const std::vector& report) { + hidpp::DeviceConnectionEvent event{}; + event.withPayload = false; + event.linkEstablished = true; + event.index = index; + event.fromTimeoutCheck = true; + + run_task([self_weak, event]() { + if (auto self = self_weak.lock()) + self->_addHandler(event); + }); + } + })); + } } std::shared_ptr ReceiverMonitor::receiver() const { @@ -217,6 +220,8 @@ void ReceiverMonitor::_addHandler(const hidpp::DeviceConnectionEvent& event, int auto device_path = _receiver->devicePath(); try { addDevice(event); + const std::lock_guard lock(_wait_mutex); + _waiters.erase(event.index); } catch (DeviceNotReady& e) { if (tries == max_tries) { logPrintf(WARN, "Failed to add device %s:%d after %d tries." diff --git a/src/logid/backend/hidpp10/ReceiverMonitor.h b/src/logid/backend/hidpp10/ReceiverMonitor.h index 32b6df65..095fa252 100644 --- a/src/logid/backend/hidpp10/ReceiverMonitor.h +++ b/src/logid/backend/hidpp10/ReceiverMonitor.h @@ -101,6 +101,9 @@ namespace logid::backend::hidpp10 { std::weak_ptr _self; + std::mutex _wait_mutex; + std::map> _waiters; + public: template static std::shared_ptr make(Args... args) { diff --git a/src/logid/backend/raw/IOMonitor.cpp b/src/logid/backend/raw/IOMonitor.cpp index 5c5d8fd8..39907afe 100644 --- a/src/logid/backend/raw/IOMonitor.cpp +++ b/src/logid/backend/raw/IOMonitor.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2022 PixlOne + * Copyright 2019-2023 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 @@ -17,6 +17,7 @@ */ #include #include +#include extern "C" { @@ -35,6 +36,55 @@ IOHandler::IOHandler(std::function r, error(std::move(err)) { } +class IOMonitor::io_lock { + std::optional> _lock; + IOMonitor* _io_monitor; + const uint64_t counter = 1; + +public: + explicit io_lock(IOMonitor* io_monitor) : _io_monitor(io_monitor) { + _io_monitor->_interrupting = true; + [[maybe_unused]] ssize_t ret = ::write(_io_monitor->_event_fd, &counter, sizeof(counter)); + assert(ret == sizeof(counter)); + _lock.emplace(_io_monitor->_run_mutex); + } + + io_lock(const io_lock&) = delete; + + io_lock& operator=(const io_lock&) = delete; + + io_lock(io_lock&& o) noexcept: _lock(std::move(o._lock)), _io_monitor(o._io_monitor) { + o._lock.reset(); + o._io_monitor = nullptr; + } + + io_lock& operator=(io_lock&& o) noexcept { + if (this != &o) { + _lock = std::move(o._lock); + _io_monitor = o._io_monitor; + o._lock.reset(); + o._io_monitor = nullptr; + } + + return *this; + } + + ~io_lock() noexcept { + if (_lock && _io_monitor) { + uint64_t buf{}; + [[maybe_unused]] const ssize_t ret = ::read( + _io_monitor->_event_fd, &buf, sizeof(counter)); + + assert(ret != -1); + + if (buf == counter) { + _io_monitor->_interrupting = false; + _io_monitor->_interrupt_cv.notify_one(); + } + } + } +}; + IOMonitor::IOMonitor() : _epoll_fd(epoll_create1(0)), _event_fd(eventfd(0, EFD_NONBLOCK)) { if (_epoll_fd < 0) { @@ -69,7 +119,6 @@ IOMonitor::IOMonitor() : _epoll_fd(epoll_create1(0)), } IOMonitor::~IOMonitor() noexcept { - std::lock_guard ctl_lock(_ctl_lock); _stop(); if (_event_fd >= 0) @@ -80,23 +129,21 @@ IOMonitor::~IOMonitor() noexcept { } void IOMonitor::_listen() { - std::lock_guard run_lock(_run_lock); + std::unique_lock lock(_run_mutex); std::vector events; _is_running = true; while (_is_running) { if (_interrupting) { - std::unique_lock lock(_interrupt_mutex); _interrupt_cv.wait(lock, [this]() { - return !(bool) _interrupting; + return !_interrupting; }); if (!_is_running) break; } - std::lock_guard io_lock(_io_lock); if (events.size() != _fds.size()) events.resize(_fds.size()); int ev_count = ::epoll_wait(_epoll_fd, events.data(), (int) events.size(), -1); @@ -113,76 +160,32 @@ void IOMonitor::_listen() { } void IOMonitor::_stop() noexcept { - _interrupt(); - _is_running = false; - _continue(); + { + [[maybe_unused]] const io_lock lock(this); + _is_running = false; + } _io_thread->join(); } -[[maybe_unused]] -bool IOMonitor::_running() const { - std::unique_lock run_lock(_run_lock, std::try_to_lock); - return !run_lock.owns_lock() || _is_running; -} - void IOMonitor::add(int fd, IOHandler handler) { - std::lock_guard lock(_ctl_lock); - _interrupt(); + [[maybe_unused]] const io_lock lock(this); struct epoll_event event{}; event.events = EPOLLIN | EPOLLHUP | EPOLLERR; event.data.fd = fd; // TODO: EPOLL_CTL_MOD - if (_fds.contains(fd)) { - _continue(); + if (_fds.contains(fd)) throw std::runtime_error("duplicate io fd"); - } - if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event)) { - _continue(); + if (::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event)) throw std::system_error(errno, std::generic_category()); - } _fds.emplace(fd, std::move(handler)); - - _continue(); } void IOMonitor::remove(int fd) noexcept { - std::lock_guard lock(_ctl_lock); - _interrupt(); - std::lock_guard io_lock(_io_lock); + [[maybe_unused]] const io_lock lock(this); ::epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr); _fds.erase(fd); - - _continue(); -} - -void IOMonitor::_interrupt() noexcept { - std::unique_lock run_lock(_run_lock, std::try_to_lock); - - _interrupting = true; - - uint64_t counter = 1; - [[maybe_unused]] ssize_t ret = ::write(_event_fd, &counter, sizeof(counter)); - assert(ret == sizeof(counter)); - - // Wait for the IO monitor to _stop - std::lock_guard io_lock(_io_lock); - -} - -void IOMonitor::_continue() noexcept { - std::unique_lock run_lock(_run_lock, std::try_to_lock); - - uint64_t counter; - [[maybe_unused]] ssize_t ret = ::read(_event_fd, &counter, sizeof(counter)); - - assert(ret != -1); - - if (counter == 1) { - _interrupting = false; - _interrupt_cv.notify_all(); - } -} +} \ No newline at end of file diff --git a/src/logid/backend/raw/IOMonitor.h b/src/logid/backend/raw/IOMonitor.h index 7fd86702..2ca44ed5 100644 --- a/src/logid/backend/raw/IOMonitor.h +++ b/src/logid/backend/raw/IOMonitor.h @@ -40,6 +40,14 @@ namespace logid::backend::raw { public: IOMonitor(); + IOMonitor(IOMonitor&&) = delete; + + IOMonitor(const IOMonitor&) = delete; + + IOMonitor& operator=(IOMonitor&&) = delete; + + IOMonitor& operator=(const IOMonitor&) = delete; + ~IOMonitor() noexcept; void add(int fd, IOHandler handler); @@ -50,26 +58,19 @@ namespace logid::backend::raw { void _listen(); // This is a blocking call void _stop() noexcept; - [[maybe_unused]] - [[nodiscard]] bool _running() const; - - void _interrupt() noexcept; - - void _continue() noexcept; - std::unique_ptr _io_thread; std::map _fds; - std::mutex _io_lock, _ctl_lock; - mutable std::mutex _run_lock; + mutable std::mutex _run_mutex; std::atomic_bool _is_running; std::atomic_bool _interrupting; - std::mutex _interrupt_mutex; std::condition_variable _interrupt_cv; const int _epoll_fd; const int _event_fd; + + class io_lock; }; } diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 213b5467..54e4c9af 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -24,6 +24,7 @@ #include #include #include +#include extern "C" { @@ -32,12 +33,17 @@ extern "C" #include #include #include +#include } using namespace logid::backend::raw; using namespace logid::backend; using namespace std::chrono; +static constexpr int max_write_tries = 8; + +static const std::regex virtual_path_regex(R"~((.*\/)(.*:)([0-9]+))~"); + int get_fd(const std::string& path) { int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK); if (fd == -1) @@ -56,7 +62,37 @@ RawDevice::dev_info get_dev_info(int fd) { "RawDevice HIDIOCGRAWINFO failed"); } - return {dev_info.vendor, dev_info.product}; + RawDevice::BusType type = RawDevice::OtherBus; + + switch (dev_info.bustype) { + case BUS_USB: + type = RawDevice::USB; + break; + case BUS_BLUETOOTH: + type = RawDevice::Bluetooth; + break; + default:; + } + + + return { + .vid = dev_info.vendor, + .pid = dev_info.product, + .bus_type = type + }; +} + +std::string get_phys(int fd) { + ssize_t len; + char buf[256]; + if (-1 == (len = ::ioctl(fd, HIDIOCGRAWPHYS(sizeof(buf)), buf))) { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRAWPHYS failed"); + } + + return {buf, static_cast(len) - 1}; } std::string get_name(int fd) { @@ -68,7 +104,7 @@ std::string get_name(int fd) { throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWNAME failed"); } - return {name_buf, static_cast(len)}; + return {name_buf, static_cast(len) - 1}; } RawDevice::RawDevice(std::string path, const std::shared_ptr& monitor) : @@ -76,6 +112,12 @@ RawDevice::RawDevice(std::string path, const std::shared_ptr& mon _dev_info(get_dev_info(_fd)), _name(get_name(_fd)), _report_desc(getReportDescriptor(_fd)), _io_monitor(monitor->ioMonitor()), _event_handlers(std::make_shared>()) { + + if (busType() == USB) { + auto phys = get_phys(_fd); + _sub_device = std::regex_match(phys, virtual_path_regex); + } + _io_monitor->add(_fd, { [this]() { _readReports(); }, [this]() { _valid = false; }, @@ -105,6 +147,14 @@ int16_t RawDevice::productId() const { return _dev_info.pid; } +RawDevice::BusType RawDevice::busType() const { + return _dev_info.bus_type; +} + +bool RawDevice::isSubDevice() const { + return _sub_device; +} + std::vector RawDevice::getReportDescriptor(const std::string& path) { int fd = ::open(path.c_str(), O_RDWR | O_NONBLOCK); if (fd == -1) @@ -150,15 +200,17 @@ void RawDevice::sendReport(const std::vector& report) { printf("\n"); } - if (write(_fd, report.data(), report.size()) == -1) - throw std::system_error(errno, std::system_category(), - "sendReport write failed"); + + for (int i = 0; i < max_write_tries && write(_fd, report.data(), report.size()) == -1; ++i) { + auto err = errno; + if (err != EPIPE) + throw std::system_error(err, std::system_category(), + "sendReport write failed"); + } } EventHandlerLock RawDevice::addEventHandler(RawEventHandler handler) { - std::unique_lock lock(_event_handlers->mutex); - _event_handlers->list.emplace_front(std::move(handler)); - return {_event_handlers, _event_handlers->list.cbegin()}; + return {_event_handlers, _event_handlers->add(std::forward(handler))}; } void RawDevice::_readReports() { @@ -181,8 +233,5 @@ void RawDevice::_readReports() { } void RawDevice::_handleEvent(const std::vector& report) { - std::shared_lock lock(_event_handlers->mutex); - for (auto& handler : _event_handlers->list) - if (handler.condition(report)) - handler.callback(report); + _event_handlers->run_all(report); } diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index 222cd124..f1669fbe 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -39,9 +39,16 @@ namespace logid::backend::raw { static constexpr int max_data_length = 32; typedef RawEventHandler EventHandler; + enum BusType { + USB, + Bluetooth, + OtherBus + }; + struct dev_info { int16_t vid; int16_t pid; + BusType bus_type; }; RawDevice(std::string path, const std::shared_ptr& monitor); @@ -57,6 +64,10 @@ namespace logid::backend::raw { [[nodiscard]] int16_t productId() const; + [[nodiscard]] BusType busType() const; + + [[nodiscard]] bool isSubDevice() const; + static std::vector getReportDescriptor(const std::string& path); static std::vector getReportDescriptor(int fd); @@ -80,6 +91,8 @@ namespace logid::backend::raw { std::shared_ptr _io_monitor; + bool _sub_device = false; + std::shared_ptr> _event_handlers; void _handleEvent(const std::vector& report); diff --git a/src/logid/features/RemapButton.cpp b/src/logid/features/RemapButton.cpp index d1f885ed..0d6dd9ab 100644 --- a/src/logid/features/RemapButton.cpp +++ b/src/logid/features/RemapButton.cpp @@ -63,7 +63,7 @@ RemapButton::RemapButton(Device* dev) : DeviceFeature(dev), if ((action->reprogFlags() & hidpp20::ReprogControls::RawXYDiverted) && (!_reprog_controls->supportsRawXY() || !(info.additionalFlags & hidpp20::ReprogControls::RawXY))) - logPrintf(WARN, "%s: Cannot divert raw XY movements for CID 0x%02x", + logPrintf(WARN, "%s: 'Cannot divert raw XY movements for CID 0x%02x", _device->name().c_str(), info.controlID); report.flags |= action->reprogFlags(); diff --git a/src/logid/util/task.cpp b/src/logid/util/task.cpp index fea2f5e2..009594dd 100644 --- a/src/logid/util/task.cpp +++ b/src/logid/util/task.cpp @@ -18,6 +18,7 @@ #include #include #include +#include using namespace logid; using namespace std::chrono; @@ -35,18 +36,38 @@ static std::priority_queue, task_less> tasks {}; static std::mutex task_mutex {}; static std::condition_variable task_cv {}; static std::atomic_bool workers_init = false; +static std::atomic_bool workers_run = false; -[[noreturn]] static void worker() { +void stop_workers() { std::unique_lock lock(task_mutex); - while (true) { - task_cv.wait(lock, []() { return !tasks.empty(); }); + if (workers_init) { + workers_run = false; + lock.unlock(); + task_cv.notify_all(); + + /* Wait for all workers to end */ + lock.lock(); + } +} + +void worker() { + std::unique_lock lock(task_mutex); + while (workers_run) { + task_cv.wait(lock, []() { return !tasks.empty() || !workers_run; }); + + if (!workers_run) + break; /* top task is in the future, wait */ if (tasks.top().time >= system_clock::now()) { auto wait = tasks.top().time - system_clock::now(); task_cv.wait_for(lock, wait, []() { - return !tasks.empty() && (tasks.top().time < system_clock::now()); + return (!tasks.empty() && (tasks.top().time < system_clock::now())) || + !workers_run; }); + + if (!workers_run) + break; } if (!tasks.empty()) { @@ -67,11 +88,15 @@ static std::atomic_bool workers_init = false; void logid::init_workers(int worker_count) { std::lock_guard lock(task_mutex); + assert(!workers_init); for (int i = 0; i < worker_count; ++i) std::thread(&worker).detach(); workers_init = true; + workers_run = true; + + atexit(&stop_workers); } void logid::run_task(std::function function) {