From ca04204a8c7c102215628565c2c5350f69099181 Mon Sep 17 00:00:00 2001 From: PixlOne <8843371+PixlOne@users.noreply.github.com> Date: Wed, 2 Oct 2019 18:41:10 -0400 Subject: [PATCH] Use receiver events to listen for devices --- src/logid/CMakeLists.txt | 7 +- src/logid/Device.cpp | 182 +++++++++++++++++++++++++++++-------- src/logid/Device.h | 12 ++- src/logid/DeviceFinder.cpp | 76 ++++++++++------ src/logid/DeviceFinder.h | 10 +- 5 files changed, 212 insertions(+), 75 deletions(-) diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 6a2b52f..0a2f6c4 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -35,9 +35,6 @@ find_path(EVDEV_INCLUDE_DIR libevdev/libevdev.h find_library(EVDEV_LIBRARY NAMES evdev libevdev) -include_directories(${HIDPP_INCLUDE_DIR}/hidpp ${EVDEV_INCLUDE_DIR}) - - if((NOT HIDPP_INCLUDE_DIR) OR (NOT EXISTS ${HIDPP_INCLUDE_DIR}) OR (NOT HIDPP_LIBRARY) OR FORCE_BUILD_HIDPP) message("Could not find libhidpp include dir, getting submodule") @@ -68,8 +65,12 @@ if((NOT HIDPP_INCLUDE_DIR) OR (NOT EXISTS ${HIDPP_INCLUDE_DIR}) OR (NOT HIDPP_LI set(HIDPP_INCLUDE_DIR "hidpp/src/libhidpp/") set(HIDPP_LIBRARY hidpp) +else() + set(HIDPP_INCLUDE_DIR ${HIDPP_INCLUDE_DIR}/hidpp) endif() +include_directories(${HIDPP_INCLUDE_DIR} ${EVDEV_INCLUDE_DIR}) + target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ ${HIDPP_LIBRARY}) install(TARGETS logid DESTINATION bin) diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 8213815..5b00304 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -20,26 +21,41 @@ #include "util.h" #include "EvdevDevice.h" - using namespace std::chrono_literals; Device::Device(std::string p, const HIDPP::DeviceIndex i) : path(std::move(p)), index (i) { - // Initialise variables - DeviceRemoved = false; + disconnected = true; dispatcher = new HIDPP::SimpleDispatcher(path.c_str()); - hidpp_dev = new HIDPP20::Device(dispatcher, index); + listener = new SimpleListener(new HIDPP::SimpleDispatcher(path.c_str()), index); + config = new DeviceConfig(); +} + +bool Device::init() +{ + // Initialise variables + disconnected = false; + try + { + hidpp_dev = new HIDPP20::Device(dispatcher, index); + } + catch(HIDPP10::Error &e) { return false; } + catch(HIDPP20::Error &e) { return false; } + name = hidpp_dev->name(); features = get_features(); - listener = new SimpleListener(new HIDPP::SimpleDispatcher(path.c_str()), index); - // Set config, if none is found for this device then use default if(global_config->devices.find(name) == global_config->devices.end()) - { log_printf(INFO, "Device %s not configured, using default config.", hidpp_dev->name().c_str()); - config = new DeviceConfig(); + else + { + delete(config); + config = global_config->devices.find(name)->second; } - else config = global_config->devices.find(name)->second; + + initialized = true; + + return true; } Device::~Device() @@ -55,32 +71,35 @@ void Device::configure() if(config->baseConfig) config = new DeviceConfig(config, this); - configuring = true; - usleep(250000); + if(!configuring.try_lock()) + { + log_printf(DEBUG, "%s %d: skip config task", path.c_str(), index); + return; + } + usleep(500000); try { - if(disconnected) { - configuring = false; return; } - + if(disconnected) + goto ret; // Divert buttons divert_buttons(); - if(disconnected) { - configuring = false; return; } - // Set DPI if it is set + if(disconnected) + goto ret; + // Set DPI if it is configured if(config->dpi != nullptr) set_dpi(*config->dpi); - if(disconnected) { - configuring = false; return; } - // Set Smartshift if it is set + if(disconnected) + goto ret; + // Set Smartshift if it is configured if(config->smartshift != nullptr) set_smartshift(*config->smartshift); - if(disconnected) { - configuring = false; return; } - // Set Hires Scroll if it is set + if(disconnected) + goto ret; + // Set Hires Scroll if it is configured if(config->hiresscroll != nullptr) set_hiresscroll(*config->hiresscroll); } @@ -89,7 +108,8 @@ void Device::configure() log_printf(ERROR, "HID++ 1.0 Error whjle configuring %s: %s", name.c_str(), e.what()); } - configuring = false; +ret: + configuring.unlock(); } void Device::reset() @@ -111,11 +131,13 @@ void Device::divert_buttons() try { HIDPP20::IReprogControls irc = HIDPP20::IReprogControls::auto_version(hidpp_dev); - if(disconnected) return; + if(disconnected) + return; int controlCount = irc.getControlCount(); for(int i = 0; i < controlCount; i++) { - if(disconnected) return; + if(disconnected) + return; uint16_t cid = irc.getControlInfo(i).control_id; uint8_t flags = 0; flags |= HIDPP20::IReprogControls::ChangeTemporaryDivert; @@ -129,7 +151,8 @@ void Device::divert_buttons() if(action->second->type == Action::Gestures) flags |= HIDPP20::IReprogControls::RawXYDiverted; } - if(disconnected) return; + if(disconnected) + return; irc.setControlReporting(cid, flags, cid); } } @@ -190,14 +213,36 @@ void Device::set_dpi(int dpi) catch (HIDPP20::Error &e) { log_printf(ERROR, "Error while setting DPI: %s", e.what()); } } +void Device::wait_for_receiver() +{ + while(true) + { + waiting_for_receiver = true; + listener->addEventHandler(std::make_unique(this)); + listener->start(); + // Listener stopped, check if stopped or ReceiverHandler event + if (waiting_for_receiver) + return; + + usleep(200000); + + if(this->init()) break; + + log_printf(ERROR, "Failed to initialize device %d on %s, waiting for receiver"); + delete(listener); + listener = new SimpleListener(new HIDPP::SimpleDispatcher(path.c_str()), index); + } + log_printf(INFO, "%s detected: device %d on %s", name.c_str(), index, path.c_str()); + this->start(); +} + void Device::start() { configure(); try { listener->addEventHandler(std::make_unique(this)); } catch(HIDPP20::UnsupportedFeature &e) { } - if(index != HIDPP::DefaultDevice && index != HIDPP::CordedDevice) - listener->addEventHandler( std::make_unique(this) ); - else + + if(index == HIDPP::DefaultDevice || index == HIDPP::CordedDevice) { try { listener->addEventHandler( std::make_unique(this) ); } catch(HIDPP20::UnsupportedFeature &e) { } @@ -205,6 +250,31 @@ void Device::start() listener->start(); } +bool Device::testConnection() +{ + int i = MAX_CONNECTION_TRIES; + do { + try + { + HIDPP20::Device _hpp20dev(dispatcher, index); + return true; + } + catch(HIDPP10::Error &e) + { + if(e.errorCode() == HIDPP10::Error::ResourceError) // Asleep, wait for next event + return false; + if(i == MAX_CONNECTION_TRIES-1) + return false; + } + catch(std::exception &e) + { + if(i == MAX_CONNECTION_TRIES-1) + return false; + } + i++; + } while(i < MAX_CONNECTION_TRIES); +} + void ButtonHandler::handleEvent (const HIDPP::Report &event) { switch (event.function()) @@ -254,11 +324,26 @@ void ReceiverHandler::handleEvent(const HIDPP::Report &event) { case HIDPP10::IReceiver::DeviceUnpaired: { - finder->stopAndDeleteDevice(dev->path, event.deviceIndex()); + log_printf(INFO, "%s (Device %d on %s) unpaired from receiver", dev->name.c_str(), dev->index, dev->path.c_str()); + std::thread {[=]() + { + finder->stopAndDeleteDevice(dev->path, dev->index); + finder->insertNewReceiverDevice(dev->path, dev->index); + }}.detach(); break; } case HIDPP10::IReceiver::DevicePaired: + { + log_printf(DEBUG, "Receiver on %s: Device %d paired", dev->path.c_str(), event.deviceIndex()); + if(dev->waiting_for_receiver) + { + if(!dev->testConnection()) return; + dev->waiting_for_receiver = false; + dev->stop(); + } + //else: Likely an enumeration event, ignore. break; + } case HIDPP10::IReceiver::ConnectionStatus: { auto status = HIDPP10::IReceiver::connectionStatusEvent(event); @@ -269,9 +354,20 @@ void ReceiverHandler::handleEvent(const HIDPP::Report &event) } else if (status == HIDPP10::IReceiver::ConnectionEstablished) { - log_printf(INFO, "Connection established to %s", dev->name.c_str()); - dev->disconnected = false; - dev->configure(); + if(dev->waiting_for_receiver) + { + log_printf(DEBUG, "Receiver on %s: Connection established to device %d", dev->path.c_str(), event.deviceIndex()); + if(!dev->testConnection()) return; + dev->waiting_for_receiver = false; + std::thread { [=]() { dev->stop(); } }.detach(); + } + else + { + if(!dev->initialized) return; + dev->disconnected = false; + dev->configure(); + log_printf(INFO, "Connection established to %s", dev->name.c_str()); + } } break; } @@ -328,12 +424,23 @@ void EventListener::addEventHandler(std::unique_ptr &&handler) void SimpleListener::start() { - try { dispatcher->listen(); } - catch(std::system_error &e) { } + bool retry; + do + { + retry = false; + try { dispatcher->listen(); } + catch(std::system_error &e) + { + retry = true; + usleep(250000); + } + } while(retry && !stopped); + } void SimpleListener::stop() { + this->stopped = true; dispatcher->stop(); } @@ -373,10 +480,7 @@ void Device::move_diverted(uint16_t cid, HIDPP20::IReprogControlsV4::Move m) { auto action = config->actions.find(cid); if(action == config->actions.end()) - { - log_printf(DEBUG, "0x%x's RawXY was diverted with no action.", cid); return; - } switch(action->second->type) { case Action::Gestures: diff --git a/src/logid/Device.h b/src/logid/Device.h index 3311513..438a645 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,7 @@ public: std::string name; + bool init(); void configure(); void reset(); @@ -30,8 +32,10 @@ public: void release_button(uint16_t cid); void move_diverted(uint16_t cid, HIDPP20::IReprogControlsV4::Move move); + void wait_for_receiver(); void start(); void stop(); + bool testConnection(); std::map get_features(); @@ -42,12 +46,13 @@ public: HIDPP::Dispatcher* dispatcher; HIDPP20::Device* hidpp_dev; - bool configuring = false; - bool disconnected = false; + std::mutex configuring; + std::atomic_bool disconnected; + bool initialized = false; + bool waiting_for_receiver = false; protected: DeviceConfig* config; - bool DeviceRemoved; EventListener* listener; void divert_buttons(); @@ -141,6 +146,7 @@ public: { } + bool stopped = false; virtual void start(); virtual void stop(); diff --git a/src/logid/DeviceFinder.cpp b/src/logid/DeviceFinder.cpp index 857c97d..1127157 100644 --- a/src/logid/DeviceFinder.cpp +++ b/src/logid/DeviceFinder.cpp @@ -15,12 +15,13 @@ #include "util.h" #include "Device.h" -#define MAX_CONNECTION_TRIES 10 -#define TIME_BETWEEN_CONNECTION_TRIES 1s +#define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corderd" void stopAndDeleteConnectedDevice (ConnectedDevice &connected_device) { - log_printf(INFO, "%s (Device %d on %s) disconnected", connected_device.device->name.c_str(), connected_device.device->index, connected_device.device->path.c_str()); + if(!connected_device.device->waiting_for_receiver) + log_printf(INFO, "%s (Device %d on %s) disconnected", connected_device.device->name.c_str(), + connected_device.device->index, connected_device.device->path.c_str()); connected_device.device->stop(); connected_device.associatedThread.join(); delete(connected_device.device); @@ -37,9 +38,10 @@ DeviceFinder::~DeviceFinder() this->devices_mutex.unlock(); } -void DeviceFinder::insertNewDevice(const std::string &path, HIDPP::DeviceIndex index) +Device* DeviceFinder::insertNewDevice(const std::string &path, HIDPP::DeviceIndex index) { - Device *device = new Device(path, index); + auto device = new Device(path, index); + device->init(); this->devices_mutex.lock(); log_printf(INFO, "%s detected: device %d on %s", device->name.c_str(), index, path.c_str()); @@ -51,6 +53,25 @@ void DeviceFinder::insertNewDevice(const std::string &path, HIDPP::DeviceIndex i }) }); this->devices_mutex.unlock(); + + return device; +} + +Device* DeviceFinder::insertNewReceiverDevice(const std::string &path, HIDPP::DeviceIndex index) +{ + auto *device = new Device(path, index); + + this->devices_mutex.lock(); + auto path_bucket = this->devices.emplace(path, std::map()).first; + path_bucket->second.emplace(index, ConnectedDevice{ + device, + std::thread([device]() { + device->wait_for_receiver(); + }) + }); + this->devices_mutex.unlock(); + + return device; } void DeviceFinder::stopAndDeleteAllDevicesIn (const std::string &path) @@ -65,8 +86,6 @@ void DeviceFinder::stopAndDeleteAllDevicesIn (const std::string &path) this->devices.erase(path_bucket); } this->devices_mutex.unlock(); - - log_printf(WARN, "Attempted to disconnect not previously connected devices on %s", path.c_str()); } void DeviceFinder::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index) @@ -86,7 +105,6 @@ void DeviceFinder::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIn log_printf(WARN, "Attempted to disconnect not previously connected device %d on %s", index, path.c_str()); } - void DeviceFinder::addDevice(const char *path) { @@ -96,22 +114,12 @@ void DeviceFinder::addDevice(const char *path) // Asynchronously scan device std::thread{[=]() { - //Check if device is an HID++ device and handle it accordingly try { HIDPP::SimpleDispatcher dispatcher(string_path.c_str()); - bool has_receiver_index = false; - for(HIDPP::DeviceIndex index: { - HIDPP::DefaultDevice, HIDPP::CordedDevice, - HIDPP::WirelessDevice1, HIDPP::WirelessDevice2, - HIDPP::WirelessDevice3, HIDPP::WirelessDevice4, - HIDPP::WirelessDevice5, HIDPP::WirelessDevice6}) + for(HIDPP::DeviceIndex index: { HIDPP::DefaultDevice, HIDPP::CordedDevice }) { - - if(!has_receiver_index && index == HIDPP::WirelessDevice1) - break; - bool device_not_connected = true; bool device_unknown = false; int remaining_tries = MAX_CONNECTION_TRIES; @@ -123,7 +131,17 @@ void DeviceFinder::addDevice(const char *path) uint major, minor; std::tie(major, minor) = version; if(index == HIDPP::DefaultDevice && version == std::make_tuple(1, 0)) - has_receiver_index = true; + { + HIDPP10::Device receiver(&dispatcher, index); + HIDPP10::IReceiver irecv(&receiver); + log_printf(INFO, "Found %s on %s", receiver.name().c_str(), string_path.c_str()); + for(HIDPP::DeviceIndex recv_index : { HIDPP::WirelessDevice1, HIDPP::WirelessDevice2, + HIDPP::WirelessDevice3, HIDPP::WirelessDevice4, + HIDPP::WirelessDevice5, HIDPP::WirelessDevice6 }) + this->insertNewReceiverDevice(string_path, recv_index); + irecv.getPairedDevices(); + return; + } if(major > 1) // HID++ 2.0 devices only { this->insertNewDevice(string_path, index); @@ -136,14 +154,14 @@ void DeviceFinder::addDevice(const char *path) { if(remaining_tries == 1) { - log_printf(WARN, "While querying %s (possibly asleep), wireless device %d: %s", string_path.c_str(), index, e.what()); + log_printf(WARN, "While querying %s (possibly asleep), %s device: %s", string_path.c_str(), NON_WIRELESS_DEV(index), e.what()); remaining_tries += MAX_CONNECTION_TRIES; // asleep devices may raise a resource error, so do not count this try - } + } } else if(e.errorCode() != HIDPP10::Error::UnknownDevice) { if(remaining_tries == 1) - log_printf(ERROR, "While querying %s, wireless device %d: %s", string_path.c_str(), index, e.what()); + log_printf(ERROR, "While querying %s, %s device: %s", string_path.c_str(), NON_WIRELESS_DEV(index), e.what()); } else device_unknown = true; } @@ -152,30 +170,32 @@ void DeviceFinder::addDevice(const char *path) if(e.errorCode() != HIDPP20::Error::UnknownDevice) { if(remaining_tries == 1) - log_printf(ERROR, "Error while querying %s, device %d: %s", string_path.c_str(), index, e.what()); + log_printf(ERROR, "Error while querying %s, device %d: %s", string_path.c_str(), NON_WIRELESS_DEV(index), e.what()); } else device_unknown = true; } catch(HIDPP::Dispatcher::TimeoutError &e) { if(remaining_tries == 1) - log_printf(ERROR, "Device %s (index %d) timed out.", string_path.c_str(), index); + { + log_printf(ERROR, "Time out on %s device: %s (possibly asleep)", NON_WIRELESS_DEV(index), string_path.c_str()); + remaining_tries += MAX_CONNECTION_TRIES; // asleep devices may raise a timeout error, so do not count this try + } } catch(std::runtime_error &e) { if(remaining_tries == 1) - log_printf(ERROR, "Runtime error on device %d on %s: %s", index, string_path.c_str(), e.what()); + log_printf(ERROR, "Runtime error on %s device on %s: %s", NON_WIRELESS_DEV(index), string_path.c_str(), e.what()); } remaining_tries--; std::this_thread::sleep_for(TIME_BETWEEN_CONNECTION_TRIES); } while (device_not_connected && !device_unknown && remaining_tries > 0); - } + } } catch(HIDPP::Dispatcher::NoHIDPPReportException &e) { } catch(std::system_error &e) { log_printf(WARN, "Failed to open %s: %s", string_path.c_str(), e.what()); } - }}.detach(); } diff --git a/src/logid/DeviceFinder.h b/src/logid/DeviceFinder.h index 8a1d24b..06eb675 100644 --- a/src/logid/DeviceFinder.h +++ b/src/logid/DeviceFinder.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef DEVICEFINDER_H +#define DEVICEFINDER_H #include #include @@ -11,6 +12,9 @@ #include "Device.h" +#define MAX_CONNECTION_TRIES 10 +#define TIME_BETWEEN_CONNECTION_TRIES 500ms + class Device; struct ConnectedDevice { @@ -23,7 +27,8 @@ class DeviceFinder : public HID::DeviceMonitor public: ~DeviceFinder(); - void insertNewDevice (const std::string &path, HIDPP::DeviceIndex index); + Device* insertNewDevice (const std::string &path, HIDPP::DeviceIndex index); + Device* insertNewReceiverDevice (const std::string &path, HIDPP::DeviceIndex index); void stopAndDeleteAllDevicesIn (const std::string &path); void stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index); protected: @@ -36,3 +41,4 @@ private: extern DeviceFinder* finder; +#endif //DEVICEFINDER_H \ No newline at end of file