From 6e7445b1978e961bed7a709233043b414f82ee95 Mon Sep 17 00:00:00 2001 From: PixlOne <8843371+PixlOne@users.noreply.github.com> Date: Fri, 10 Apr 2020 20:53:13 -0400 Subject: [PATCH 01/81] Add versioning to logiops binaries --- CMakeLists.txt | 37 +++++++++++++++++++++++++++++++++++++ src/logid/logid.cpp | 25 ++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 22658b3..a1d9fd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,4 +11,41 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -Wall -Wextra") set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) +find_package(Git) + +# Set version number +if(EXISTS ${CMAKE_SOURCE_DIR}/version.txt) + file(READ version.txt LOGIOPS_VERSION) + string(REGEX REPLACE "\n$" "" LOGIOPS_VERSION ${LOGIOPS_VERSION}) +endif() + +if(GIT_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/.git) + execute_process(COMMAND ${GIT_EXECUTABLE} + rev-parse --abbrev-ref HEAD + OUTPUT_VARIABLE LOGIOPS_GIT_BRANCH) + string(REGEX REPLACE "\n$" "" LOGIOPS_GIT_BRANCH ${LOGIOPS_GIT_BRANCH}) + if(LOGIOPS_GIT_BRANCH MATCHES "^tags/?") + STRING(REGEX REPLACE "^tags/" "" + LOGIOPS_VERSION ${LOGIOPS_GIT_BRANCH}) + else() + execute_process(COMMAND ${GIT_EXECUTABLE} + rev-parse --short HEAD + OUTPUT_VARIABLE LOGIOPS_COMMIT_HASH) + string(REGEX REPLACE "\n$" "" LOGIOPS_COMMIT_HASH ${LOGIOPS_COMMIT_HASH}) + if(LOGIOPS_VERSION) + string(APPEND LOGIOPS_VERSION -${LOGIOPS_COMMIT_HASH}) + else() + set(LOGIOPS_VERSION git-${LOGIOPS_COMMIT_HASH}) + endif() + endif() +endif() + +if(LOGIOPS_VERSION) + message("LogiOps Version Number: ${LOGIOPS_VERSION}") +else() + set(LOGIOPS_VERSION "null") +endif() + +add_definitions( -DLOGIOPS_VERSION="${LOGIOPS_VERSION}") + add_subdirectory(src/logid) diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index e112e50..ab63b8a 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -19,6 +19,11 @@ #define evdev_name "logid" #define DEFAULT_CONFIG_FILE "/etc/logid.cfg" +#ifndef LOGIOPS_VERSION +#define LOGIOPS_VERSION "null" +#warning Version is undefined! +#endif + using namespace logid; LogLevel logid::global_verbosity = INFO; @@ -31,7 +36,8 @@ enum class Option None, Verbose, Config, - Help + Help, + Version }; int main(int argc, char** argv) @@ -51,11 +57,15 @@ int main(int argc, char** argv) if (op_str == "--verbose") option = Option::Verbose; if (op_str == "--config") option = Option::Config; if (op_str == "--help") option = Option::Help; + if (op_str == "--version") option = Option::Version; break; } case 'v': // Verbosity option = Option::Verbose; break; + case 'V': //Version + option = Option::Version; + break; case 'c': // Config file path option = Option::Config; break; @@ -103,14 +113,23 @@ int main(int argc, char** argv) break; } case Option::Help: - printf(R"(Usage: %s [options] + { + printf(R"(logid version %s +Usage: %s [options] Possible options are: -v,--verbose [level] Set log level to debug/info/warn/error (leave blank for debug) + -V,--version Print version number -c,--config [file path] Change config file from default at %s -h,--help Print this message. -)", argv[0], DEFAULT_CONFIG_FILE); +)", LOGIOPS_VERSION, argv[0], DEFAULT_CONFIG_FILE); return EXIT_SUCCESS; + } + case Option::Version: + { + printf("%s\n", LOGIOPS_VERSION); + return EXIT_SUCCESS; + } case Option::None: break; } From 6316b89840a5fa3da402959b90ee74ead5a9afa5 Mon Sep 17 00:00:00 2001 From: PixlOne <8843371+PixlOne@users.noreply.github.com> Date: Sun, 12 Apr 2020 19:44:12 -0400 Subject: [PATCH 02/81] Reorganise logid.cpp --- src/logid/logid.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index ab63b8a..ba36241 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -26,6 +26,8 @@ using namespace logid; +std::string config_file = DEFAULT_CONFIG_FILE; + LogLevel logid::global_verbosity = INFO; Configuration* logid::global_config; EvdevDevice* logid::global_evdev; @@ -40,10 +42,8 @@ enum class Option Version }; -int main(int argc, char** argv) +void read_cli_options(int argc, char** argv) { - std::string config_file = DEFAULT_CONFIG_FILE; - // Read command line options for(int i = 1; i < argc; i++) { Option option = Option::None; @@ -97,7 +97,7 @@ int main(int argc, char** argv) { log_printf(WARN, e.what()); printf("Valid verbosity levels are: Debug, Info, Warn/Warning, or Error.\n"); - return EXIT_FAILURE; + exit(EXIT_FAILURE); } } break; @@ -107,7 +107,7 @@ int main(int argc, char** argv) if (++i >= argc) { log_printf(ERROR, "Config file is not specified."); - return EXIT_FAILURE; + exit(EXIT_FAILURE); } config_file = argv[i]; break; @@ -123,18 +123,23 @@ Possible options are: -h,--help Print this message. )", LOGIOPS_VERSION, argv[0], DEFAULT_CONFIG_FILE); - return EXIT_SUCCESS; + exit(EXIT_SUCCESS); } case Option::Version: { printf("%s\n", LOGIOPS_VERSION); - return EXIT_SUCCESS; + exit(EXIT_SUCCESS); } case Option::None: break; } } } +} + +int main(int argc, char** argv) +{ + read_cli_options(argc, argv); // Read config try { global_config = new Configuration(config_file.c_str()); } From 526ffec61aa03f4f49b1f249160c53ab3bd6e6f2 Mon Sep 17 00:00:00 2001 From: PixlOne <8843371+PixlOne@users.noreply.github.com> Date: Sun, 12 Apr 2020 22:25:39 -0400 Subject: [PATCH 03/81] Implement reload method --- src/logid/logid.cpp | 36 +++++++++++++++++++++++++++--------- src/logid/logid.h | 14 ++++++++++++++ 2 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 src/logid/logid.h diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index ba36241..891574b 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -1,13 +1,7 @@ -#include -#include -#include -#include -#include -#include -#include #include #include #include +#include #include "util.h" #include "Device.h" @@ -15,6 +9,8 @@ #include "Configuration.h" #include "EvdevDevice.h" #include "DeviceFinder.h" +#include "IPCServer.h" +#include "logid.h" #define evdev_name "logid" #define DEFAULT_CONFIG_FILE "/etc/logid.cfg" @@ -33,6 +29,9 @@ Configuration* logid::global_config; EvdevDevice* logid::global_evdev; DeviceFinder* logid::finder; +bool logid::kill_logid = false; +std::mutex logid::finder_reloading; + enum class Option { None, @@ -42,6 +41,19 @@ enum class Option Version }; +void logid::reload() +{ + log_printf(INFO, "Reloading logid..."); + finder_reloading.lock(); + finder->stop(); + Configuration* old_config = global_config; + global_config = new Configuration(config_file.c_str()); + delete(old_config); + delete(finder); + finder = new DeviceFinder(); + finder_reloading.unlock(); +} + void read_cli_options(int argc, char** argv) { for(int i = 1; i < argc; i++) @@ -155,7 +167,13 @@ int main(int argc, char** argv) // Scan devices, create listeners, handlers, etc. finder = new DeviceFinder(); - finder->run(); + + while(!kill_logid) + { + finder_reloading.lock(); + finder_reloading.unlock(); + finder->run(); + } return EXIT_SUCCESS; -} \ No newline at end of file +} diff --git a/src/logid/logid.h b/src/logid/logid.h new file mode 100644 index 0000000..a5dc790 --- /dev/null +++ b/src/logid/logid.h @@ -0,0 +1,14 @@ +#ifndef LOGID_LOGID_H +#define LOGID_LOGID_H + +#include + +namespace logid +{ + void reload(); + + extern bool kill_logid; + extern std::mutex finder_reloading; +} + +#endif //LOGID_LOGID_H \ No newline at end of file From ec4ae56bc4efe43706e31e93fa5e13d561ebecb9 Mon Sep 17 00:00:00 2001 From: pixl Date: Tue, 16 Jun 2020 19:53:38 -0400 Subject: [PATCH 04/81] Implement raw DeviceMonitor Multiple things have been done in this commit; the base of the new backend has effectively been created. This branch currently has many vital parts commented out. Therefore, this branch is currently only intended for debugging. --- src/logid/CMakeLists.txt | 63 ++------ src/logid/Device.h | 2 +- src/logid/DeviceMonitor.cpp | 126 +++++++++++++++ src/logid/{DeviceFinder.h => DeviceMonitor.h} | 26 ++-- src/logid/backend/dj/Report.cpp | 32 ++++ src/logid/backend/dj/Report.h | 20 +++ src/logid/backend/hidpp/Device.cpp | 30 ++++ src/logid/backend/hidpp/Device.h | 51 ++++++ src/logid/backend/hidpp/Error.cpp | 0 src/logid/backend/hidpp/Error.h | 0 src/logid/backend/hidpp/Report.cpp | 83 ++++++++++ src/logid/backend/hidpp/Report.h | 57 +++++++ src/logid/backend/raw/DeviceMonitor.cpp | 133 ++++++++++++++++ src/logid/backend/raw/DeviceMonitor.h | 32 ++++ src/logid/backend/raw/RawDevice.cpp | 147 ++++++++++++++++++ src/logid/backend/raw/RawDevice.h | 41 +++++ src/logid/logid.cpp | 22 +-- src/logid/logid.h | 2 +- src/logid/util.cpp | 4 + src/logid/util.h | 4 +- 20 files changed, 797 insertions(+), 78 deletions(-) create mode 100644 src/logid/DeviceMonitor.cpp rename src/logid/{DeviceFinder.h => DeviceMonitor.h} (54%) create mode 100644 src/logid/backend/dj/Report.cpp create mode 100644 src/logid/backend/dj/Report.h create mode 100644 src/logid/backend/hidpp/Device.cpp create mode 100644 src/logid/backend/hidpp/Device.h create mode 100644 src/logid/backend/hidpp/Error.cpp create mode 100644 src/logid/backend/hidpp/Error.h create mode 100644 src/logid/backend/hidpp/Report.cpp create mode 100644 src/logid/backend/hidpp/Report.h create mode 100644 src/logid/backend/raw/DeviceMonitor.cpp create mode 100644 src/logid/backend/raw/DeviceMonitor.h create mode 100644 src/logid/backend/raw/RawDevice.cpp create mode 100644 src/logid/backend/raw/RawDevice.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index c924dca..1bafd4a 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -11,66 +11,29 @@ find_package(PkgConfig REQUIRED) add_executable(logid logid.cpp util.cpp - util.h - Configuration.cpp - Configuration.h - Actions.cpp - Actions.h - Device.cpp - Device.h - DeviceFinder.cpp - DeviceFinder.h - EvdevDevice.cpp - EvdevDevice.h) + DeviceMonitor.cpp + backend/raw/DeviceMonitor.cpp + backend/raw/RawDevice.cpp + backend/hidpp/Device.cpp + backend/hidpp/Report.cpp + backend/dj/Report.cpp) + set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -pkg_check_modules(PC_EVDEV libevdev) +pkg_check_modules(PC_EVDEV libevdev REQUIRED) pkg_check_modules(SYSTEMD "systemd") -pkg_check_modules(LIBCONFIG libconfig) - -find_path(HIDPP_INCLUDE_DIR hidpp) -find_library(HIDPP_LIBRARY libhidpp.so) +pkg_check_modules(LIBCONFIG libconfig REQUIRED) +pkg_check_modules(LIBUDEV libudev REQUIRED) find_path(EVDEV_INCLUDE_DIR libevdev/libevdev.h HINTS ${PC_EVDEV_INCLUDE_DIRS} ${PC_EVDEV_INCLUDEDIR}) find_library(EVDEV_LIBRARY NAMES evdev libevdev) -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") +include_directories(${HIDPP_INCLUDE_DIR} ${EVDEV_INCLUDE_DIR} ${DBUSCXX_INCLUDE_DIR} ${LIBUDEV_INCLUDE_DIRECTORIES}) - execute_process(COMMAND git submodule update --init -- hidpp - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - - if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") - set(DEFAULT_HID_BACKEND "linux") - elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - set(DEFAULT_HID_BACKEND "windows") - else() - message(WARNING "System is not supported") - endif() - - set(HID_BACKEND "${DEFAULT_HID_BACKEND}" CACHE STRING "Backend used for accessing HID devices") - set_property(CACHE HID_BACKEND PROPERTY STRINGS linux windows) - - if("${HID_BACKEND}" STREQUAL "linux") - pkg_check_modules(LIBUDEV libudev REQUIRED) - elseif("${HID_BACKEND}" STREQUAL "windows") - add_definitions(-DUNICODE -D_UNICODE) - add_definitions(-D_WIN32_WINNT=0x0600) # Use vista or later - else() - message(FATAL_ERROR "HID_BACKEND is invalid.") - endif() - - add_subdirectory(hidpp/src/libhidpp) - - 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++ + ${DBUSCXX_LIBRARIES} ${LIBUDEV_LIBRARIES}) target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ ${HIDPP_LIBRARY}) diff --git a/src/logid/Device.h b/src/logid/Device.h index 7fba0f7..4a3ca6c 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -2,7 +2,7 @@ #define LOGID_DEVICE_H #include "Actions.h" -#include "DeviceFinder.h" +#include "DeviceMonitor.h" #include "Configuration.h" #include diff --git a/src/logid/DeviceMonitor.cpp b/src/logid/DeviceMonitor.cpp new file mode 100644 index 0000000..790df62 --- /dev/null +++ b/src/logid/DeviceMonitor.cpp @@ -0,0 +1,126 @@ +#include +#include + +#include "DeviceMonitor.h" +#include "util.h" + +#define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded" + +using namespace logid; +using namespace logid::backend; + +/* +void stopAndDeleteConnectedDevice (ConnectedDevice &connected_device) +{ + 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); +} + +DeviceMonitor::~DeviceMonitor() +{ + this->devices_mutex.lock(); + for (auto it = this->devices.begin(); it != this->devices.end(); it++) { + for (auto jt = it->second.begin(); jt != it->second.end(); jt++) { + stopAndDeleteConnectedDevice(jt->second); + } + } + this->devices_mutex.unlock(); +} + +Device* DeviceMonitor::insertNewDevice(const std::string &path, HIDPP::DeviceIndex 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()); + auto path_bucket = this->devices.emplace(path, std::map()).first; + path_bucket->second.emplace(index, ConnectedDevice{ + device, + std::thread([device]() { + device->start(); + }) + }); + this->devices_mutex.unlock(); + + return device; +} + +Device* DeviceMonitor::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->waitForReceiver(); + }) + }); + this->devices_mutex.unlock(); + + return device; +} + +void DeviceMonitor::stopAndDeleteAllDevicesIn (const std::string &path) +{ + this->devices_mutex.lock(); + auto path_bucket = this->devices.find(path); + if (path_bucket != this->devices.end()) + { + for (auto& index_bucket : path_bucket->second) { + stopAndDeleteConnectedDevice(index_bucket.second); + } + this->devices.erase(path_bucket); + } + this->devices_mutex.unlock(); +} + +void DeviceMonitor::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index) +{ + this->devices_mutex.lock(); + auto path_bucket = this->devices.find(path); + if (path_bucket != this->devices.end()) + { + auto index_bucket = path_bucket->second.find(index); + if (index_bucket != path_bucket->second.end()) + { + stopAndDeleteConnectedDevice(index_bucket->second); + path_bucket->second.erase(index_bucket); + } + } + this->devices_mutex.unlock(); + + log_printf(WARN, "Attempted to disconnect not previously connected device %d on %s", index, path.c_str()); +} +*/ + +void DeviceMonitor::addDevice(std::string path) +{ + try { + backend::hidpp::Device device(path, hidpp::DeviceIndex::DefaultDevice); + + log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); + } + catch(backend::hidpp::Device::InvalidDevice &e) + { + log_printf(DEBUG, "Detected device at %s but %s", path.c_str(), e.what()); + } + catch(std::system_error &e) + { + log_printf(WARN, "Failed to open %s: %s", path.c_str(), e.what()); + } +} + +void DeviceMonitor::removeDevice(std::string path) +{ + log_printf(DEBUG, "Device %s disconnected", path.c_str()); + /* + ipc_server->removeReceiver(path); + this->stopAndDeleteAllDevicesIn(std::string(path)); + */ +} diff --git a/src/logid/DeviceFinder.h b/src/logid/DeviceMonitor.h similarity index 54% rename from src/logid/DeviceFinder.h rename to src/logid/DeviceMonitor.h index 687b33d..c288f0c 100644 --- a/src/logid/DeviceFinder.h +++ b/src/logid/DeviceMonitor.h @@ -1,16 +1,12 @@ -#ifndef LOGID_DEVICEFINDER_H -#define LOGID_DEVICEFINDER_H +#ifndef LOGID_DEVICEMONITOR_H +#define LOGID_DEVICEMONITOR_H -#include -#include -#include -#include -#include #include #include #include -#include "Device.h" +#include "backend/raw/DeviceMonitor.h" +#include "backend/hidpp/Device.h" #define MAX_CONNECTION_TRIES 10 #define TIME_BETWEEN_CONNECTION_TRIES 500ms @@ -25,24 +21,26 @@ namespace logid std::thread associatedThread; }; - class DeviceFinder : public HID::DeviceMonitor + class DeviceMonitor : public backend::raw::DeviceMonitor { public: - ~DeviceFinder(); + ~DeviceMonitor(); + /* 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: - void addDevice(const char* path); - void removeDevice(const char* path); + void addDevice(std::string path) override; + void removeDevice(std::string path) override; private: std::mutex devices_mutex; - std::map> devices; + std::map> devices; }; - extern DeviceFinder* finder; + extern DeviceMonitor* finder; } #endif //LOGID_DEVICEFINDER_H \ No newline at end of file diff --git a/src/logid/backend/dj/Report.cpp b/src/logid/backend/dj/Report.cpp new file mode 100644 index 0000000..b8cccb3 --- /dev/null +++ b/src/logid/backend/dj/Report.cpp @@ -0,0 +1,32 @@ +#include +#include +#include "Report.h" + +using namespace logid::backend::dj; +using namespace logid::backend; + +static const std::array DJReportDesc = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x20, // Report ID (32) + 0x95, 0x0E, // Report Count (14) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x41, // Usage (0x41) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x41, // Usage (0x41) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0x85, 0x21, // Report ID (33) + 0x95, 0x1F, // Report Count (31) + 0x09, 0x42, // Usage (0x42) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x42, // Usage (0x42) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + +bool dj::supportsDjReports(std::vector& rdesc) +{ + auto it = std::search(rdesc.begin(), rdesc.end(), DJReportDesc.begin(), DJReportDesc.end()); + return it != rdesc.end(); +} \ No newline at end of file diff --git a/src/logid/backend/dj/Report.h b/src/logid/backend/dj/Report.h new file mode 100644 index 0000000..c4078f4 --- /dev/null +++ b/src/logid/backend/dj/Report.h @@ -0,0 +1,20 @@ +#ifndef LOGID_BACKEND_DJ_REPORT_H +#define LOGID_BACKEND_DJ_REPORT_H + +#include +#include "../raw/RawDevice.h" + +namespace logid::backend::dj +{ + bool supportsDjReports(std::vector& rawDevice); + class Report + { + enum Type: uint8_t + { + Short = 0x20, // Short DJ reports use 12 byte parameters + Long = 0x21 // Long DJ reports use 29 byte parameters + }; + }; +} + +#endif //LOGID_BACKEND_DJ_REPORT_H diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp new file mode 100644 index 0000000..9b517bb --- /dev/null +++ b/src/logid/backend/hidpp/Device.cpp @@ -0,0 +1,30 @@ +#include "Device.h" +#include "Report.h" + +using namespace logid::backend; +using namespace logid::backend::hidpp; + +const char* Device::InvalidDevice::what() const noexcept +{ + switch(_reason) + { + case NoHIDPPReport: + return "Invalid HID++ device"; + case InvalidRawDevice: + return "Invalid raw device"; + } +} + +Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept +{ + return _reason; +} + +/// TODO: Initialize a single RawDevice for each path. +Device::Device(std::string path, DeviceIndex index): + raw_device (std::make_shared(path)), path (path), index (index) +{ + supported_reports = getSupportedReports(raw_device->reportDescriptor()); + if(!supported_reports) + throw InvalidDevice(InvalidDevice::NoHIDPPReport); +} \ No newline at end of file diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h new file mode 100644 index 0000000..70e0956 --- /dev/null +++ b/src/logid/backend/hidpp/Device.h @@ -0,0 +1,51 @@ +#ifndef LOGID_HIDPP_DEVICE_H +#define LOGID_HIDPP_DEVICE_H + +#include +#include +#include "../raw/RawDevice.h" + +namespace logid::backend::hidpp +{ + enum DeviceIndex: uint8_t + { + DefaultDevice = 0, + WirelessDevice1 = 1, + WirelessDevice2 = 2, + WirelessDevice3 = 3, + WirelessDevice4 = 4, + WirelessDevice5 = 5, + WirelessDevice6 = 6, + CordedDevice = 0xff + }; + + class Device + { + public: + class InvalidDevice : std::exception + { + public: + enum Reason + { + NoHIDPPReport, + InvalidRawDevice + }; + InvalidDevice(Reason reason) : _reason (reason) {} + virtual const char *what() const noexcept; + virtual Reason code() const noexcept; + private: + Reason _reason; + + }; + Device(std::string path, DeviceIndex index); + std::string devicePath() const { return path; } + DeviceIndex deviceIndex() const { return index; } + private: + std::shared_ptr raw_device; + std::string path; + DeviceIndex index; + uint8_t supported_reports; + }; +} + +#endif //LOGID_HIDPP_DEVICE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp/Error.cpp b/src/logid/backend/hidpp/Error.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/logid/backend/hidpp/Error.h b/src/logid/backend/hidpp/Error.h new file mode 100644 index 0000000..e69de29 diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp new file mode 100644 index 0000000..c9b27b4 --- /dev/null +++ b/src/logid/backend/hidpp/Report.cpp @@ -0,0 +1,83 @@ +#include +#include +#include "Report.h" + +using namespace logid::backend::hidpp; +using namespace logid::backend; + +/* Report descriptors were sourced from cvuchener/hidpp */ +static const std::array ShortReportDesc = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x10, // Report ID (16) + 0x75, 0x08, // Report Size (8) + 0x95, 0x06, // Report Count (6) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x01, // Usage (0001 - Vendor) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x01, // Usage (0001 - Vendor) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + +static const std::array LongReportDesc = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x11, // Report ID (17) + 0x75, 0x08, // Report Size (8) + 0x95, 0x13, // Report Count (19) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x02, // Usage (0002 - Vendor) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x02, // Usage (0002 - Vendor) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + +/* Alternative versions from the G602 */ +static const std::array ShortReportDesc2 = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x10, // Report ID (16) + 0x95, 0x06, // Report Count (6) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x01, // Usage (0001 - Vendor) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x01, // Usage (0001 - Vendor) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + +static const std::array LongReportDesc2 = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x11, // Report ID (17) + 0x95, 0x13, // Report Count (19) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x02, // Usage (0002 - Vendor) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x02, // Usage (0002 - Vendor) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + +uint8_t hidpp::getSupportedReports(std::vector&& rdesc) +{ + uint8_t ret = 0; + + auto it = std::search(rdesc.begin(), rdesc.end(), ShortReportDesc.begin(), ShortReportDesc.end()); + if(it == rdesc.end()) + it = std::search(rdesc.begin(), rdesc.end(), ShortReportDesc2.begin(), ShortReportDesc2.end()); + if(it != rdesc.end()) + ret |= HIDPP_REPORT_SHORT_SUPPORTED; + + it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc.begin(), LongReportDesc2.end()); + if(it == rdesc.end()) + it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc2.begin(), LongReportDesc2.end()); + if(it != rdesc.end()) + ret |= HIDPP_REPORT_LONG_SUPPORTED; + + return ret; +} \ No newline at end of file diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h new file mode 100644 index 0000000..d69c4b6 --- /dev/null +++ b/src/logid/backend/hidpp/Report.h @@ -0,0 +1,57 @@ +#ifndef LOGID_BACKEND_HIDPP_REPORT_H +#define LOGID_BACKEND_HIDPP_REPORT_H + +#include +#include "../raw/RawDevice.h" +#include "Device.h" + +#define LOGID_HIDPP_SW_ID 0x0f + +/* Some devices only support a subset of these reports */ +#define HIDPP_REPORT_SHORT_SUPPORTED 1U +#define HIDPP_REPORT_LONG_SUPPORTED 1U<<1U +/* Very long reports exist, however they have not been encountered so far */ + +namespace logid::backend::hidpp +{ + uint8_t getSupportedReports(std::vector&& rdesc); + class Report + { + public: + enum Type: uint8_t + { + Short = 0x10, + Long = 0x11 + }; + + class InvalidReportID: std::exception + { + InvalidReportID(); + virtual const char* what() const noexcept; + }; + + class InvalidReportLength: std::exception + { + InvalidReportLength(); + virtual const char* what() const noexcept; + }; + + static constexpr std::size_t MaxDataLength = 32; + + Report(uint8_t report_id, const uint8_t* data, std::size_t length); + Report(std::vector data); + + Type type() const; + void setType(Report::Type type); + + logid::backend::hidpp::DeviceIndex deviceIndex(); + + std::vector rawReport () const { return _data; } + + private: + static constexpr std::size_t HeaderLength = 4; + std::vector _data; + }; +} + +#endif //LOGID_BACKEND_HIDPP_REPORT_H \ No newline at end of file diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp new file mode 100644 index 0000000..69eac98 --- /dev/null +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -0,0 +1,133 @@ +#include "DeviceMonitor.h" + +#include +#include + +extern "C" +{ +#include +#include +} + +using namespace logid::backend::raw; + +DeviceMonitor::DeviceMonitor() +{ + if(-1 == pipe(monitor_pipe)) + throw std::system_error(errno, std::system_category(), "pipe creation failed"); + + udev_context = udev_new(); + if(!udev_context) + throw std::runtime_error("udev_new failed"); +} + +DeviceMonitor::~DeviceMonitor() +{ + bool is_running = running.try_lock(); + if(is_running) + running.unlock(); + else + this->stop(); + + udev_unref(udev_context); + + for(int i : monitor_pipe) + close(i); +} + +void DeviceMonitor::run() +{ + int ret; + const std::lock_guard run_lock(running); + + struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev_context, "udev"); + if(!monitor) + throw std::runtime_error("udev_monitor_new_from_netlink failed"); + + ret = udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw", nullptr); + if (0 != ret) + throw std::system_error (-ret, std::system_category (), + "udev_monitor_filter_add_match_subsystem_devtype"); + + ret = udev_monitor_enable_receiving(monitor); + if(0 != ret) + throw std::system_error(-ret, std::system_category(), + "udev_moniotr_enable_receiving"); + + this->enumerate(); + + int fd = udev_monitor_get_fd(monitor); + while (true) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(monitor_pipe[0], &fds); + FD_SET(fd, &fds); + if (-1 == select (std::max (monitor_pipe[0], fd)+1, &fds, nullptr, nullptr, nullptr)) { + if (errno == EINTR) + continue; + throw std::system_error (errno, std::system_category(), "udev_monitor select"); + } + if (FD_ISSET(fd, &fds)) { + struct udev_device *device = udev_monitor_receive_device(monitor); + std::string action = udev_device_get_action(device); + std::string devnode = udev_device_get_devnode(device); + if (action == "add") + std::thread([this](const std::string name) { + this->addDevice(name); + }, devnode).detach(); + else if (action == "remove") + std::thread([this](const std::string name) { + this->removeDevice(name); + }, devnode).detach(); + udev_device_unref (device); + } + if (FD_ISSET(monitor_pipe[0], &fds)) { + char c; + if (-1 == read(monitor_pipe[0], &c, sizeof (char))) + throw std::system_error (errno, std::system_category (), + "read pipe"); + break; + } + } +} + +void DeviceMonitor::stop() +{ + +} + +void DeviceMonitor::enumerate() +{ + int ret; + struct udev_enumerate* udev_enum = udev_enumerate_new(udev_context); + ret = udev_enumerate_add_match_subsystem(udev_enum, "hidraw"); + if(0 != ret) + throw std::system_error(-ret, std::system_category(), + "udev_enumerate_add_match_subsystem"); + + ret = udev_enumerate_scan_devices(udev_enum); + if(0 != ret) + throw std::system_error(-ret, std::system_category(), + "udev_enumerate_scan_devices"); + + struct udev_list_entry* udev_enum_entry; + udev_list_entry_foreach(udev_enum_entry, + udev_enumerate_get_list_entry(udev_enum)) + { + const char* name = udev_list_entry_get_name(udev_enum_entry); + + struct udev_device* device = udev_device_new_from_syspath(udev_context, + name); + if(!device) + throw std::runtime_error("udev_device_new_from_syspath failed"); + + std::string devnode = udev_device_get_devnode(device); + udev_device_unref(device); + + std::thread([this](const std::string name) { + this->addDevice(name); + }, devnode).detach(); + } + + udev_enumerate_unref(udev_enum); +} \ No newline at end of file diff --git a/src/logid/backend/raw/DeviceMonitor.h b/src/logid/backend/raw/DeviceMonitor.h new file mode 100644 index 0000000..af2a0c6 --- /dev/null +++ b/src/logid/backend/raw/DeviceMonitor.h @@ -0,0 +1,32 @@ +#ifndef LOGID_BACKEND_RAW_DEVICEMONITOR_H +#define LOGID_BACKEND_RAW_DEVICEMONITOR_H + +#include +#include + +extern "C" +{ +#include +} + +namespace logid::backend::raw +{ + class DeviceMonitor + { + public: + void enumerate(); + void run(); + void stop(); + protected: + DeviceMonitor(); + ~DeviceMonitor(); + virtual void addDevice(std::string device) = 0; + virtual void removeDevice(std::string device) = 0; + private: + struct udev* udev_context; + int monitor_pipe[2]; + std::mutex running; + }; +} + +#endif //LOGID_BACKEND_RAW_DEVICEMONITOR_H \ No newline at end of file diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp new file mode 100644 index 0000000..22513ae --- /dev/null +++ b/src/logid/backend/raw/RawDevice.cpp @@ -0,0 +1,147 @@ +#include "RawDevice.h" + +#include +#include +#include + + +extern "C" +{ +#include +#include +#include +#include +} + +using namespace logid::backend::raw; +using namespace std::chrono; + +RawDevice::RawDevice(std::string path) : path (path) +{ + int ret; + + fd = ::open(path.c_str(), O_RDWR); + if (fd == -1) + 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"); + } + vid = devinfo.vendor; + pid = devinfo.product; + + char name_buf[256]; + if (-1 == (ret = ::ioctl(fd, HIDIOCGRAWNAME(sizeof(name_buf)), name_buf))) + { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWNAME failed"); + } + 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"); + } + if (-1 == ::ioctl(fd, HIDIOCGRDESC, &_rdesc)) + { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESC failed"); + } + rdesc = std::vector(_rdesc.value, _rdesc.value + _rdesc.size); + + if (-1 == ::pipe(dev_pipe)) + { + int err = errno; + close(fd); + throw std::system_error(err, std::system_category(), "RawDevice pipe open failed"); + } +} + +RawDevice::~RawDevice() +{ + if(fd != -1) + { + ::close(fd); + ::close(dev_pipe[0]); + ::close(dev_pipe[1]); + } +} + +void RawDevice::sendReport(std::vector report) +{ + _sendReport(std::move(report)); +} + +std::vector RawDevice::readReport(std::size_t maxDataLength) +{ + return _readReport(maxDataLength); +} + +void RawDevice::_sendReport(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"); +} + +std::vector RawDevice::_readReport(std::size_t maxDataLength) +{ + std::lock_guard lock(dev_io); + int ret; + std::vector report(maxDataLength); + + timeval timeout = { duration_cast(HIDPP_IO_TIMEOUT).count(), + duration_cast(HIDPP_IO_TIMEOUT).count() }; + + fd_set fds; + do { + FD_ZERO(&fds); + FD_SET(fd, &fds); + FD_SET(dev_pipe[0], &fds); + + ret = select(std::max(fd, dev_pipe[0]) + 1, + &fds, nullptr, nullptr, + (HIDPP_IO_TIMEOUT.count() > 0 ? nullptr : &timeout)); + } while(ret == -1 && errno == EINTR); + + if(ret == -1) + 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"); + report.resize(ret); + } + + if(FD_ISSET(dev_pipe[0], &fds)) + { + 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"); + } + + return report; +} + +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"); + + // Ensure I/O has halted + std::lock_guard lock(dev_io); +} \ No newline at end of file diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h new file mode 100644 index 0000000..c6c88fc --- /dev/null +++ b/src/logid/backend/raw/RawDevice.h @@ -0,0 +1,41 @@ +#ifndef LOGID_BACKEND_RAWDEVICE_H +#define LOGID_BACKEND_RAWDEVICE_H + +#include +#include +#include + +#define HIDPP_IO_TIMEOUT std::chrono::seconds(2) + +namespace logid::backend::raw +{ + class RawDevice + { + public: + RawDevice(std::string path); + ~RawDevice(); + std::string hidrawPath() const { return path; } + std::vector reportDescriptor() const { return rdesc; } + + /// TODO: Process reports in a queue. + void sendReport(std::vector report); + std::vector readReport(std::size_t maxDataLength); + + void interruptRead(); + private: + std::mutex dev_io; + std::string path; + int fd; + int dev_pipe[2]; + uint16_t vid; + uint16_t pid; + std::string name; + std::vector rdesc; + + /* These will only be used internally and processed with a queue */ + void _sendReport(std::vector report); + std::vector _readReport(std::size_t maxDataLength); + }; +} + +#endif //LOGID_BACKEND_RAWDEVICE_H \ No newline at end of file diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 891574b..562341f 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -4,12 +4,7 @@ #include #include "util.h" -#include "Device.h" -#include "Actions.h" -#include "Configuration.h" -#include "EvdevDevice.h" -#include "DeviceFinder.h" -#include "IPCServer.h" +#include "DeviceMonitor.h" #include "logid.h" #define evdev_name "logid" @@ -25,9 +20,8 @@ using namespace logid; std::string config_file = DEFAULT_CONFIG_FILE; LogLevel logid::global_verbosity = INFO; -Configuration* logid::global_config; -EvdevDevice* logid::global_evdev; -DeviceFinder* logid::finder; +// Configuration* logid::global_config; +DeviceMonitor* logid::finder; bool logid::kill_logid = false; std::mutex logid::finder_reloading; @@ -41,6 +35,7 @@ enum class Option Version }; +/* void logid::reload() { log_printf(INFO, "Reloading logid..."); @@ -50,9 +45,10 @@ void logid::reload() global_config = new Configuration(config_file.c_str()); delete(old_config); delete(finder); - finder = new DeviceFinder(); + finder = new DeviceMonitor(); finder_reloading.unlock(); } + */ void read_cli_options(int argc, char** argv) { @@ -153,10 +149,12 @@ int main(int argc, char** argv) { read_cli_options(argc, argv); + /* // Read config try { global_config = new Configuration(config_file.c_str()); } catch (std::exception &e) { global_config = new Configuration(); } + //Create an evdev device called 'logid' try { global_evdev = new EvdevDevice(evdev_name); } catch(std::system_error& e) @@ -165,8 +163,10 @@ int main(int argc, char** argv) return EXIT_FAILURE; } + */ + // Scan devices, create listeners, handlers, etc. - finder = new DeviceFinder(); + finder = new DeviceMonitor(); while(!kill_logid) { diff --git a/src/logid/logid.h b/src/logid/logid.h index a5dc790..7498e5c 100644 --- a/src/logid/logid.h +++ b/src/logid/logid.h @@ -5,7 +5,7 @@ namespace logid { - void reload(); + // void reload(); extern bool kill_logid; extern std::mutex finder_reloading; diff --git a/src/logid/util.cpp b/src/logid/util.cpp index 32d9aca..7b0218d 100644 --- a/src/logid/util.cpp +++ b/src/logid/util.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "util.h" @@ -34,6 +35,7 @@ const char* logid::level_prefix(LogLevel level) return "DEBUG"; } +/* Direction logid::getDirection(int x, int y) { if(x == 0 && y == 0) return Direction::None; @@ -111,6 +113,8 @@ Action logid::stringToAction(std::string s) throw std::invalid_argument(original_str + " is an invalid action."); } + */ + LogLevel logid::stringToLogLevel(std::string s) { std::string original_str = s; diff --git a/src/logid/util.h b/src/logid/util.h index 7f41cb7..09bea48 100644 --- a/src/logid/util.h +++ b/src/logid/util.h @@ -1,7 +1,7 @@ #ifndef LOGID_UTIL_H #define LOGID_UTIL_H -#include "Actions.h" +//#include "Actions.h" namespace logid { @@ -19,10 +19,12 @@ namespace logid const char* level_prefix(LogLevel level); + /* Direction getDirection(int x, int y); Direction stringToDirection(std::string s); GestureMode stringToGestureMode(std::string s); Action stringToAction(std::string s); + */ LogLevel stringToLogLevel(std::string s); } From 1de722b935ea8f894d386c3260fb4f16856fbf7f Mon Sep 17 00:00:00 2001 From: pixl Date: Tue, 16 Jun 2020 19:56:27 -0400 Subject: [PATCH 05/81] Add note about state of branch in README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1a05617..30c3a0e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # logiops +#### This branch is a WIP and currently does not function. + This is an unofficial driver for Logitech mice and keyboard. This is currently only compatible with HID++ \>2.0 devices. @@ -13,7 +15,7 @@ Default location for the configuration file is /etc/logid.cfg. ## Dependencies -This project requires a C++14 compiler, cmake, libevdev, libudev, and libconfig. For popular distributions, I've included commands below. +This project requires a C++14 compiler, cmake, libevdev, libudev, and libconfig. For popular distributions, I've included commands below. **Debian/Ubuntu:** `sudo apt install cmake libevdev-dev libudev-dev libconfig++-dev` From 6b895b30150da9d2ba47f65fb8d136f98e75f704 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 17 Jun 2020 02:43:53 -0400 Subject: [PATCH 06/81] Monitor all HID++ reports on wireless device 1 Again, many things were done in this commit such as implementing an I/O queue, a mutex_queue, and implementing the hidpp::Report class. I'm expecting commits to be like this until I can get a clean codebase for the backend. --- src/logid/CMakeLists.txt | 4 +- src/logid/DeviceMonitor.cpp | 24 ++++++- src/logid/DeviceMonitor.h | 1 + src/logid/backend/Error.cpp | 6 ++ src/logid/backend/Error.h | 16 +++++ src/logid/backend/defs.h | 15 ++++ src/logid/backend/hidpp/Device.cpp | 62 ++++++++++++++++- src/logid/backend/hidpp/Device.h | 42 ++++++++---- src/logid/backend/hidpp/Report.cpp | 79 ++++++++++++++++++++- src/logid/backend/hidpp/Report.h | 47 +++++++++---- src/logid/backend/hidpp/defs.h | 24 +++++++ src/logid/backend/raw/RawDevice.cpp | 103 ++++++++++++++++++++++++---- src/logid/backend/raw/RawDevice.h | 37 +++++++--- src/logid/util/mutex_queue.h | 37 ++++++++++ 14 files changed, 442 insertions(+), 55 deletions(-) create mode 100644 src/logid/backend/Error.cpp create mode 100644 src/logid/backend/Error.h create mode 100644 src/logid/backend/defs.h create mode 100644 src/logid/backend/hidpp/defs.h create mode 100644 src/logid/util/mutex_queue.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 1bafd4a..e1c0924 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -12,11 +12,13 @@ add_executable(logid logid.cpp util.cpp DeviceMonitor.cpp + backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp backend/hidpp/Device.cpp backend/hidpp/Report.cpp - backend/dj/Report.cpp) + backend/dj/Report.cpp + util/mutex_queue.h) set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/src/logid/DeviceMonitor.cpp b/src/logid/DeviceMonitor.cpp index 790df62..e69bda4 100644 --- a/src/logid/DeviceMonitor.cpp +++ b/src/logid/DeviceMonitor.cpp @@ -102,9 +102,29 @@ void DeviceMonitor::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceI void DeviceMonitor::addDevice(std::string path) { try { - backend::hidpp::Device device(path, hidpp::DeviceIndex::DefaultDevice); - + backend::hidpp::Device device(path, hidpp::DeviceIndex::WirelessDevice1); log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); + + backend::hidpp::EventHandler eventHandler; + eventHandler.condition = [device](backend::hidpp::Report& report)->bool + { + return true; + }; + eventHandler.callback = [device](backend::hidpp::Report& report)->void + { + log_printf(DEBUG, "Event on %s:%d", device.devicePath().c_str(), + device.deviceIndex()); + for(auto& i : report.rawReport()) + printf("%02x ", i); + printf("\n"); + }; + + device.addEventHandler("MONITOR_ALL", eventHandler); + + std::thread([](backend::hidpp::Device device) { device.listen(); }, device).detach(); + + /* This is a temporary solution to avoid std::bad_function_call */ + while(true) {} } catch(backend::hidpp::Device::InvalidDevice &e) { diff --git a/src/logid/DeviceMonitor.h b/src/logid/DeviceMonitor.h index c288f0c..159970e 100644 --- a/src/logid/DeviceMonitor.h +++ b/src/logid/DeviceMonitor.h @@ -38,6 +38,7 @@ namespace logid private: std::mutex devices_mutex; std::map> devices; + backend::hidpp::EventHandler eventHandler; }; extern DeviceMonitor* finder; diff --git a/src/logid/backend/Error.cpp b/src/logid/backend/Error.cpp new file mode 100644 index 0000000..c224f76 --- /dev/null +++ b/src/logid/backend/Error.cpp @@ -0,0 +1,6 @@ +#include "Error.h" + +const char *logid::backend::TimeoutError::what() noexcept +{ + return "Device timed out"; +} diff --git a/src/logid/backend/Error.h b/src/logid/backend/Error.h new file mode 100644 index 0000000..20865cf --- /dev/null +++ b/src/logid/backend/Error.h @@ -0,0 +1,16 @@ +#ifndef LOGID_BACKEND_ERROR_H +#define LOGID_BACKEND_ERROR_H + +#include + +namespace logid { +namespace backend { +class TimeoutError: public std::exception +{ +public: + TimeoutError() = default; + virtual const char* what() noexcept; +}; +}} + +#endif //LOGID_BACKEND_ERROR_H \ No newline at end of file diff --git a/src/logid/backend/defs.h b/src/logid/backend/defs.h new file mode 100644 index 0000000..8ef5af1 --- /dev/null +++ b/src/logid/backend/defs.h @@ -0,0 +1,15 @@ +#ifndef LOGID_BACKEND_DEFS_H +#define LOGID_BACKEND_DEFS_H + +#include + +namespace logid::backend +{ + struct RawEventHandler + { + std::function& )> condition; + std::function& )> callback; + }; +} + +#endif //LOGID_BACKEND_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 9b517bb..38a2764 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -1,3 +1,4 @@ +#include #include "Device.h" #include "Report.h" @@ -21,10 +22,67 @@ Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept } /// TODO: Initialize a single RawDevice for each path. -Device::Device(std::string path, DeviceIndex index): +Device::Device(const std::string& path, DeviceIndex index): raw_device (std::make_shared(path)), path (path), index (index) { supported_reports = getSupportedReports(raw_device->reportDescriptor()); if(!supported_reports) throw InvalidDevice(InvalidDevice::NoHIDPPReport); -} \ No newline at end of file + + // Pass all HID++ events with device index to this device. + RawEventHandler rawEventHandler; + rawEventHandler.condition = [index](std::vector& report)->bool + { + return (report[Offset::Type] == Report::Short || + report[Offset::Type] == Report::Long) && (report[Offset::DeviceIndex] == 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::addEventHandler(const std::string& nickname, EventHandler& handler) +{ + auto it = event_handlers.find(nickname); + assert(it == event_handlers.end()); + + event_handlers.emplace(nickname, handler); +} + +void Device::removeEventHandler(const std::string& nickname) +{ + event_handlers.erase(nickname); +} + +void Device::handleEvent(Report& report) +{ + for(auto& handler : event_handlers) + if(handler.second.condition(report)) + handler.second.callback(report); +} + +Report Device::sendReport(Report& report) +{ + switch(report.type()) + { + case Report::Short: + if(!(supported_reports & HIDPP_REPORT_SHORT_SUPPORTED)) + report.setType(Report::Long); + break; + case Report::Long: + /* Report can be truncated, but that isn't a good idea. */ + assert(supported_reports & HIDPP_REPORT_LONG_SUPPORTED); + } + + auto raw_response = raw_device->sendReport(report.rawReport()); + return Report(raw_response); +} + +void Device::listen() +{ + raw_device->listen(); +} diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 70e0956..fcaf732 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -3,22 +3,21 @@ #include #include +#include +#include #include "../raw/RawDevice.h" +#include "Report.h" +#include "defs.h" -namespace logid::backend::hidpp +namespace logid { +namespace backend { +namespace hidpp { - enum DeviceIndex: uint8_t + struct EventHandler { - DefaultDevice = 0, - WirelessDevice1 = 1, - WirelessDevice2 = 2, - WirelessDevice3 = 3, - WirelessDevice4 = 4, - WirelessDevice5 = 5, - WirelessDevice6 = 6, - CordedDevice = 0xff + std::function condition; + std::function callback; }; - class Device { public: @@ -35,17 +34,30 @@ namespace logid::backend::hidpp virtual Reason code() const noexcept; private: Reason _reason; - }; - Device(std::string path, DeviceIndex index); + + Device(const std::string& path, DeviceIndex index); + std::string devicePath() const { return path; } DeviceIndex deviceIndex() const { return index; } + + void listen(); // Runs asynchronously + void stopListening(); + + void addEventHandler(const std::string& nickname, EventHandler& handler); + void removeEventHandler(const std::string& nickname); + + Report sendReport(Report& report); + + void handleEvent(Report& report); private: - std::shared_ptr raw_device; + std::shared_ptr raw_device; std::string path; DeviceIndex index; uint8_t supported_reports; + + std::map event_handlers; }; -} +} } } #endif //LOGID_HIDPP_DEVICE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index c9b27b4..0666db7 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "Report.h" using namespace logid::backend::hidpp; @@ -80,4 +81,80 @@ uint8_t hidpp::getSupportedReports(std::vector&& rdesc) ret |= HIDPP_REPORT_LONG_SUPPORTED; return ret; -} \ No newline at end of file +} + +const char *Report::InvalidReportID::what() const noexcept +{ + return "Invalid report ID"; +} + +const char *Report::InvalidReportLength::what() const noexcept +{ + return "Invalid report length"; +} + +Report::Report(Report::Type type, DeviceIndex device_index, + uint8_t feature_index, uint8_t function, uint8_t sw_id) +{ + assert(!(function & functionMask)); + assert(!(sw_id & swIdMask)); + + switch(type) + { + case Short: + _data.resize(HeaderLength + ShortParamLength); + break; + case Long: + _data.resize(HeaderLength + LongParamLength); + break; + default: + throw InvalidReportID(); + } + + _data[Offset::Type] = type; + _data[Offset::DeviceIndex] = device_index; + _data[Offset::Feature] = feature_index; + _data[Offset::Function] = (function & functionMask) << 4 | (sw_id & swIdMask); +} + +Report::Report(const std::vector& data) +{ + _data = data; + + switch(_data[Offset::Type]) + { + case Short: + _data.resize(HeaderLength + ShortParamLength); + break; + case Long: + _data.resize(HeaderLength + LongParamLength); + break; + default: + throw InvalidReportID(); + } +} + +void Report::setType(Report::Type type) +{ + switch(type) + { + case Short: + _data.resize(HeaderLength + ShortParamLength); + break; + case Long: + _data.resize(HeaderLength + LongParamLength); + break; + default: + throw InvalidReportID(); + } + + _data[Offset::Type] = type; +} + +void Report::setParams(const std::vector& _params) +{ + assert(_params.size() <= _data.size()-HeaderLength); + + for(std::size_t i = 0; i < _params.size(); i++) + _data[Offset::Parameters + i] = _params[i]; +} diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index d69c4b6..90e10b7 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -3,9 +3,7 @@ #include #include "../raw/RawDevice.h" -#include "Device.h" - -#define LOGID_HIDPP_SW_ID 0x0f +#include "defs.h" /* Some devices only support a subset of these reports */ #define HIDPP_REPORT_SHORT_SUPPORTED 1U @@ -15,6 +13,16 @@ namespace logid::backend::hidpp { uint8_t getSupportedReports(std::vector&& rdesc); + + namespace Offset + { + static constexpr uint8_t Type = 0; + static constexpr uint8_t DeviceIndex = 1; + static constexpr uint8_t Feature = 2; + static constexpr uint8_t Function = 3; + static constexpr uint8_t Parameters = 4; + } + class Report { public: @@ -24,30 +32,43 @@ namespace logid::backend::hidpp Long = 0x11 }; - class InvalidReportID: std::exception + class InvalidReportID: public std::exception { - InvalidReportID(); + public: + InvalidReportID() = default; virtual const char* what() const noexcept; }; - class InvalidReportLength: std::exception + class InvalidReportLength: public std::exception { - InvalidReportLength(); + public: + InvalidReportLength() = default;; virtual const char* what() const noexcept; }; - static constexpr std::size_t MaxDataLength = 32; + static constexpr std::size_t MaxDataLength = 20; + static constexpr uint8_t swIdMask = 0x0f; + static constexpr uint8_t functionMask = 0x0f; - Report(uint8_t report_id, const uint8_t* data, std::size_t length); - Report(std::vector data); + Report(Type type, DeviceIndex device_index, + uint8_t feature_index, + uint8_t function, + uint8_t sw_id); + explicit Report(const std::vector& data); - Type type() const; + Type type() const { return static_cast(_data[Offset::Type]); }; void setType(Report::Type type); - logid::backend::hidpp::DeviceIndex deviceIndex(); + std::vector::const_iterator paramBegin() const { return _data.begin() + Offset::Parameters; } + std::vector::const_iterator paramEnd() const { return _data.end(); } + void setParams(const std::vector& _params); + + 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; std::vector _data; diff --git a/src/logid/backend/hidpp/defs.h b/src/logid/backend/hidpp/defs.h new file mode 100644 index 0000000..5539587 --- /dev/null +++ b/src/logid/backend/hidpp/defs.h @@ -0,0 +1,24 @@ +#ifndef LOGID_HIDPP_DEFS_H +#define LOGID_HIDPP_DEFS_H + +#define LOGID_HIDPP_SOFTWARE_ID 1 + +namespace logid::backend::hidpp +{ + enum DeviceIndex: uint8_t + { + DefaultDevice = 0, + WirelessDevice1 = 1, + WirelessDevice2 = 2, + WirelessDevice3 = 3, + WirelessDevice4 = 4, + WirelessDevice5 = 5, + WirelessDevice6 = 6, + CordedDevice = 0xff + }; + + static constexpr std::size_t ShortParamLength = 3; + static constexpr std::size_t LongParamLength = 16; +} + +#endif //LOGID_HIDPP_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 22513ae..e334d5b 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -1,9 +1,11 @@ #include "RawDevice.h" +#include "../Error.h" #include #include -#include +#include +#define MAX_DATA_LENGTH 32 extern "C" { @@ -76,29 +78,43 @@ RawDevice::~RawDevice() } } -void RawDevice::sendReport(std::vector report) +std::vector RawDevice::sendReport(const std::vector& report) { - _sendReport(std::move(report)); + std::packaged_task()> task( + [=]() { + _sendReport(report); + std::vector response; + _readReport(response, MAX_DATA_LENGTH); + return response; + }); + + /* If the listener will stop, handle I/O manually. + * Otherwise, push to queue and wait for result. */ + if(continue_listen) + { + auto f = task.get_future(); + write_queue.push(&task); + return f.get(); + } + else + return task.get_future().get(); } -std::vector RawDevice::readReport(std::size_t maxDataLength) -{ - return _readReport(maxDataLength); -} - -void RawDevice::_sendReport(std::vector report) +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"); + + return ret; } -std::vector RawDevice::_readReport(std::size_t maxDataLength) +int RawDevice::_readReport(std::vector& report, std::size_t maxDataLength) { std::lock_guard lock(dev_io); int ret; - std::vector report(maxDataLength); + report.resize(maxDataLength); timeval timeout = { duration_cast(HIDPP_IO_TIMEOUT).count(), duration_cast(HIDPP_IO_TIMEOUT).count() }; @@ -133,7 +149,10 @@ std::vector RawDevice::_readReport(std::size_t maxDataLength) throw std::system_error(errno, std::system_category(), "_readReport read pipe failed"); } - return report; + if(0 == ret) + throw backend::TimeoutError(); + + return ret; } void RawDevice::interruptRead() @@ -144,4 +163,62 @@ void RawDevice::interruptRead() // Ensure I/O has halted std::lock_guard lock(dev_io); -} \ No newline at end of file +} + +void RawDevice::listen() +{ + std::lock_guard lock(listening); + + continue_listen = true; + while(continue_listen) + { + while(!write_queue.empty()) + { + auto task = write_queue.front(); + (*task)(); + write_queue.pop(); + } + std::vector report; + _readReport(report, MAX_DATA_LENGTH); + std::thread([this](std::vector report) { + this->handleEvent(report); + }, report).detach(); + } + + continue_listen = false; +} + +void RawDevice::stopListener() +{ + continue_listen = false; + interruptRead(); +} + +void RawDevice::addEventHandler(const std::string &nickname, RawEventHandler &handler) +{ + auto it = event_handlers.find(nickname); + assert(it == event_handlers.end()); + event_handlers.emplace(nickname, handler); +} + +void RawDevice::removeEventHandler(const std::string &nickname) +{ + event_handlers.erase(nickname); +} + +void RawDevice::handleEvent(std::vector &report) +{ + for(auto& handler : event_handlers) + if(handler.second.condition(report)) + handler.second.callback(report); +} + +bool RawDevice::isListening() +{ + bool ret = listening.try_lock(); + + if(ret) + listening.unlock(); + + return ret; +} diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index c6c88fc..e7baba6 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -4,10 +4,18 @@ #include #include #include +#include +#include +#include + +#include "../defs.h" +#include "../../util/mutex_queue.h" #define HIDPP_IO_TIMEOUT std::chrono::seconds(2) -namespace logid::backend::raw +namespace logid { +namespace backend { +namespace raw { class RawDevice { @@ -18,12 +26,18 @@ namespace logid::backend::raw std::vector reportDescriptor() const { return rdesc; } /// TODO: Process reports in a queue. - void sendReport(std::vector report); - std::vector readReport(std::size_t maxDataLength); - + std::vector sendReport(const std::vector& report); void interruptRead(); + + void listen(); + void stopListener(); + bool isListening(); + + void addEventHandler(const std::string& nickname, RawEventHandler& handler); + void removeEventHandler(const std::string& nickname); + private: - std::mutex dev_io; + std::mutex dev_io, listening; std::string path; int fd; int dev_pipe[2]; @@ -32,10 +46,17 @@ namespace logid::backend::raw std::string name; std::vector rdesc; + std::atomic continue_listen; + + std::map event_handlers; + void handleEvent(std::vector& report); + /* These will only be used internally and processed with a queue */ - void _sendReport(std::vector report); - std::vector _readReport(std::size_t maxDataLength); + int _sendReport(const std::vector& report); + int _readReport(std::vector& report, std::size_t maxDataLength); + + mutex_queue()>*> write_queue; }; -} +}}} #endif //LOGID_BACKEND_RAWDEVICE_H \ No newline at end of file diff --git a/src/logid/util/mutex_queue.h b/src/logid/util/mutex_queue.h new file mode 100644 index 0000000..153b1a5 --- /dev/null +++ b/src/logid/util/mutex_queue.h @@ -0,0 +1,37 @@ +#ifndef MUTEX_QUEUE_H +#define MUTEX_QUEUE_H + +#include +#include + +template +class mutex_queue +{ +public: + mutex_queue() = default; + bool empty() + { + std::lock_guard lock(_mutex); + return _queue.empty(); + } + data& front() + { + std::lock_guard lock(_mutex); + return _queue.front(); + } + void push(const data& _data) + { + std::lock_guard lock(_mutex); + _queue.push(_data); + } + void pop() + { + std::lock_guard lock(_mutex); + _queue.pop(); + } +private: + std::queue _queue; + std::mutex _mutex; +}; + +#endif //MUTEX_QUEUE_H \ No newline at end of file From 91954e8a7330ba0fe5fbd9e2d6942682b6513978 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 17 Jun 2020 04:15:00 -0400 Subject: [PATCH 07/81] Use shared_ptr for devices in DeviceMonitor --- src/logid/DeviceMonitor.cpp | 19 +++++++++---------- src/logid/DeviceMonitor.h | 4 ++-- src/logid/backend/hidpp/Device.cpp | 6 +++--- src/logid/backend/hidpp/Device.h | 4 ++-- src/logid/backend/raw/RawDevice.cpp | 2 +- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/logid/DeviceMonitor.cpp b/src/logid/DeviceMonitor.cpp index e69bda4..3027e6d 100644 --- a/src/logid/DeviceMonitor.cpp +++ b/src/logid/DeviceMonitor.cpp @@ -102,29 +102,28 @@ void DeviceMonitor::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceI void DeviceMonitor::addDevice(std::string path) { try { - backend::hidpp::Device device(path, hidpp::DeviceIndex::WirelessDevice1); + auto device = std::make_shared(path, hidpp::DeviceIndex::WirelessDevice1); log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); - backend::hidpp::EventHandler eventHandler; - eventHandler.condition = [device](backend::hidpp::Report& report)->bool + auto eventHandler = std::make_shared(); + eventHandler->condition = [device](backend::hidpp::Report& report)->bool { return true; }; - eventHandler.callback = [device](backend::hidpp::Report& report)->void + eventHandler->callback = [device](backend::hidpp::Report& report)->void { - log_printf(DEBUG, "Event on %s:%d", device.devicePath().c_str(), - device.deviceIndex()); + log_printf(DEBUG, "Event on %s:%d", device->devicePath().c_str(), + device->deviceIndex()); for(auto& i : report.rawReport()) printf("%02x ", i); printf("\n"); }; - device.addEventHandler("MONITOR_ALL", eventHandler); + device->addEventHandler("MONITOR_ALL", eventHandler); - std::thread([](backend::hidpp::Device device) { device.listen(); }, device).detach(); + devices.push_back(device); - /* This is a temporary solution to avoid std::bad_function_call */ - while(true) {} + std::thread([device]() { device->listen(); }).detach(); } catch(backend::hidpp::Device::InvalidDevice &e) { diff --git a/src/logid/DeviceMonitor.h b/src/logid/DeviceMonitor.h index 159970e..f95f642 100644 --- a/src/logid/DeviceMonitor.h +++ b/src/logid/DeviceMonitor.h @@ -37,8 +37,8 @@ namespace logid void removeDevice(std::string path) override; private: std::mutex devices_mutex; - std::map> devices; - backend::hidpp::EventHandler eventHandler; + std::vector> devices; //tmp + //std::map> devices; }; extern DeviceMonitor* finder; diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 38a2764..680ff91 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -45,7 +45,7 @@ Device::Device(const std::string& path, DeviceIndex index): raw_device->addEventHandler("DEV_" + std::to_string(index), rawEventHandler); } -void Device::addEventHandler(const std::string& nickname, EventHandler& handler) +void Device::addEventHandler(const std::string& nickname, const std::shared_ptr& handler) { auto it = event_handlers.find(nickname); assert(it == event_handlers.end()); @@ -61,8 +61,8 @@ void Device::removeEventHandler(const std::string& nickname) void Device::handleEvent(Report& report) { for(auto& handler : event_handlers) - if(handler.second.condition(report)) - handler.second.callback(report); + if(handler.second->condition(report)) + handler.second->callback(report); } Report Device::sendReport(Report& report) diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index fcaf732..8d5aa95 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -44,7 +44,7 @@ namespace hidpp void listen(); // Runs asynchronously void stopListening(); - void addEventHandler(const std::string& nickname, EventHandler& handler); + void addEventHandler(const std::string& nickname, const std::shared_ptr& handler); void removeEventHandler(const std::string& nickname); Report sendReport(Report& report); @@ -56,7 +56,7 @@ namespace hidpp DeviceIndex index; uint8_t supported_reports; - std::map event_handlers; + std::map> event_handlers; }; } } } diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index e334d5b..473ad25 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -194,7 +194,7 @@ void RawDevice::stopListener() interruptRead(); } -void RawDevice::addEventHandler(const std::string &nickname, RawEventHandler &handler) +void RawDevice::addEventHandler(const std::string& nickname, RawEventHandler& handler) { auto it = event_handlers.find(nickname); assert(it == event_handlers.end()); From 14d07c220e207294de02c06b64639cc33cc442df Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 17 Jun 2020 05:40:49 -0400 Subject: [PATCH 08/81] Create enum of feature IDs --- src/logid/backend/hidpp20/feature_defs.h | 102 +++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/logid/backend/hidpp20/feature_defs.h diff --git a/src/logid/backend/hidpp20/feature_defs.h b/src/logid/backend/hidpp20/feature_defs.h new file mode 100644 index 0000000..72de1e1 --- /dev/null +++ b/src/logid/backend/hidpp20/feature_defs.h @@ -0,0 +1,102 @@ +#ifndef LOGID_BACKEND_HIDPP20_FEATUREDEFS +#define LOGID_BACKEND_HIDPP20_FEATUREDEFS + +#include + +namespace logid { +namespace backend { +namespace hidpp20 { + enum FeatureID : uint16_t + { + ROOT = 0x0000, + FEATURE_SET = 0x0001, + FEATURE_INFO = 0x0002, + FW_VERSION = 0x0003, + DEVICE_NAME = 0x0005, + DEVICE_GROUPS = 0x0006, + DEVICE_FRIENDLY_NAME = 0x0007, + RESET = 0x0020, + CRYPTO_IDENTIFIER = 0x0021, + DFUCONTROL = 0x00c0, + DFUCONTROL_V2 = 0x00c1, + DFUCONTROL_V3 = 0x00c2, + DFU = 0xd000, + BATTERY_STATUS = 0x1000, + BATTERY_VOLTAGE = 0x1001, + CHARGING_CONTROL = 0x1010, + LED_CONTROL = 0x1300, + GENERIC_TEST = 0x1800, + DEVICE_RESET = 0x1802, + OOB_STATE = 0x1805, + CONFIGURABLE_DEVICE_PROPERTIES = 0x1806, + CHANGE_HOST = 0x1814, + HOSTS_INFO = 0x1815, + BACKLIGHT = 0x1981, + BACKLIGHT_V2 = 0x1982, + BACKLIGHT_V3 = 0x1983, + PRESENTER_CONTROL = 0x1a00, + SENSOR_3D = 0x1a01, + REPROG_CONTROLS = 0x1b00, + REPROG_CONTROLS_V2 = 0x1b01, + REPROG_CONTROLS_V2_2 = 0x1b02, + REPROG_CONTROLS_V3 = 0x1b03, + REPROG_CONTROLS_V4 = 0x1b04, + PERSISTENT_REMAPPABLE_ACTION = 0x1bc0, + WIRELESS_DEVICE_STATUS = 0x1d4b, + ENABLE_HIDDEN_FEATURE = 0x1e00, + FIRMWARE_PROPERTIES = 0x1f1f, + ADC_MEASUREMENT = 0x1f20, + LEFT_RIGHT_SWAP = 0x2001, + SWAP_BUTTON = 0x2005, + POINTER_AXES_ORIENTATION = 0x2006, + VERTICAL_SCROLLING = 0x2100, + SMART_SHIFT = 0x2110, + HIRES_SCROLLING = 0x2120, + HIRES_SCROLLING_V2 = 0x2121, // Referred to as Hi-res wheel in cvuchener/hidpp, seems to be V2? + LORES_SCROLLING = 0x2130, + MOUSE_POINTER = 0x2200, // Possibly predecessor to 0x2201? + ADJUSTABLE_DPI = 0x2201, + ANGLE_SNAPPING = 0x2230, + SURFACE_TUNING = 0x2240, + HYBRID_TRACKING = 0x2400, + FN_INVERSION = 0x40a0, + FN_INVERSION_V2 = 0x40a2, // Is 0x40a1 skipped? + FN_INVERSION_V3 = 0x40a3, + ENCRYPTION = 0x4100, + LOCK_KEY_STATE = 0x4220, + SOLAR_DASHBOARD = 0x4301, + KEYBOARD_LAYOUT = 0x4520, + KEYBOARD_DISABLE = 0x4521, + DISABLE_KEYS = 0x4522, + MULTIPLATFORM = 0x4530, // Dual platform only? + MULTIPLATFORM_V2 = 0x4531, + KEYBOARD_LAYOUT_V2 = 0x4540, + CROWN = 0x4600, + TOUCHPAD_FW = 0x6010, + TOUCHPAD_SW = 0x6011, + TOUCHPAD_FW_WIN8 = 0x6012, + TOUCHMOUSE_RAW = 0x6100, + // TOUCHMOUSE_6120 = 0x6120, (Keeping this commented out until a better name is found) + GESTURE = 0x6500, + GESTURE_V2 = 0x6501, + G_KEY = 0x8010, + M_KEY = 0x8020, + // MR = 0x8030, (Keeping this commented out until a better name is found) + BRIGHTNESS_CONTROL = 0x8040, + REPORT_RATE = 0x8060, + RGB_EFFECTS = 0x8070, + RGB_EFFECTS_V2 = 0x8071, + PER_KEY_LIGHTING = 0x8080, + PER_KEY_LIGHTING_V2 = 0x8081, + MODE_STATUS = 0x8100, + MOUSE_BUTTON_SPY = 0x8110, + LATENCY_MONITORING = 0x8111, + GAMING_ATTACHMENTS = 0x8120, + FORCE_FEEDBACK = 0x8123, + SIDETONE = 0x8300, + EQUALIZER = 0x8310, + HEADSET_OUT = 0x8320 + }; +}}} + +#endif //LOGID_BACKEND_HIDPP20_FEATUREDEFS \ No newline at end of file From c21a923ab244a70fc29c7dbd64d74246e3888597 Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 18 Jun 2020 01:34:25 -0400 Subject: [PATCH 09/81] Print version number of device 1 on each raw dev. Only works on HID++ >=2.0 so far. Also solves a race condition where the wrong response can be sent to a request. --- src/logid/CMakeLists.txt | 4 + src/logid/DeviceMonitor.cpp | 3 + src/logid/backend/dj/defs.h | 20 ++ src/logid/backend/hidpp/Device.cpp | 19 +- src/logid/backend/hidpp/Device.h | 3 + src/logid/backend/hidpp/Report.cpp | 23 ++- src/logid/backend/hidpp/Report.h | 21 ++- src/logid/backend/hidpp/defs.h | 9 + src/logid/backend/hidpp20/Device.cpp | 25 +++ src/logid/backend/hidpp20/Device.h | 19 ++ src/logid/backend/hidpp20/Error.cpp | 46 +++++ src/logid/backend/hidpp20/Error.h | 39 ++++ src/logid/backend/hidpp20/Feature.cpp | 28 +++ src/logid/backend/hidpp20/Feature.h | 37 ++++ src/logid/backend/hidpp20/feature_defs.h | 191 +++++++++++--------- src/logid/backend/hidpp20/features/Root.cpp | 25 +++ src/logid/backend/hidpp20/features/Root.h | 37 ++++ src/logid/backend/raw/RawDevice.cpp | 86 ++++++++- src/logid/backend/raw/RawDevice.h | 6 +- 19 files changed, 518 insertions(+), 123 deletions(-) create mode 100644 src/logid/backend/dj/defs.h create mode 100644 src/logid/backend/hidpp20/Device.cpp create mode 100644 src/logid/backend/hidpp20/Device.h create mode 100644 src/logid/backend/hidpp20/Error.cpp create mode 100644 src/logid/backend/hidpp20/Error.h create mode 100644 src/logid/backend/hidpp20/Feature.cpp create mode 100644 src/logid/backend/hidpp20/Feature.h create mode 100644 src/logid/backend/hidpp20/features/Root.cpp create mode 100644 src/logid/backend/hidpp20/features/Root.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index e1c0924..e4e10d9 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -17,6 +17,10 @@ add_executable(logid backend/raw/RawDevice.cpp backend/hidpp/Device.cpp backend/hidpp/Report.cpp + backend/hidpp20/Device.cpp + backend/hidpp20/Error.cpp + backend/hidpp20/Feature.cpp + backend/hidpp20/features/Root.cpp backend/dj/Report.cpp util/mutex_queue.h) diff --git a/src/logid/DeviceMonitor.cpp b/src/logid/DeviceMonitor.cpp index 3027e6d..7c4c235 100644 --- a/src/logid/DeviceMonitor.cpp +++ b/src/logid/DeviceMonitor.cpp @@ -105,6 +105,9 @@ void DeviceMonitor::addDevice(std::string path) auto device = std::make_shared(path, hidpp::DeviceIndex::WirelessDevice1); log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); + auto version = device->version(); + log_printf(DEBUG, "HID++ version: %d.%d", std::get<0>(version), std::get<1>(version)); + auto eventHandler = std::make_shared(); eventHandler->condition = [device](backend::hidpp::Report& report)->bool { diff --git a/src/logid/backend/dj/defs.h b/src/logid/backend/dj/defs.h new file mode 100644 index 0000000..3f373c2 --- /dev/null +++ b/src/logid/backend/dj/defs.h @@ -0,0 +1,20 @@ +#ifndef LOGID_BACKEND_DJ_DEFS_H +#define LOGID_BACKEND_DJ_DEFS_H + +#include + +namespace logid { +namespace backend { +namespace dj +{ + namespace ReportType + { + enum ReportType : uint8_t + { + Short = 0x20, + Long = 0x21 + }; + } +}}} + +#endif //LOGID_BACKEND_DJ_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 680ff91..d135045 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -1,6 +1,7 @@ #include #include "Device.h" #include "Report.h" +#include "../hidpp20/features/Root.h" using namespace logid::backend; using namespace logid::backend::hidpp; @@ -29,12 +30,20 @@ Device::Device(const std::string& path, DeviceIndex index): if(!supported_reports) throw InvalidDevice(InvalidDevice::NoHIDPPReport); + Report versionRequest(Report::Type::Short, index, hidpp20::FeatureID::ROOT, + hidpp20::Root::Ping, LOGID_HIDPP_SOFTWARE_ID); + + ///TODO: Catch error + auto versionResponse = sendReport(versionRequest); + auto versionResponse_params = versionResponse.paramBegin(); + _version = std::make_tuple(versionResponse_params[0], versionResponse_params[1]); + // Pass all HID++ events with device index to this device. RawEventHandler rawEventHandler; rawEventHandler.condition = [index](std::vector& report)->bool { - return (report[Offset::Type] == Report::Short || - report[Offset::Type] == Report::Long) && (report[Offset::DeviceIndex] == index); + return (report[Offset::Type] == Report::Type::Short || + report[Offset::Type] == Report::Type::Long) && (report[Offset::DeviceIndex] == index); }; rawEventHandler.callback = [this](std::vector& report)->void { @@ -69,11 +78,11 @@ Report Device::sendReport(Report& report) { switch(report.type()) { - case Report::Short: + case Report::Type::Short: if(!(supported_reports & HIDPP_REPORT_SHORT_SUPPORTED)) - report.setType(Report::Long); + report.setType(Report::Type::Long); break; - case Report::Long: + case Report::Type::Long: /* Report can be truncated, but that isn't a good idea. */ assert(supported_reports & HIDPP_REPORT_LONG_SUPPORTED); } diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 8d5aa95..0e91b76 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -40,6 +40,7 @@ namespace hidpp std::string devicePath() const { return path; } DeviceIndex deviceIndex() const { return index; } + std::tuple version() const { return _version; } void listen(); // Runs asynchronously void stopListening(); @@ -56,6 +57,8 @@ namespace hidpp DeviceIndex index; uint8_t supported_reports; + std::tuple _version; + std::map> event_handlers; }; } } } diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index 0666db7..22274c1 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -96,15 +96,15 @@ const char *Report::InvalidReportLength::what() const noexcept Report::Report(Report::Type type, DeviceIndex device_index, uint8_t feature_index, uint8_t function, uint8_t sw_id) { - assert(!(function & functionMask)); - assert(!(sw_id & swIdMask)); + assert(function <= functionMask); + assert(sw_id <= swIdMask); switch(type) { - case Short: + case Type::Short: _data.resize(HeaderLength + ShortParamLength); break; - case Long: + case Type::Long: _data.resize(HeaderLength + LongParamLength); break; default: @@ -123,10 +123,10 @@ Report::Report(const std::vector& data) switch(_data[Offset::Type]) { - case Short: + case Type::Short: _data.resize(HeaderLength + ShortParamLength); break; - case Long: + case Type::Long: _data.resize(HeaderLength + LongParamLength); break; default: @@ -138,10 +138,10 @@ void Report::setType(Report::Type type) { switch(type) { - case Short: + case Type::Short: _data.resize(HeaderLength + ShortParamLength); break; - case Long: + case Type::Long: _data.resize(HeaderLength + LongParamLength); break; default: @@ -158,3 +158,10 @@ void Report::setParams(const std::vector& _params) for(std::size_t i = 0; i < _params.size(); i++) _data[Offset::Parameters + i] = _params[i]; } + +bool Report::isError20(Report::hidpp20_error* error) +{ + if(_data[Offset::Type] != Type::Long || + _data[Offset::Feature] != 0xff) + return false; +} \ No newline at end of file diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index 90e10b7..1ad7f92 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -26,11 +26,7 @@ namespace logid::backend::hidpp class Report { public: - enum Type: uint8_t - { - Short = 0x10, - Long = 0x11 - }; + typedef ReportType::ReportType Type; class InvalidReportID: public std::exception { @@ -50,19 +46,26 @@ namespace logid::backend::hidpp static constexpr uint8_t swIdMask = 0x0f; static constexpr uint8_t functionMask = 0x0f; - Report(Type type, DeviceIndex device_index, + Report(Report::Type type, DeviceIndex device_index, uint8_t feature_index, uint8_t function, uint8_t sw_id); explicit Report(const std::vector& data); - Type type() const { return static_cast(_data[Offset::Type]); }; + Report::Type type() const { return static_cast(_data[Offset::Type]); }; void setType(Report::Type type); - std::vector::const_iterator paramBegin() const { return _data.begin() + Offset::Parameters; } - std::vector::const_iterator paramEnd() const { return _data.end(); } + std::vector::iterator paramBegin() { return _data.begin() + Offset::Parameters; } + std::vector::iterator paramEnd() { return _data.end(); } void setParams(const std::vector& _params); + struct hidpp20_error + { + uint8_t feature_index, function, software_id, error_code; + }; + + bool isError20(hidpp20_error* error); + logid::backend::hidpp::DeviceIndex deviceIndex() { return static_cast(_data[Offset::DeviceIndex]); diff --git a/src/logid/backend/hidpp/defs.h b/src/logid/backend/hidpp/defs.h index 5539587..d03b347 100644 --- a/src/logid/backend/hidpp/defs.h +++ b/src/logid/backend/hidpp/defs.h @@ -5,6 +5,15 @@ namespace logid::backend::hidpp { + namespace ReportType + { + enum ReportType : uint8_t + { + Short = 0x10, + Long = 0x11 + }; + } + enum DeviceIndex: uint8_t { DefaultDevice = 0, diff --git a/src/logid/backend/hidpp20/Device.cpp b/src/logid/backend/hidpp20/Device.cpp new file mode 100644 index 0000000..917601c --- /dev/null +++ b/src/logid/backend/hidpp20/Device.cpp @@ -0,0 +1,25 @@ +#include + +#include "Device.h" +#include "../hidpp/defs.h" + +using namespace logid::backend::hidpp20; + +std::vector Device::callFunction(uint8_t feature_index, + uint8_t function, 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, deviceIndex(), feature_index, function, + LOGID_HIDPP_SOFTWARE_ID); + std::copy(params.begin(), params.end(), request.paramBegin()); + + auto response = this->sendReport(request); + return std::vector(response.paramBegin(), response.paramEnd()); +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Device.h b/src/logid/backend/hidpp20/Device.h new file mode 100644 index 0000000..38f42eb --- /dev/null +++ b/src/logid/backend/hidpp20/Device.h @@ -0,0 +1,19 @@ +#ifndef LOGID_HIDPP20_DEVICE_H +#define LOGID_HIDPP20_DEVICE_H + +#include "../hidpp/Device.h" +#include + +namespace logid { +namespace backend { +namespace hidpp20 { + class Device : public hidpp::Device + { + public: + std::vector callFunction(uint8_t feature_index, + uint8_t function, + std::vector& params); + }; +}}} + +#endif //LOGID_HIDPP20_DEVICE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Error.cpp b/src/logid/backend/hidpp20/Error.cpp new file mode 100644 index 0000000..33fdfe1 --- /dev/null +++ b/src/logid/backend/hidpp20/Error.cpp @@ -0,0 +1,46 @@ +#include +#include +#include "Error.h" + +using namespace logid::backend::hidpp20; + +Error::Error(uint8_t code) : _code (code) +{ + assert(_code != NoError); +} + +const char* Error::what() const noexcept +{ + switch(_code) + { + case NoError: + return "No error"; + case Unknown: + return "Unknown"; + case InvalidArgument: + return "Invalid argument"; + case OutOfRange: + return "Out of range"; + case HardwareError: + return "Hardware error"; + case LogitechInternal: + return "Logitech internal feature"; + case InvalidFeatureIndex: + return "Invalid feature index"; + case InvalidFunctionID: + return "Invalid function ID"; + case Busy: + return "Busy"; + case Unsupported: + return "Unsupported"; + case UnknownDevice: + return "Unknown device"; + default: + return std::string("Unknown error code " + std::to_string(_code)).c_str(); + } +} + +uint8_t Error::code() const noexcept +{ + return _code; +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Error.h b/src/logid/backend/hidpp20/Error.h new file mode 100644 index 0000000..53fec65 --- /dev/null +++ b/src/logid/backend/hidpp20/Error.h @@ -0,0 +1,39 @@ +#ifndef LOGID_BACKEND_HIDPP20_ERROR_H +#define LOGID_BACKEND_HIDPP20_ERROR_H + +#include +#include + +namespace logid { +namespace backend { +namespace hidpp20 { + static constexpr uint8_t ErrorID = 0xFF; + + class Error: public std::exception + { + public: + enum ErrorCode: uint8_t { + NoError = 0, + Unknown = 1, + InvalidArgument = 2, + OutOfRange = 3, + HardwareError = 4, + LogitechInternal = 5, + InvalidFeatureIndex = 6, + InvalidFunctionID = 7, + Busy = 8, + Unsupported = 9, + UnknownDevice = 10 + }; + + Error(uint8_t code); + + virtual const char* what() const noexcept; + uint8_t code() const noexcept; + + private: + uint8_t _code; + }; +}}} + +#endif //LOGID_BACKEND_HIDPP20_ERROR_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Feature.cpp b/src/logid/backend/hidpp20/Feature.cpp new file mode 100644 index 0000000..58abb57 --- /dev/null +++ b/src/logid/backend/hidpp20/Feature.cpp @@ -0,0 +1,28 @@ +#include "Feature.h" + +using namespace logid::backend::hidpp20; + +const char* Feature::UnsupportedFeature::what() const noexcept +{ + return "Unsupported feature"; +} + +uint16_t Feature::UnsupportedFeature::code() const noexcept +{ + return _f_id; +} + +std::vector Feature::callFunction(uint8_t function_id, std::vector& params) +{ + return _device->callFunction(_index, function_id, params); +} + +Feature::Feature(Device* dev, uint16_t _id) : _device (dev), _index (0xff) +{ + ///TODO: Set index +} + +Feature::Feature(Device* dev, uint8_t _index) : _device (dev), _index (_index) +{ + +} diff --git a/src/logid/backend/hidpp20/Feature.h b/src/logid/backend/hidpp20/Feature.h new file mode 100644 index 0000000..dcea923 --- /dev/null +++ b/src/logid/backend/hidpp20/Feature.h @@ -0,0 +1,37 @@ +#ifndef LOGID_HIDPP20_FEATURE_H +#define LOGID_HIDPP20_FEATURE_H + +#include +#include "Device.h" + +namespace logid { +namespace backend { +namespace hidpp20 { + class Feature + { + class UnsupportedFeature : public std::exception + { + public: + explicit UnsupportedFeature(uint16_t ID) : _f_id (ID) {} + virtual const char* what() const noexcept; + uint16_t code() const noexcept; + private: + uint16_t _f_id; + }; + + public: + static const uint16_t ID; + virtual uint16_t getID() = 0; + + protected: + explicit Feature(Device* dev, uint16_t _id); + explicit Feature(Device* dev, uint8_t _index); + std::vector callFunction(uint8_t function_id, + std::vector& params); + private: + Device* _device; + uint8_t _index; + }; +}}} + +#endif //LOGID_HIDPP20_FEATURE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/feature_defs.h b/src/logid/backend/hidpp20/feature_defs.h index 72de1e1..f72db8f 100644 --- a/src/logid/backend/hidpp20/feature_defs.h +++ b/src/logid/backend/hidpp20/feature_defs.h @@ -6,97 +6,108 @@ namespace logid { namespace backend { namespace hidpp20 { - enum FeatureID : uint16_t - { - ROOT = 0x0000, - FEATURE_SET = 0x0001, - FEATURE_INFO = 0x0002, - FW_VERSION = 0x0003, - DEVICE_NAME = 0x0005, - DEVICE_GROUPS = 0x0006, - DEVICE_FRIENDLY_NAME = 0x0007, - RESET = 0x0020, - CRYPTO_IDENTIFIER = 0x0021, - DFUCONTROL = 0x00c0, - DFUCONTROL_V2 = 0x00c1, - DFUCONTROL_V3 = 0x00c2, - DFU = 0xd000, - BATTERY_STATUS = 0x1000, - BATTERY_VOLTAGE = 0x1001, - CHARGING_CONTROL = 0x1010, - LED_CONTROL = 0x1300, - GENERIC_TEST = 0x1800, - DEVICE_RESET = 0x1802, - OOB_STATE = 0x1805, - CONFIGURABLE_DEVICE_PROPERTIES = 0x1806, - CHANGE_HOST = 0x1814, - HOSTS_INFO = 0x1815, - BACKLIGHT = 0x1981, - BACKLIGHT_V2 = 0x1982, - BACKLIGHT_V3 = 0x1983, - PRESENTER_CONTROL = 0x1a00, - SENSOR_3D = 0x1a01, - REPROG_CONTROLS = 0x1b00, - REPROG_CONTROLS_V2 = 0x1b01, - REPROG_CONTROLS_V2_2 = 0x1b02, - REPROG_CONTROLS_V3 = 0x1b03, - REPROG_CONTROLS_V4 = 0x1b04, - PERSISTENT_REMAPPABLE_ACTION = 0x1bc0, - WIRELESS_DEVICE_STATUS = 0x1d4b, - ENABLE_HIDDEN_FEATURE = 0x1e00, - FIRMWARE_PROPERTIES = 0x1f1f, - ADC_MEASUREMENT = 0x1f20, - LEFT_RIGHT_SWAP = 0x2001, - SWAP_BUTTON = 0x2005, - POINTER_AXES_ORIENTATION = 0x2006, - VERTICAL_SCROLLING = 0x2100, - SMART_SHIFT = 0x2110, - HIRES_SCROLLING = 0x2120, - HIRES_SCROLLING_V2 = 0x2121, // Referred to as Hi-res wheel in cvuchener/hidpp, seems to be V2? - LORES_SCROLLING = 0x2130, - MOUSE_POINTER = 0x2200, // Possibly predecessor to 0x2201? - ADJUSTABLE_DPI = 0x2201, - ANGLE_SNAPPING = 0x2230, - SURFACE_TUNING = 0x2240, - HYBRID_TRACKING = 0x2400, - FN_INVERSION = 0x40a0, - FN_INVERSION_V2 = 0x40a2, // Is 0x40a1 skipped? - FN_INVERSION_V3 = 0x40a3, - ENCRYPTION = 0x4100, - LOCK_KEY_STATE = 0x4220, - SOLAR_DASHBOARD = 0x4301, - KEYBOARD_LAYOUT = 0x4520, - KEYBOARD_DISABLE = 0x4521, - DISABLE_KEYS = 0x4522, - MULTIPLATFORM = 0x4530, // Dual platform only? - MULTIPLATFORM_V2 = 0x4531, - KEYBOARD_LAYOUT_V2 = 0x4540, - CROWN = 0x4600, - TOUCHPAD_FW = 0x6010, - TOUCHPAD_SW = 0x6011, - TOUCHPAD_FW_WIN8 = 0x6012, - TOUCHMOUSE_RAW = 0x6100, - // TOUCHMOUSE_6120 = 0x6120, (Keeping this commented out until a better name is found) - GESTURE = 0x6500, - GESTURE_V2 = 0x6501, - G_KEY = 0x8010, - M_KEY = 0x8020, - // MR = 0x8030, (Keeping this commented out until a better name is found) - BRIGHTNESS_CONTROL = 0x8040, - REPORT_RATE = 0x8060, - RGB_EFFECTS = 0x8070, - RGB_EFFECTS_V2 = 0x8071, - PER_KEY_LIGHTING = 0x8080, - PER_KEY_LIGHTING_V2 = 0x8081, - MODE_STATUS = 0x8100, - MOUSE_BUTTON_SPY = 0x8110, - LATENCY_MONITORING = 0x8111, - GAMING_ATTACHMENTS = 0x8120, - FORCE_FEEDBACK = 0x8123, - SIDETONE = 0x8300, - EQUALIZER = 0x8310, - HEADSET_OUT = 0x8320 + struct feature_info { + uint16_t feature_id; + bool obsolete; + bool internal; + bool hidden; }; + + namespace FeatureID + { + enum FeatureID : uint16_t + { + ROOT = 0x0000, + FEATURE_SET = 0x0001, + FEATURE_INFO = 0x0002, + FW_VERSION = 0x0003, + DEVICE_NAME = 0x0005, + DEVICE_GROUPS = 0x0006, + DEVICE_FRIENDLY_NAME = 0x0007, + RESET = 0x0020, + CRYPTO_IDENTIFIER = 0x0021, + DFUCONTROL = 0x00c0, + DFUCONTROL_V2 = 0x00c1, + DFUCONTROL_V3 = 0x00c2, + DFU = 0xd000, + BATTERY_STATUS = 0x1000, + BATTERY_VOLTAGE = 0x1001, + CHARGING_CONTROL = 0x1010, + LED_CONTROL = 0x1300, + GENERIC_TEST = 0x1800, + DEVICE_RESET = 0x1802, + OOB_STATE = 0x1805, + CONFIGURABLE_DEVICE_PROPERTIES = 0x1806, + CHANGE_HOST = 0x1814, + HOSTS_INFO = 0x1815, + BACKLIGHT = 0x1981, + BACKLIGHT_V2 = 0x1982, + BACKLIGHT_V3 = 0x1983, + PRESENTER_CONTROL = 0x1a00, + SENSOR_3D = 0x1a01, + REPROG_CONTROLS = 0x1b00, + REPROG_CONTROLS_V2 = 0x1b01, + REPROG_CONTROLS_V2_2 = 0x1b02, + REPROG_CONTROLS_V3 = 0x1b03, + REPROG_CONTROLS_V4 = 0x1b04, + PERSISTENT_REMAPPABLE_ACTION = 0x1bc0, + WIRELESS_DEVICE_STATUS = 0x1d4b, + ENABLE_HIDDEN_FEATURE = 0x1e00, + FIRMWARE_PROPERTIES = 0x1f1f, + ADC_MEASUREMENT = 0x1f20, + LEFT_RIGHT_SWAP = 0x2001, + SWAP_BUTTON = 0x2005, + POINTER_AXES_ORIENTATION = 0x2006, + VERTICAL_SCROLLING = 0x2100, + SMART_SHIFT = 0x2110, + HIRES_SCROLLING = 0x2120, + HIRES_SCROLLING_V2 = 0x2121, // Referred to as Hi-res wheel in cvuchener/hidpp, seems to be V2? + LORES_SCROLLING = 0x2130, + MOUSE_POINTER = 0x2200, // Possibly predecessor to 0x2201? + ADJUSTABLE_DPI = 0x2201, + ANGLE_SNAPPING = 0x2230, + SURFACE_TUNING = 0x2240, + HYBRID_TRACKING = 0x2400, + FN_INVERSION = 0x40a0, + FN_INVERSION_V2 = 0x40a2, // Is 0x40a1 skipped? + FN_INVERSION_V3 = 0x40a3, + ENCRYPTION = 0x4100, + LOCK_KEY_STATE = 0x4220, + SOLAR_DASHBOARD = 0x4301, + KEYBOARD_LAYOUT = 0x4520, + KEYBOARD_DISABLE = 0x4521, + DISABLE_KEYS = 0x4522, + MULTIPLATFORM = 0x4530, // Dual platform only? + MULTIPLATFORM_V2 = 0x4531, + KEYBOARD_LAYOUT_V2 = 0x4540, + CROWN = 0x4600, + TOUCHPAD_FW = 0x6010, + TOUCHPAD_SW = 0x6011, + TOUCHPAD_FW_WIN8 = 0x6012, + TOUCHMOUSE_RAW = 0x6100, + // TOUCHMOUSE_6120 = 0x6120, (Keeping this commented out until a better name is found) + GESTURE = 0x6500, + GESTURE_V2 = 0x6501, + G_KEY = 0x8010, + M_KEY = 0x8020, + // MR = 0x8030, (Keeping this commented out until a better name is found) + BRIGHTNESS_CONTROL = 0x8040, + REPORT_RATE = 0x8060, + RGB_EFFECTS = 0x8070, + RGB_EFFECTS_V2 = 0x8071, + PER_KEY_LIGHTING = 0x8080, + PER_KEY_LIGHTING_V2 = 0x8081, + MODE_STATUS = 0x8100, + MOUSE_BUTTON_SPY = 0x8110, + LATENCY_MONITORING = 0x8111, + GAMING_ATTACHMENTS = 0x8120, + FORCE_FEEDBACK = 0x8123, + SIDETONE = 0x8300, + EQUALIZER = 0x8310, + HEADSET_OUT = 0x8320 + }; + } + }}} #endif //LOGID_BACKEND_HIDPP20_FEATUREDEFS \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/Root.cpp b/src/logid/backend/hidpp20/features/Root.cpp new file mode 100644 index 0000000..8e06874 --- /dev/null +++ b/src/logid/backend/hidpp20/features/Root.cpp @@ -0,0 +1,25 @@ +#include "Root.h" + +using namespace logid::backend::hidpp20; + +Root::Root(Device* dev) : Feature(dev, ID) +{ + +} + +feature_info Root::getFeature(uint16_t feature_id) +{ + feature_info info; + std::vector params(2); + params[0] = feature_id & 0xff; + params[1] = (feature_id >> 8) & 0xff; + + auto response = this->callFunction(Function::Ping, params); + + info.feature_id = response[0]; + info.hidden = response[1] & FeatureFlag::Hidden; + info.obsolete = response[1] & FeatureFlag::Obsolete; + info.internal = response[1] & FeatureFlag::Internal; + + return info; +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/Root.h b/src/logid/backend/hidpp20/features/Root.h new file mode 100644 index 0000000..59a41ea --- /dev/null +++ b/src/logid/backend/hidpp20/features/Root.h @@ -0,0 +1,37 @@ +#ifndef LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H +#define LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H + +#include "../Feature.h" +#include "../feature_defs.h" + +namespace logid { +namespace backend { +namespace hidpp20 +{ + class Root : public Feature + { + public: + static const uint16_t ID = FeatureID::ROOT; + virtual uint16_t getID() { return ID; } + + enum Function : uint8_t + { + GetFeature = 0, + Ping = 1 + }; + + Root(Device* device); + + feature_info getFeature (uint16_t feature_id); + void ping(); + private: + enum FeatureFlag : uint8_t + { + Obsolete = 1<<7, + Hidden = 1<<6, + Internal = 1<<5 + }; + }; +}}} + +#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 473ad25..0def59c 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -1,5 +1,7 @@ #include "RawDevice.h" #include "../Error.h" +#include "../hidpp/defs.h" +#include "../dj/defs.h" #include #include @@ -16,9 +18,16 @@ extern "C" } using namespace logid::backend::raw; +using namespace logid::backend; using namespace std::chrono; -RawDevice::RawDevice(std::string path) : path (path) +bool RawDevice::supportedReportID(uint8_t 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) { int ret; @@ -80,24 +89,81 @@ RawDevice::~RawDevice() std::vector RawDevice::sendReport(const std::vector& report) { - std::packaged_task()> task( - [=]() { - _sendReport(report); - std::vector response; - _readReport(response, MAX_DATA_LENGTH); - return response; - }); + assert(supportedReportID(report[0])); - /* If the listener will stop, handle I/O manually. + /* If the listener will stop, handle I/O manually. * Otherwise, push to queue and wait for result. */ if(continue_listen) { + std::packaged_task()> task( [this, report]() { + return this->_respondToReport(report); + }); auto f = task.get_future(); write_queue.push(&task); return f.get(); } else - return task.get_future().get(); + return _respondToReport(report); +} + +std::vector RawDevice::_respondToReport + (const std::vector& request) +{ + _sendReport(request); + while(true) + { + std::vector response; + _readReport(response, MAX_DATA_LENGTH); + + // All reports have the device index at byte 2 + if(response[1] != request[1]) + { + std::thread([this](std::vector report) { + this->handleEvent(report); + }, request).detach(); + continue; + } + + if(hidpp::ReportType::Short == request[0] || + hidpp::ReportType::Long == request[0]) + { + if(hidpp::ReportType::Short != response[0] && + hidpp::ReportType::Long != response[0]) + { + std::thread([this](std::vector report) { + this->handleEvent(report); + }, request).detach(); + continue; + } + + // Error; leave to device to handle + if(response[2] == 0x8f || response[2] == 0xff) + return response; + + bool others_match = true; + for(int i = 2; i < 4; i++) + { + if(response[i] != request[i]) + others_match = false; + } + + if(others_match) + return response; + } + else if(dj::ReportType::Short == request[0] || + dj::ReportType::Long == request[0]) + { + //Error; leave to device ot handle + if(0x7f == response[2]) + return response; + else if(response[2] == request[2]) + return response; + } + + std::thread([this](std::vector report) { + this->handleEvent(report); + }, request).detach(); + } } int RawDevice::_sendReport(const std::vector& report) diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index e7baba6..8e78003 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "../defs.h" #include "../../util/mutex_queue.h" @@ -20,12 +21,13 @@ namespace raw class RawDevice { public: + static bool supportedReportID(uint8_t id); + RawDevice(std::string path); ~RawDevice(); std::string hidrawPath() const { return path; } std::vector reportDescriptor() const { return rdesc; } - /// TODO: Process reports in a queue. std::vector sendReport(const std::vector& report); void interruptRead(); @@ -55,6 +57,8 @@ namespace raw int _sendReport(const std::vector& report); int _readReport(std::vector& report, std::size_t maxDataLength); + std::vector _respondToReport(const std::vector& request); + mutex_queue()>*> write_queue; }; }}} From cc025d3b967d0e1b32fe71e0af1cc68df448f80e Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 18 Jun 2020 02:22:05 -0400 Subject: [PATCH 10/81] Fully implement Root and virutal Feature class --- src/logid/backend/hidpp20/Feature.cpp | 24 +++++++++++++++------ src/logid/backend/hidpp20/Feature.h | 5 ++--- src/logid/backend/hidpp20/features/Root.cpp | 17 ++++++++++++--- src/logid/backend/hidpp20/features/Root.h | 2 +- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/logid/backend/hidpp20/Feature.cpp b/src/logid/backend/hidpp20/Feature.cpp index 58abb57..90ec9ae 100644 --- a/src/logid/backend/hidpp20/Feature.cpp +++ b/src/logid/backend/hidpp20/Feature.cpp @@ -1,13 +1,15 @@ #include "Feature.h" +#include "feature_defs.h" +#include "features/Root.h" using namespace logid::backend::hidpp20; -const char* Feature::UnsupportedFeature::what() const noexcept +const char* UnsupportedFeature::what() const noexcept { return "Unsupported feature"; } -uint16_t Feature::UnsupportedFeature::code() const noexcept +uint16_t UnsupportedFeature::code() const noexcept { return _f_id; } @@ -17,12 +19,20 @@ std::vector Feature::callFunction(uint8_t function_id, std::vectorcallFunction(_index, function_id, params); } -Feature::Feature(Device* dev, uint16_t _id) : _device (dev), _index (0xff) +Feature::Feature(Device* dev, uint16_t _id) : _device (dev) { - ///TODO: Set index -} + _index = hidpp20::FeatureID::ROOT; -Feature::Feature(Device* dev, uint8_t _index) : _device (dev), _index (_index) -{ + if(_id) + { + std::vector getFunc_req(2); + getFunc_req[0] = _id & 0xff; + getFunc_req[1] = (_id >> 8) & 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/Feature.h b/src/logid/backend/hidpp20/Feature.h index dcea923..8a96602 100644 --- a/src/logid/backend/hidpp20/Feature.h +++ b/src/logid/backend/hidpp20/Feature.h @@ -7,8 +7,6 @@ namespace logid { namespace backend { namespace hidpp20 { - class Feature - { class UnsupportedFeature : public std::exception { public: @@ -19,13 +17,14 @@ namespace hidpp20 { uint16_t _f_id; }; + class Feature + { public: static const uint16_t ID; virtual uint16_t getID() = 0; protected: explicit Feature(Device* dev, uint16_t _id); - explicit Feature(Device* dev, uint8_t _index); std::vector callFunction(uint8_t function_id, std::vector& params); private: diff --git a/src/logid/backend/hidpp20/features/Root.cpp b/src/logid/backend/hidpp20/features/Root.cpp index 8e06874..7f44da8 100644 --- a/src/logid/backend/hidpp20/features/Root.cpp +++ b/src/logid/backend/hidpp20/features/Root.cpp @@ -4,22 +4,33 @@ using namespace logid::backend::hidpp20; Root::Root(Device* dev) : Feature(dev, ID) { - } feature_info Root::getFeature(uint16_t feature_id) { - feature_info info; + feature_info info{}; std::vector params(2); params[0] = feature_id & 0xff; params[1] = (feature_id >> 8) & 0xff; - auto response = this->callFunction(Function::Ping, params); + auto response = this->callFunction(Function::GetFeature, params); 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; return info; +} + +std::tuple Root::getVersion() +{ + std::vector params(0); + auto response = this->callFunction(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 59a41ea..b6f0fb2 100644 --- a/src/logid/backend/hidpp20/features/Root.h +++ b/src/logid/backend/hidpp20/features/Root.h @@ -23,7 +23,7 @@ namespace hidpp20 Root(Device* device); feature_info getFeature (uint16_t feature_id); - void ping(); + std::tuple getVersion(); private: enum FeatureFlag : uint8_t { From 7571be1f54294e3dc59cf0a87a1a2c3124742309 Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 18 Jun 2020 02:28:43 -0400 Subject: [PATCH 11/81] Don't create a new thread for each raw event Threads should be created as necessary, there is no reason to launch a simple event handler in a new thread. --- src/logid/backend/raw/RawDevice.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 0def59c..eeabeb5 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -91,7 +91,7 @@ std::vector RawDevice::sendReport(const std::vector& report) { assert(supportedReportID(report[0])); - /* If the listener will stop, handle I/O manually. + /* If the listener will stop, handle I/O manually. * Otherwise, push to queue and wait for result. */ if(continue_listen) { @@ -118,9 +118,8 @@ std::vector RawDevice::_respondToReport // All reports have the device index at byte 2 if(response[1] != request[1]) { - std::thread([this](std::vector report) { - this->handleEvent(report); - }, request).detach(); + if(continue_listen) + this->handleEvent(response); continue; } @@ -130,9 +129,8 @@ std::vector RawDevice::_respondToReport if(hidpp::ReportType::Short != response[0] && hidpp::ReportType::Long != response[0]) { - std::thread([this](std::vector report) { - this->handleEvent(report); - }, request).detach(); + if(continue_listen) + this->handleEvent(response); continue; } @@ -160,9 +158,8 @@ std::vector RawDevice::_respondToReport return response; } - std::thread([this](std::vector report) { - this->handleEvent(report); - }, request).detach(); + if(continue_listen) + this->handleEvent(response); } } @@ -246,9 +243,8 @@ void RawDevice::listen() } std::vector report; _readReport(report, MAX_DATA_LENGTH); - std::thread([this](std::vector report) { - this->handleEvent(report); - }, report).detach(); + + this->handleEvent(report); } continue_listen = false; From ecc5062e0ffd858c48b760551cd0d32b239885ba Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 18 Jun 2020 04:47:04 -0400 Subject: [PATCH 12/81] Support getting version of HID++ 1.0 devices --- src/logid/CMakeLists.txt | 1 + src/logid/DeviceMonitor.cpp | 9 +++++- src/logid/backend/hidpp/Device.cpp | 38 ++++++++++++++++++---- src/logid/backend/hidpp/Report.cpp | 26 ++++++++++++++- src/logid/backend/hidpp/Report.h | 8 +++++ src/logid/backend/hidpp/defs.h | 2 +- src/logid/backend/hidpp10/Error.cpp | 50 +++++++++++++++++++++++++++++ src/logid/backend/hidpp10/Error.h | 41 +++++++++++++++++++++++ 8 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 src/logid/backend/hidpp10/Error.cpp create mode 100644 src/logid/backend/hidpp10/Error.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index e4e10d9..89d2422 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -17,6 +17,7 @@ add_executable(logid backend/raw/RawDevice.cpp backend/hidpp/Device.cpp backend/hidpp/Report.cpp + backend/hidpp10/Error.cpp backend/hidpp20/Device.cpp backend/hidpp20/Error.cpp backend/hidpp20/Feature.cpp diff --git a/src/logid/DeviceMonitor.cpp b/src/logid/DeviceMonitor.cpp index 7c4c235..90b4cdd 100644 --- a/src/logid/DeviceMonitor.cpp +++ b/src/logid/DeviceMonitor.cpp @@ -3,6 +3,7 @@ #include "DeviceMonitor.h" #include "util.h" +#include "backend/hidpp10/Error.h" #define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded" @@ -128,7 +129,13 @@ void DeviceMonitor::addDevice(std::string path) std::thread([device]() { device->listen(); }).detach(); } - catch(backend::hidpp::Device::InvalidDevice &e) + catch(hidpp10::Error &e) + { + if(e.code() == hidpp10::Error::UnknownDevice) {} + else + throw; + } + catch(hidpp::Device::InvalidDevice &e) { log_printf(DEBUG, "Detected device at %s but %s", path.c_str(), e.what()); } diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index d135045..6134475 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -2,6 +2,8 @@ #include "Device.h" #include "Report.h" #include "../hidpp20/features/Root.h" +#include "../hidpp20/Error.h" +#include "../hidpp10/Error.h" using namespace logid::backend; using namespace logid::backend::hidpp; @@ -30,13 +32,24 @@ Device::Device(const std::string& path, DeviceIndex index): if(!supported_reports) throw InvalidDevice(InvalidDevice::NoHIDPPReport); - Report versionRequest(Report::Type::Short, index, hidpp20::FeatureID::ROOT, - hidpp20::Root::Ping, LOGID_HIDPP_SOFTWARE_ID); + try + { + Report versionRequest(Report::Type::Short, index, hidpp20::FeatureID::ROOT, + hidpp20::Root::Ping, LOGID_HIDPP_SOFTWARE_ID); - ///TODO: Catch error - auto versionResponse = sendReport(versionRequest); - auto versionResponse_params = versionResponse.paramBegin(); - _version = std::make_tuple(versionResponse_params[0], versionResponse_params[1]); + auto versionResponse = sendReport(versionRequest); + auto versionResponse_params = versionResponse.paramBegin(); + _version = std::make_tuple(versionResponse_params[0], versionResponse_params[1]); + } + catch(hidpp10::Error &e) + { + // Valid HID++ 1.0 devices should send an InvalidSubID error + if(e.code() != hidpp10::Error::InvalidSubID) + throw; + + // HID++ 2.0 is not supported, assume HID++ 1.0 + _version = std::make_tuple(1, 0); + } // Pass all HID++ events with device index to this device. RawEventHandler rawEventHandler; @@ -88,7 +101,18 @@ Report Device::sendReport(Report& report) } auto raw_response = raw_device->sendReport(report.rawReport()); - return Report(raw_response); + + Report response(raw_response); + + Report::hidpp10_error hidpp10Error{}; + if(response.isError10(&hidpp10Error)) + throw hidpp10::Error(hidpp10Error.error_code); + + Report::hidpp20_error hidpp20Error{}; + if(response.isError20(&hidpp20Error)) + throw hidpp10::Error(hidpp20Error.error_code); + + return response; } void Device::listen() diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index 22274c1..99e32ea 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -2,6 +2,8 @@ #include #include #include "Report.h" +#include "../hidpp10/Error.h" +#include "../hidpp20/Error.h" using namespace logid::backend::hidpp; using namespace logid::backend; @@ -159,9 +161,31 @@ void Report::setParams(const std::vector& _params) _data[Offset::Parameters + i] = _params[i]; } +bool Report::isError10(Report::hidpp10_error *error) +{ + assert(error != nullptr); + + if(_data[Offset::Type] != Type::Short || + _data[Offset::SubID] != hidpp10::ErrorID) + return false; + + error->sub_id = _data[3]; + error->address = _data[4]; + error->error_code = _data[5]; + + return true; +} + bool Report::isError20(Report::hidpp20_error* error) { if(_data[Offset::Type] != Type::Long || - _data[Offset::Feature] != 0xff) + _data[Offset::Feature] != hidpp20::ErrorID) return false; + + error->feature_index= _data[3]; + error->function = (_data[4] >> 4) & 0x0f; + error->software_id = _data[4] & 0x0f; + error->error_code = _data[5]; + + return true; } \ No newline at end of file diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index 1ad7f92..929f783 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -18,6 +18,7 @@ namespace logid::backend::hidpp { static constexpr uint8_t Type = 0; static constexpr uint8_t DeviceIndex = 1; + static constexpr uint8_t SubID = 2; static constexpr uint8_t Feature = 2; static constexpr uint8_t Function = 3; static constexpr uint8_t Parameters = 4; @@ -59,6 +60,13 @@ namespace logid::backend::hidpp std::vector::iterator paramEnd() { return _data.end(); } void setParams(const std::vector& _params); + struct hidpp10_error + { + uint8_t sub_id, address, error_code; + }; + + bool isError10(hidpp10_error* error); + struct hidpp20_error { uint8_t feature_index, function, software_id, error_code; diff --git a/src/logid/backend/hidpp/defs.h b/src/logid/backend/hidpp/defs.h index d03b347..c026ba3 100644 --- a/src/logid/backend/hidpp/defs.h +++ b/src/logid/backend/hidpp/defs.h @@ -1,7 +1,7 @@ #ifndef LOGID_HIDPP_DEFS_H #define LOGID_HIDPP_DEFS_H -#define LOGID_HIDPP_SOFTWARE_ID 1 +#define LOGID_HIDPP_SOFTWARE_ID 0 namespace logid::backend::hidpp { diff --git a/src/logid/backend/hidpp10/Error.cpp b/src/logid/backend/hidpp10/Error.cpp new file mode 100644 index 0000000..30f6b3f --- /dev/null +++ b/src/logid/backend/hidpp10/Error.cpp @@ -0,0 +1,50 @@ +#include +#include +#include "Error.h" + +using namespace logid::backend::hidpp10; + +Error::Error(uint8_t code): _code(code) +{ + assert(code != Success); +} + +const char* Error::what() const noexcept +{ + switch(_code) + { + case Success: + return "Success"; + case InvalidSubID: + return "Invalid sub ID"; + case InvalidAddress: + return "Invalid address"; + case InvalidValue: + return "Invalid value"; + case ConnectFail: + return "Connection failure"; + case TooManyDevices: + return "Too many devices"; + case AlreadyExists: + return "Already exists"; + case Busy: + return "Busy"; + case UnknownDevice: + return "Unknown device"; + case ResourceError: + return "Resource error"; + case RequestUnavailable: + return "Request unavailable"; + case InvalidParameterValue: + return "Invalid parameter value"; + case WrongPINCode: + return "Wrong PIN code"; + default: + return std::string("Unknown error code " + std::to_string(_code)).c_str(); + } +} + +uint8_t Error::code() const noexcept +{ + return _code; +} \ No newline at end of file diff --git a/src/logid/backend/hidpp10/Error.h b/src/logid/backend/hidpp10/Error.h new file mode 100644 index 0000000..71488b0 --- /dev/null +++ b/src/logid/backend/hidpp10/Error.h @@ -0,0 +1,41 @@ +#ifndef LOGID_BACKEND_HIDPP10_ERROR_H +#define LOGID_BACKEND_HIDPP10_ERROR_H + +#include + +namespace logid { +namespace backend { +namespace hidpp10 { + static constexpr uint8_t ErrorID = 0x8f; + + class Error: public std::exception + { + public: + enum ErrorCode: uint8_t + { + Success = 0x00, + InvalidSubID = 0x01, + InvalidAddress = 0x02, + InvalidValue = 0x03, + ConnectFail = 0x04, + TooManyDevices = 0x05, + AlreadyExists = 0x06, + Busy = 0x07, + UnknownDevice = 0x08, + ResourceError = 0x09, + RequestUnavailable = 0x0A, + InvalidParameterValue = 0x0B, + WrongPINCode = 0x0C + }; + + Error(uint8_t code); + + virtual const char* what() const noexcept; + uint8_t code() const noexcept; + + private: + uint8_t _code; + }; +}}} + +#endif //LOGID_BACKEND_HIDPP10_ERROR_H \ No newline at end of file From 47db60fad8d390336fd4d83c23165945ed71b269 Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 18 Jun 2020 14:45:16 -0400 Subject: [PATCH 13/81] Fix swapped DefaultDevice and CordedDevice indexes --- src/logid/backend/hidpp/defs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/logid/backend/hidpp/defs.h b/src/logid/backend/hidpp/defs.h index c026ba3..295af39 100644 --- a/src/logid/backend/hidpp/defs.h +++ b/src/logid/backend/hidpp/defs.h @@ -16,14 +16,14 @@ namespace logid::backend::hidpp enum DeviceIndex: uint8_t { - DefaultDevice = 0, + DefaultDevice = 0xff, + CordedDevice = 0, WirelessDevice1 = 1, WirelessDevice2 = 2, WirelessDevice3 = 3, WirelessDevice4 = 4, WirelessDevice5 = 5, WirelessDevice6 = 6, - CordedDevice = 0xff }; static constexpr std::size_t ShortParamLength = 3; From b41649b0de77445e9f6b51baa5e3d772a682c581 Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 19 Jun 2020 03:58:00 -0400 Subject: [PATCH 14/81] Implement dj::Receiver class Again, many things were done here. --- src/logid/CMakeLists.txt | 3 + src/logid/DeviceMonitor.cpp | 30 +++- src/logid/backend/dj/Error.cpp | 21 +++ src/logid/backend/dj/Error.h | 30 ++++ src/logid/backend/dj/Receiver.cpp | 255 +++++++++++++++++++++++++++ src/logid/backend/dj/Receiver.h | 125 +++++++++++++ src/logid/backend/dj/Report.cpp | 67 ++++++- src/logid/backend/dj/Report.h | 30 +++- src/logid/backend/dj/defs.h | 6 + src/logid/backend/hidpp/Device.cpp | 34 +++- src/logid/backend/hidpp/Device.h | 8 +- src/logid/backend/hidpp/Report.cpp | 79 +++++++++ src/logid/backend/hidpp/Report.h | 26 ++- src/logid/backend/hidpp10/Device.cpp | 62 +++++++ src/logid/backend/hidpp10/Device.h | 28 +++ src/logid/backend/hidpp10/defs.h | 17 ++ src/logid/backend/raw/RawDevice.cpp | 23 +++ src/logid/backend/raw/RawDevice.h | 1 + 18 files changed, 819 insertions(+), 26 deletions(-) create mode 100644 src/logid/backend/dj/Error.cpp create mode 100644 src/logid/backend/dj/Error.h create mode 100644 src/logid/backend/dj/Receiver.cpp create mode 100644 src/logid/backend/dj/Receiver.h create mode 100644 src/logid/backend/hidpp10/Device.cpp create mode 100644 src/logid/backend/hidpp10/Device.h create mode 100644 src/logid/backend/hidpp10/defs.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 89d2422..4ce4d1f 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -15,9 +15,12 @@ add_executable(logid backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp + backend/dj/Receiver.cpp + backend/dj/Error.cpp backend/hidpp/Device.cpp backend/hidpp/Report.cpp backend/hidpp10/Error.cpp + backend/hidpp10/Device.cpp backend/hidpp20/Device.cpp backend/hidpp20/Error.cpp backend/hidpp20/Feature.cpp diff --git a/src/logid/DeviceMonitor.cpp b/src/logid/DeviceMonitor.cpp index 90b4cdd..a24aec4 100644 --- a/src/logid/DeviceMonitor.cpp +++ b/src/logid/DeviceMonitor.cpp @@ -4,6 +4,7 @@ #include "DeviceMonitor.h" #include "util.h" #include "backend/hidpp10/Error.h" +#include "backend/dj/Receiver.h" #define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded" @@ -103,12 +104,26 @@ void DeviceMonitor::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceI void DeviceMonitor::addDevice(std::string path) { try { - auto device = std::make_shared(path, hidpp::DeviceIndex::WirelessDevice1); - log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); + std::tuple version; + try + { + hidpp::Device device(path, hidpp::DefaultDevice); - auto version = device->version(); - log_printf(DEBUG, "HID++ version: %d.%d", std::get<0>(version), std::get<1>(version)); + log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); + version = device.version(); + log_printf(DEBUG, "HID++ version: %d.%d", std::get<0>(version), std::get<1>(version)); + } catch(std::system_error &e) { } + + if(version == std::make_tuple(1, 0)) + { + // This is a receiver + dj::Receiver receiver(path); + receiver.enumerate(); + receiver.listen(); + while(true) {} + } + /* auto eventHandler = std::make_shared(); eventHandler->condition = [device](backend::hidpp::Report& report)->bool { @@ -125,9 +140,9 @@ void DeviceMonitor::addDevice(std::string path) device->addEventHandler("MONITOR_ALL", eventHandler); - devices.push_back(device); + devices.push_back(device);*/ - std::thread([device]() { device->listen(); }).detach(); + //std::thread([device]() { device->listen(); }).detach(); } catch(hidpp10::Error &e) { @@ -139,10 +154,11 @@ void DeviceMonitor::addDevice(std::string path) { log_printf(DEBUG, "Detected device at %s but %s", path.c_str(), e.what()); } + /* catch(std::system_error &e) { log_printf(WARN, "Failed to open %s: %s", path.c_str(), e.what()); - } + } */ } void DeviceMonitor::removeDevice(std::string path) diff --git a/src/logid/backend/dj/Error.cpp b/src/logid/backend/dj/Error.cpp new file mode 100644 index 0000000..d3a4dbc --- /dev/null +++ b/src/logid/backend/dj/Error.cpp @@ -0,0 +1,21 @@ +#include "Error.h" + +using namespace logid::backend::dj; + +Error::Error(uint8_t code) : _code (code) +{ +} + +const char* Error::what() const noexcept +{ + switch(_code) + { + case Unknown: + return "Unknown"; + case KeepAliveTimeout: + return "Keep-alive timeout"; + default: + return std::string("Reserved error code " + std::to_string(_code)) + .c_str(); + } +} \ No newline at end of file diff --git a/src/logid/backend/dj/Error.h b/src/logid/backend/dj/Error.h new file mode 100644 index 0000000..f462377 --- /dev/null +++ b/src/logid/backend/dj/Error.h @@ -0,0 +1,30 @@ +#ifndef LOGID_HIDPP_BACKEND_DJ_ERROR_H +#define LOGID_HIDPP_BACKEND_DJ_ERROR_H + +#include +#include + +namespace logid { +namespace backend { +namespace dj +{ + class Error : public std::exception + { + public: + enum ErrorCode : uint8_t + { + Unknown = 0x00, + KeepAliveTimeout = 0x01 + }; + + Error(uint8_t code); + + virtual const char* what() const noexcept; + uint8_t code() const noexcept; + + private: + uint8_t _code; + }; +}}} + +#endif //LOGID_HIDPP_BACKEND_DJ_ERROR_H diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp new file mode 100644 index 0000000..7d5aef4 --- /dev/null +++ b/src/logid/backend/dj/Receiver.cpp @@ -0,0 +1,255 @@ +#include +#include "Report.h" +#include "Receiver.h" +#include "Error.h" + +using namespace logid::backend::dj; +using namespace logid::backend; + +InvalidReceiver::InvalidReceiver(Reason reason) : _reason (reason) +{ +} + +const char* InvalidReceiver::what() const noexcept +{ + return "Invalid receiver"; +} + +InvalidReceiver::Reason InvalidReceiver::code() const noexcept +{ + return _reason; +} + +Receiver::Receiver(std::string path) : + raw_device (std::make_shared(path)), + _hidpp10_device (raw_device, hidpp::DefaultDevice) +{ + 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() +{ + sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{}); +} + +Receiver::notification_flags Receiver::getHidppNotifications() +{ + auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {}); + + notification_flags flags{}; + + flags.device_battery_status = response[0] & (1 << 4); + flags.receiver_wireless_notifications = response[1] & (1 << 0); + flags.receiver_software_present = response[1] & (1 << 3); +} + +void Receiver::enableHidppNotifications(notification_flags flags) +{ + std::vector request(3); + + if(flags.device_battery_status) + request[0] |= (1 << 4); + if(flags.receiver_wireless_notifications) + request[1] |= (1 << 0); + if(flags.receiver_software_present) + request[1] |= (1 << 3); + + _hidpp10_device.setRegister(EnableHidppNotifications, request); +} + +///TODO: Investigate usage +uint8_t Receiver::getConnectionState(hidpp::DeviceIndex index) +{ + auto response = _hidpp10_device.setRegister(ConnectionState, {index}); + + return response[0]; +} + +void Receiver::startPairing(uint8_t timeout) +{ + ///TODO: Device number == Device index? + std::vector request(3); + + request[0] = 1; + request[1] = hidpp::DefaultDevice; + request[2] = timeout; + + _hidpp10_device.setRegister(DevicePairing, request); +} + +void Receiver::stopPairing() +{ + ///TODO: Device number == Device index? + std::vector request(3); + + request[0] = 2; + request[1] = hidpp::DefaultDevice; + + _hidpp10_device.setRegister(DevicePairing, request); +} + +void Receiver::disconnect(hidpp::DeviceIndex index) +{ + ///TODO: Device number == Device index? + std::vector request(3); + + request[0] = 3; + request[1] = index; + + _hidpp10_device.setRegister(DevicePairing, request); +} + +std::map Receiver::getDeviceActivity() +{ + auto response = _hidpp10_device.getRegister(DeviceActivity, {}); + + std::map device_activity; + for(uint8_t i = hidpp::WirelessDevice1; i <= hidpp::WirelessDevice6; i++) + device_activity[static_cast(i)] = response[i]; + + return device_activity; +} + +Receiver::pairing_info Receiver::getPairingInfo(hidpp::DeviceIndex index) +{ + std::vector request(1); + request[0] = index; + request[0] += 0x19; + + auto response = _hidpp10_device.getRegister(PairingInfo, request); + + pairing_info info{}; + info.destination_id = response[0]; + info.report_interval = response[1]; + info.pid = response[2]; + info.pid |= (response[3] << 8); + info.device_type = response[6]; + + return info; +} + +Receiver::extended_pairing_info + Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index) +{ + std::vector request(1); + request[0] = index; + request[0] += 0x29; + + auto response = _hidpp10_device.getRegister(PairingInfo, request); + + extended_pairing_info info{}; + + info.serial_number = 0; + for(uint8_t i = 0; i < 4; i++) + info.serial_number |= (response[i] << 8*i); + + for(uint8_t i = 0; i < 4; i++) + info.report_types[i] = response[i + 4]; + + info.power_switch_location = response[8] & 0xf; + + return info; +} + +std::string Receiver::getDeviceName(hidpp::DeviceIndex index) +{ + std::vector request(1); + request[0] = index; + request[0] += 0x39; + + auto response = _hidpp10_device.getRegister(PairingInfo, request); + + uint8_t size = response[0]; + assert(size <= 14); + + std::string name(size, ' '); + for(std::size_t i = 0; i < size; i++) + name[i] = response[i + 1]; + + return name; +} + +hidpp::DeviceIndex Receiver::deviceConnectionEvent(hidpp::Report &report) +{ + assert(report.subId() == DeviceConnection); + return report.deviceIndex(); +} + +hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(hidpp::Report& report) +{ + assert(report.subId() == DeviceDisconnection); + return report.deviceIndex(); +} + +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"); + } +} + +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"); +} + +void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function, + const std::vector&& params) +{ + assert(params.size() <= LongParamLength); + + Report::Type type = params.size() <= ShortParamLength ? + ReportType::Short : ReportType::Long; + + Report request(type, index, function); + + std::copy(params.begin(), params.end(), request.paramBegin()); + + raw_device->sendReportNoResponse(request.rawData()); +} + +void Receiver::listen() +{ + std::thread{[=]() { raw_device->listen(); }}.detach(); +} \ No newline at end of file diff --git a/src/logid/backend/dj/Receiver.h b/src/logid/backend/dj/Receiver.h new file mode 100644 index 0000000..24ffa19 --- /dev/null +++ b/src/logid/backend/dj/Receiver.h @@ -0,0 +1,125 @@ +#ifndef LOGID_BACKEND_DJ_RECEIVER_H +#define LOGID_BACKEND_DJ_RECEIVER_H + +#include +#include "../raw/RawDevice.h" +#include "Report.h" +#include "../hidpp/Report.h" +#include "../hidpp10/Device.h" + +namespace logid { +namespace backend { +namespace dj +{ + class InvalidReceiver : public std::exception + { + public: + enum Reason + { + NoDJReports + }; + explicit InvalidReceiver(Reason reason); + virtual const char* what() const noexcept; + Reason code() const noexcept; + private: + Reason _reason; + }; + + class Receiver + { + public: + explicit Receiver(std::string path); + + enum DjEvents : uint8_t + { + DeviceDisconnection = 0x40, + DeviceConnection = 0x41, + ConnectionStatus = 0x42 + }; + + enum DjCommands : uint8_t + { + SwitchAndKeepAlive = 0x80, + GetPairedDevices = 0x81 + }; + + void enumerate(); + + /* The following functions deal with HID++ 1.0 features. + * While these are not technically DJ functions, it is redundant + * to have a separate hidpp10::Receiver class for these functions. + */ + + enum HidppEvents : uint8_t + { + // These events are identical to their DJ counterparts + // DeviceDisconnection = 0x40, + // DeviceConnection = 0x41, + LockingChange = 0x4a + }; + + enum HidppRegisters : uint8_t + { + EnableHidppNotifications = 0x00, + ConnectionState = 0x02, + DevicePairing = 0xb2, + DeviceActivity = 0xb3, + PairingInfo = 0xb5 + }; + + struct notification_flags + { + bool device_battery_status; + bool receiver_wireless_notifications; + bool receiver_software_present; + }; + + notification_flags getHidppNotifications(); + void enableHidppNotifications(notification_flags flags); + + ///TODO: Understand output of this function + uint8_t getConnectionState(hidpp::DeviceIndex index); + + void startPairing(uint8_t timeout = 0); + void stopPairing(); + void disconnect(hidpp::DeviceIndex index); + + std::map getDeviceActivity(); + + struct pairing_info + { + uint8_t destination_id; + uint8_t report_interval; + uint16_t pid; + uint8_t device_type; ///TODO: Create enum for DeviceType + }; + + struct extended_pairing_info + { + uint32_t serial_number; + uint8_t report_types[4]; + uint8_t power_switch_location; ///TODO: Create enum + }; + + pairing_info getPairingInfo(hidpp::DeviceIndex index); + extended_pairing_info getExtendedPairingInfo(hidpp::DeviceIndex index); + + std::string getDeviceName(hidpp::DeviceIndex index); + + hidpp::DeviceIndex deviceConnectionEvent(hidpp::Report& report); + hidpp::DeviceIndex deviceDisconnectionEvent(hidpp::Report& report); + + void handleDjEvent(dj::Report& report); + void handleHidppEvent(hidpp::Report& report); + + void listen(); + private: + void sendDjRequest(hidpp::DeviceIndex index, uint8_t function, + const std::vector&& params); + + std::shared_ptr raw_device; + hidpp10::Device _hidpp10_device; + }; +}}} + +#endif //LOGID_BACKEND_DJ_RECEIVER_H \ No newline at end of file diff --git a/src/logid/backend/dj/Report.cpp b/src/logid/backend/dj/Report.cpp index b8cccb3..0dff2fd 100644 --- a/src/logid/backend/dj/Report.cpp +++ b/src/logid/backend/dj/Report.cpp @@ -1,11 +1,12 @@ #include #include +#include #include "Report.h" using namespace logid::backend::dj; using namespace logid::backend; -static const std::array DJReportDesc = { +static const std::array DJReportDesc = { 0xA1, 0x01, // Collection (Application) 0x85, 0x20, // Report ID (32) 0x95, 0x0E, // Report Count (14) @@ -25,8 +26,68 @@ static const std::array DJReportDesc = { 0xC0 // End Collection }; -bool dj::supportsDjReports(std::vector& rdesc) +bool dj::supportsDjReports(std::vector&& rdesc) { auto it = std::search(rdesc.begin(), rdesc.end(), DJReportDesc.begin(), DJReportDesc.end()); return it != rdesc.end(); -} \ No newline at end of file +} + +Report::Report(std::vector& data) : _data (data) +{ + switch(data[Offset::Type]) + { + case ReportType::Short: + _data.resize(HeaderLength+ShortParamLength); + break; + case ReportType::Long: + _data.resize(HeaderLength+LongParamLength); + break; + default: + assert(false); + } +} + +Report::Report(Report::Type type, hidpp::DeviceIndex index, uint8_t feature) +{ + switch(type) + { + case ReportType::Short: + _data.resize(HeaderLength+ShortParamLength); + break; + case ReportType::Long: + _data.resize(HeaderLength+LongParamLength); + break; + default: + assert(false); + } + + _data[Offset::Type] = type; + _data[Offset::DeviceIndex] = index; + _data[Offset::Feature] = feature; +} + + +Report::Type Report::type() const +{ + return static_cast(_data[Offset::Type]); +} + +hidpp::DeviceIndex Report::index() const +{ + return static_cast(_data[Offset::DeviceIndex]); +} + +uint8_t Report::feature() const +{ + return _data[Offset::Feature]; +} + +std::vector::iterator Report::paramBegin() +{ + return _data.begin() + Offset::Parameters; +} + +std::vector Report::rawData() const +{ + return _data; +} diff --git a/src/logid/backend/dj/Report.h b/src/logid/backend/dj/Report.h index c4078f4..6f01bb7 100644 --- a/src/logid/backend/dj/Report.h +++ b/src/logid/backend/dj/Report.h @@ -3,17 +3,35 @@ #include #include "../raw/RawDevice.h" +#include "defs.h" +#include "../hidpp/defs.h" namespace logid::backend::dj { - bool supportsDjReports(std::vector& rawDevice); + namespace Offset + { + static constexpr uint8_t Type = 0; + static constexpr uint8_t DeviceIndex = 1; + static constexpr uint8_t Feature = 2; + static constexpr uint8_t Parameters = 3; + } + + bool supportsDjReports(std::vector&& rdesc); class Report { - enum Type: uint8_t - { - Short = 0x20, // Short DJ reports use 12 byte parameters - Long = 0x21 // Long DJ reports use 29 byte parameters - }; + public: + typedef ReportType::ReportType Type; + + explicit Report(std::vector& data); + Report(Type type, hidpp::DeviceIndex index, uint8_t feature); + + Type type() const; + hidpp::DeviceIndex index() const; + uint8_t feature() const; + std::vector::iterator paramBegin(); + std::vector rawData() const; + private: + std::vector _data; }; } diff --git a/src/logid/backend/dj/defs.h b/src/logid/backend/dj/defs.h index 3f373c2..5c0124e 100644 --- a/src/logid/backend/dj/defs.h +++ b/src/logid/backend/dj/defs.h @@ -15,6 +15,12 @@ namespace dj Long = 0x21 }; } + + static constexpr uint8_t ErrorFeature = 0x7f; + + static constexpr std::size_t HeaderLength = 3; + static constexpr std::size_t ShortParamLength = 12; + static constexpr std::size_t LongParamLength = 29; }}} #endif //LOGID_BACKEND_DJ_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 6134475..4994aef 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -26,7 +26,19 @@ 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)), path (path), + _index (index) +{ + _init(); +} + +Device::Device(std::shared_ptr raw_device, DeviceIndex index) : + raw_device (raw_device), _index (index) +{ + _init(); +} + +void Device::_init() { supported_reports = getSupportedReports(raw_device->reportDescriptor()); if(!supported_reports) @@ -34,8 +46,9 @@ Device::Device(const std::string& path, DeviceIndex index): try { - Report versionRequest(Report::Type::Short, index, hidpp20::FeatureID::ROOT, - hidpp20::Root::Ping, LOGID_HIDPP_SOFTWARE_ID); + 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(); @@ -53,10 +66,11 @@ Device::Device(const std::string& path, DeviceIndex index): // Pass all HID++ events with device index to this device. RawEventHandler rawEventHandler; - rawEventHandler.condition = [index](std::vector& report)->bool + rawEventHandler.condition = [this](std::vector& report)->bool { return (report[Offset::Type] == Report::Type::Short || - report[Offset::Type] == Report::Type::Long) && (report[Offset::DeviceIndex] == index); + report[Offset::Type] == Report::Type::Long) && + (report[Offset::DeviceIndex] == this->_index); }; rawEventHandler.callback = [this](std::vector& report)->void { @@ -64,7 +78,15 @@ Device::Device(const std::string& path, DeviceIndex index): this->handleEvent(_report); }; - raw_device->addEventHandler("DEV_" + std::to_string(index), rawEventHandler); + raw_device->addEventHandler("DEV_" + std::to_string(_index), rawEventHandler); +} + +Device::~Device() +{ + raw_device->removeEventHandler("DEV_" + std::to_string(_index)); + ///TODO: tmp + raw_device->stopListener(); + raw_device.reset(); } void Device::addEventHandler(const std::string& nickname, const std::shared_ptr& handler) diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 0e91b76..fc5215d 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -37,9 +37,11 @@ namespace hidpp }; Device(const std::string& path, DeviceIndex index); + Device(std::shared_ptr raw_device, DeviceIndex index); + ~Device(); std::string devicePath() const { return path; } - DeviceIndex deviceIndex() const { return index; } + DeviceIndex deviceIndex() const { return _index; } std::tuple version() const { return _version; } void listen(); // Runs asynchronously @@ -52,9 +54,11 @@ namespace hidpp void handleEvent(Report& report); private: + void _init(); + std::shared_ptr raw_device; std::string path; - DeviceIndex index; + DeviceIndex _index; uint8_t supported_reports; std::tuple _version; diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index 99e32ea..ac8b173 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -95,6 +95,27 @@ const char *Report::InvalidReportLength::what() const noexcept return "Invalid report length"; } +Report::Report(Report::Type type, DeviceIndex device_index, + uint8_t sub_id, uint8_t address) +{ + switch(type) + { + case Type::Short: + _data.resize(HeaderLength + ShortParamLength); + break; + case Type::Long: + _data.resize(HeaderLength + LongParamLength); + break; + default: + throw InvalidReportID(); + } + + _data[Offset::Type] = type; + _data[Offset::DeviceIndex] = device_index; + _data[Offset::SubID] = sub_id; + _data[Offset::Address] = address; +} + Report::Report(Report::Type type, DeviceIndex device_index, uint8_t feature_index, uint8_t function, uint8_t sw_id) { @@ -123,6 +144,7 @@ Report::Report(const std::vector& data) { _data = data; + // Truncating data is entirely valid here. switch(_data[Offset::Type]) { case Type::Short: @@ -136,6 +158,11 @@ Report::Report(const std::vector& data) } } +Report::Type Report::type() const +{ + return static_cast(_data[Offset::Type]); +} + void Report::setType(Report::Type type) { switch(type) @@ -153,6 +180,58 @@ void Report::setType(Report::Type type) _data[Offset::Type] = type; } +uint8_t Report::feature() const +{ + return _data[Offset::Feature]; +} + +void Report::setFeature(uint8_t feature) +{ + _data[Offset::Parameters] = feature; +} + +uint8_t Report::subId() const +{ + return _data[Offset::SubID]; +} + +void Report::setSubId(uint8_t sub_id) +{ + _data[Offset::SubID] = sub_id; +} + +uint8_t Report::function() const +{ + return (_data[Offset::Function] >> 4) & 0x0f; +} + +void Report::setFunction(uint8_t function) +{ + _data[Offset::Function] &= 0x0f; + _data[Offset::Function] |= (function << 4) & 0x0f; +} + +uint8_t Report::swId() const +{ + return _data[Offset::Function] & 0x0f; +} + +void Report::setSwId(uint8_t sub_id) +{ + _data[Offset::Function] &= 0x0f; + _data[Offset::Function] |= sub_id & 0x0f; +} + +uint8_t Report::address() const +{ + return _data[Offset::Address]; +} + +void Report::setAddress(uint8_t address) +{ + _data[Offset::Address] = address; +} + void Report::setParams(const std::vector& _params) { assert(_params.size() <= _data.size()-HeaderLength); diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index 929f783..8945cf1 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -20,6 +20,7 @@ namespace logid::backend::hidpp static constexpr uint8_t DeviceIndex = 1; static constexpr uint8_t SubID = 2; static constexpr uint8_t Feature = 2; + static constexpr uint8_t Address = 3; static constexpr uint8_t Function = 3; static constexpr uint8_t Parameters = 4; } @@ -47,16 +48,37 @@ namespace logid::backend::hidpp static constexpr uint8_t swIdMask = 0x0f; static constexpr uint8_t functionMask = 0x0f; + Report(Report::Type type, DeviceIndex device_index, + uint8_t sub_id, + uint8_t address); Report(Report::Type type, DeviceIndex device_index, uint8_t feature_index, uint8_t function, uint8_t sw_id); explicit Report(const std::vector& data); - Report::Type type() const { return static_cast(_data[Offset::Type]); }; + Report::Type type() const; void setType(Report::Type type); - std::vector::iterator paramBegin() { return _data.begin() + Offset::Parameters; } + uint8_t feature() const; + void setFeature(uint8_t feature); + + uint8_t subId() const; + void setSubId(uint8_t sub_id); + + uint8_t function() const; + void setFunction(uint8_t function); + + uint8_t swId() const; + void setSwId(uint8_t sw_id); + + uint8_t address() const; + void setAddress(uint8_t address); + + std::vector::iterator paramBegin() + { + return _data.begin() + Offset::Parameters; + } std::vector::iterator paramEnd() { return _data.end(); } void setParams(const std::vector& _params); diff --git a/src/logid/backend/hidpp10/Device.cpp b/src/logid/backend/hidpp10/Device.cpp new file mode 100644 index 0000000..b69e579 --- /dev/null +++ b/src/logid/backend/hidpp10/Device.cpp @@ -0,0 +1,62 @@ +#include +#include +#include "Device.h" +#include "defs.h" + +using namespace logid::backend; +using namespace logid::backend::hidpp10; + +Device::Device(const std::string &path, hidpp::DeviceIndex index) : + hidpp::Device(path, index) +{ + assert(version() == std::make_tuple(1, 0)); +} + +Device::Device(std::shared_ptr raw_dev, + hidpp::DeviceIndex index) : hidpp::Device(std::move(raw_dev), index) +{ + assert(version() == std::make_tuple(1, 0)); +} + +std::vector Device::getRegister(uint8_t address, + const std::vector& params) +{ + 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; + + return accessRegister(sub_id, address, params); +} + +std::vector Device::setRegister(uint8_t address, + const std::vector& params) +{ + 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; + + return accessRegister(sub_id, address, params); +} + +std::vector Device::accessRegister(uint8_t sub_id, uint8_t address, const std::vector ¶ms) +{ + hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? + hidpp::Report::Type::Short : hidpp::Report::Type::Long; + + hidpp::Report request(type, deviceIndex(), sub_id, address); + std::copy(params.begin(), params.end(), request.paramBegin()); + + auto response = sendReport(request); + return std::vector(response.paramBegin(), response.paramEnd()); +} + + + diff --git a/src/logid/backend/hidpp10/Device.h b/src/logid/backend/hidpp10/Device.h new file mode 100644 index 0000000..76da044 --- /dev/null +++ b/src/logid/backend/hidpp10/Device.h @@ -0,0 +1,28 @@ +#ifndef LOGID_BACKEND_HIDPP10_DEVICE_H +#define LOGID_BACKEND_HIDPP10_DEVICE_H + +#include "../hidpp/Device.h" + +namespace logid { +namespace backend { +namespace hidpp10 +{ + class Device : public hidpp::Device + { + public: + Device(const std::string& path, hidpp::DeviceIndex index); + Device(std::shared_ptr raw_dev, + hidpp::DeviceIndex index); + + std::vector getRegister(uint8_t address, + const std::vector& params); + + std::vector setRegister(uint8_t address, + const std::vector& params); + private: + std::vector accessRegister(uint8_t sub_id, + uint8_t address, const std::vector& params); + }; +}}} + +#endif //LOGID_BACKEND_HIDPP10_DEVICE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp10/defs.h b/src/logid/backend/hidpp10/defs.h new file mode 100644 index 0000000..1d080f1 --- /dev/null +++ b/src/logid/backend/hidpp10/defs.h @@ -0,0 +1,17 @@ +#ifndef LOGID_BACKEND_HIDPP10_DEFS_H +#define LOGID_BACKEND_HIDPP10_DEFS_H + +namespace logid { +namespace backend { +namespace hidpp10 +{ + enum SubID: uint8_t + { + SetRegisterShort = 0x80, + GetRegisterShort = 0x81, + SetRegisterLong = 0x82, + GetRegisterLong = 0x83 + }; +}}} + +#endif //LOGID_BACKEND_HIDPP10_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index eeabeb5..d7b9b60 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -75,6 +75,8 @@ RawDevice::RawDevice(std::string path) : path (path), continue_listen (false) close(fd); throw std::system_error(err, std::system_category(), "RawDevice pipe open failed"); } + + continue_listen = false; } RawDevice::~RawDevice() @@ -106,6 +108,27 @@ std::vector RawDevice::sendReport(const std::vector& report) return _respondToReport(report); } +// DJ commands are not systematically acknowledged, do not expect a result. +void RawDevice::sendReportNoResponse(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) + { + std::packaged_task()> task([this, report]() { + this->_sendReport(report); + return std::vector(); + }); + auto f = task.get_future(); + write_queue.push(&task); + f.get(); + } + else + _sendReport(report); +} + std::vector RawDevice::_respondToReport (const std::vector& request) { diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index 8e78003..1eebeb0 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -29,6 +29,7 @@ namespace raw std::vector reportDescriptor() const { return rdesc; } std::vector sendReport(const std::vector& report); + void sendReportNoResponse(const std::vector& report); void interruptRead(); void listen(); From 7f1af8191010fdc50e6fa53b3d0159e233f271ca Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 19 Jun 2020 21:58:33 -0400 Subject: [PATCH 15/81] Rewrite logid::DeviceMonitor --- src/logid/CMakeLists.txt | 2 + src/logid/Device.cpp | 547 +---------------------- src/logid/Device.h | 172 +------ src/logid/DeviceMonitor.cpp | 199 +++------ src/logid/DeviceMonitor.h | 27 +- src/logid/Receiver.cpp | 9 + src/logid/Receiver.h | 17 + src/logid/backend/dj/ReceiverMonitor.cpp | 0 src/logid/backend/dj/ReceiverMonitor.h | 6 + src/logid/backend/hidpp20/Device.cpp | 12 + src/logid/backend/hidpp20/Device.h | 3 + src/logid/util.h | 2 +- 12 files changed, 129 insertions(+), 867 deletions(-) create mode 100644 src/logid/Receiver.cpp create mode 100644 src/logid/Receiver.h create mode 100644 src/logid/backend/dj/ReceiverMonitor.cpp create mode 100644 src/logid/backend/dj/ReceiverMonitor.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 4ce4d1f..4aed930 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -12,6 +12,8 @@ add_executable(logid logid.cpp util.cpp DeviceMonitor.cpp + Device.cpp + Receiver.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 215a833..a5689eb 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -1,551 +1,20 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Device.h" #include "util.h" -#include "EvdevDevice.h" +#include "Device.h" using namespace logid; -using namespace std::chrono_literals; - -Device::Device(std::string p, const HIDPP::DeviceIndex i) : path(std::move(p)), index (i) +Device::Device(std::string path, backend::hidpp::DeviceIndex index) : + _hidpp20 (path, index), _path (path), _index (index) { - disconnected = true; - dispatcher = new HIDPP::SimpleDispatcher(path.c_str()); - listener = new SimpleListener(new HIDPP::SimpleDispatcher(path.c_str()), index); - config = new DeviceConfig(); + log_printf(DEBUG, "logid::Device created on %s:%d"); } -bool Device::init() +void Device::sleep() { - // 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(); - - if(std::find(global_config->blacklist.begin(), global_config->blacklist.end(), - hidpp_dev->productID()) != global_config->blacklist.end()) - { - log_printf(INFO, "Ignored blacklisted device %s", name.c_str()); - throw BlacklistedDevice(); - } - - features = getFeatures(); - // 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()); - else - { - delete(config); - config = global_config->devices.find(name)->second; - } - - initialized = true; - - return true; + log_printf(INFO, "%s:%d fell asleep.", _path.c_str(), _index); } -Device::~Device() +void Device::wakeup() { - if(!disconnected) - this->reset(); - if(!config->baseConfig) - delete(this->config); -} - -void Device::configure() -{ - if(config->baseConfig) - config = new DeviceConfig(config, this); - - if(!configuring.try_lock()) - { - log_printf(DEBUG, "%s %d: skip config task", path.c_str(), index); - return; - } - usleep(500000); - - try - { - if(disconnected) - goto ret; - // Divert buttons - divert_buttons(); - - if(disconnected) - goto ret; - // Set DPI if it is configured - if(config->dpi != nullptr) - setDPI(*config->dpi); - - if(disconnected) - goto ret; - // Set Smartshift if it is configured - if(config->smartshift != nullptr) - setSmartShift(*config->smartshift); - - if(disconnected) - goto ret; - // Set Hires Scroll if it is configured - if(config->hiresscroll != nullptr) - setHiresScroll(*config->hiresscroll); - } - catch(HIDPP10::Error &e) - { - log_printf(ERROR, "HID++ 1.0 Error whjle configuring %s: %s", name.c_str(), e.what()); - } - -ret: - configuring.unlock(); -} - -void Device::reset() -{ - try - { - HIDPP20::IReset iReset(hidpp_dev); - iReset.reset(); - } - catch(HIDPP20::UnsupportedFeature &e) { } - catch(HIDPP10::Error &e) - { - log_printf(ERROR, "Failed to reset %s: %s", name.c_str(), e.what()); - } -} - -void Device::divert_buttons() -{ - try - { - HIDPP20::IReprogControls irc = HIDPP20::IReprogControls::auto_version(hidpp_dev); - if(disconnected) - return; - int controlCount = irc.getControlCount(); - for(int i = 0; i < controlCount; i++) - { - if(disconnected) - return; - uint16_t cid = irc.getControlInfo(i).control_id; - uint8_t flags = 0; - flags |= HIDPP20::IReprogControls::ChangeTemporaryDivert; - flags |= HIDPP20::IReprogControls::ChangeRawXYDivert; - - auto action = config->actions.find(cid); - if(action != config->actions.end()) - { - flags |= HIDPP20::IReprogControls::ChangeTemporaryDivert; - flags |= HIDPP20::IReprogControls::TemporaryDiverted; - if(action->second->type == Action::Gestures) - flags |= HIDPP20::IReprogControls::RawXYDiverted; - } - if(disconnected) - return; - irc.setControlReporting(cid, flags, cid); - } - } - catch(HIDPP20::UnsupportedFeature &e) - { - log_printf(DEBUG, "%s does not support Reprog controls, not diverting!", name.c_str()); - } - catch(HIDPP20::Error &e) - { - if(e.errorCode() == HIDPP20::Error::InvalidFunctionID) { - // Not really an error, the device does not support diverting buttons - } - else { - log_printf(ERROR, "Could not divert buttons: HID++ 2.0 Error %s!", e.what()); - } - } - catch(HIDPP10::Error &e) - { - log_printf(DEBUG, "Could not divert buttons: HID++ 1.0 Error %s!", e.what()); - } -} - -void Device::setSmartShift(HIDPP20::ISmartShift::SmartshiftStatus ops) -{ - try - { - if(disconnected) return; - HIDPP20::ISmartShift ss(hidpp_dev); - if(disconnected) return; - ss.setStatus(ops); - } - catch (HIDPP20::UnsupportedFeature &e) - { - log_printf(ERROR, "Device does not support SmartShift"); - } - catch (HIDPP20::Error &e) - { - log_printf(ERROR, "Error setting SmartShift options, code %d: %s\n", e.errorCode(), e.what()); - } -} - -void Device::setHiresScroll(uint8_t ops) -{ - try - { - if(disconnected) return; - HIDPP20::IHiresScroll hs(hidpp_dev); - if(disconnected) return; - hs.setMode(ops); - } - catch (HIDPP20::UnsupportedFeature &e) - { - log_printf(ERROR, "Device does not support Hires Scrolling"); - } - catch (HIDPP20::Error &e) - { - log_printf(ERROR, "Error setting Hires Scrolling options, code %d: %s\n", e.errorCode(), e.what()); - } -} - -void Device::setDPI(int dpi) -{ - if(disconnected) return; - HIDPP20::IAdjustableDPI iad(hidpp_dev); - if(disconnected) return; - try { for(unsigned int i = 0; i < iad.getSensorCount(); i++) iad.setSensorDPI(i, dpi); } - catch (HIDPP20::Error &e) { log_printf(ERROR, "Error while setting DPI: %s", e.what()); } -} - -void Device::waitForReceiver() -{ - 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); - - try - { - if(this->init()) break; - } - catch(BlacklistedDevice& e) { return; } - - 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::printCIDs() { - try - { - HIDPP20::IReprogControls irc = HIDPP20::IReprogControls::auto_version(hidpp_dev); - if(disconnected) - return; - int controlCount = irc.getControlCount(); - for(int i = 0; i < controlCount; i++) - { - if(disconnected) - return; - uint16_t cid = irc.getControlInfo(i).control_id; - log_printf(DEBUG, "Available CID: 0x%x", cid); - } - } - catch(HIDPP20::UnsupportedFeature &e) - { - log_printf(DEBUG, "%s does not support Reprog controls, not diverting!", name.c_str()); - } -} - -void Device::start() -{ - printCIDs(); - configure(); - try { listener->addEventHandler(std::make_unique(this)); } - catch(HIDPP20::UnsupportedFeature &e) { } - - if(index == HIDPP::DefaultDevice || index == HIDPP::CordedDevice) - { - try { listener->addEventHandler( std::make_unique(this) ); } - catch(HIDPP20::UnsupportedFeature &e) { } - } - 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); - - return false; -} - -void ButtonHandler::handleEvent (const HIDPP::Report &event) -{ - switch (event.function()) - { - case HIDPP20::IReprogControls::Event::DivertedButtonEvent: - { - new_states = HIDPP20::IReprogControls::divertedButtonEvent(event); - if (states.empty()) - { - for (uint16_t i : new_states) - std::thread{[=]() { dev->pressButton(i); }}.detach(); - states = new_states; - break; - } - std::vector::iterator it; - std::vector cids(states.size() + new_states.size()); - it = std::set_union(states.begin(), states.end(), new_states.begin(), new_states.end(), cids.begin()); - cids.resize((ulong)(it - cids.begin())); - for (uint16_t i : cids) - { - if (std::find(new_states.begin(), new_states.end(), i) != new_states.end()) - { - if (std::find(states.begin(), states.end(), i) == states.end()) - std::thread{[=]() { dev->pressButton(i); }}.detach(); - } else - std::thread{[=]() { dev->releaseButton(i); }}.detach(); - } - states = new_states; - break; - } - case HIDPP20::IReprogControlsV4::Event::DivertedRawXYEvent: - { - auto raw_xy = HIDPP20::IReprogControlsV4::divertedRawXYEvent(event); - - for(uint16_t i : states) - std::thread{[=]() { dev->moveDiverted(i, raw_xy); }}.detach(); - break; - } - default: - break; - } -} - -void ReceiverHandler::handleEvent(const HIDPP::Report &event) -{ - switch(event.featureIndex()) - { - case HIDPP10::IReceiver::DeviceUnpaired: - { - 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); - if(status == HIDPP10::IReceiver::LinkLoss) - { - log_printf(INFO, "Link lost to %s", dev->name.c_str()); - dev->disconnected = true; - } - else if (status == HIDPP10::IReceiver::ConnectionEstablished) - { - 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; - } - default: - break; - } -} - -void WirelessStatusHandler::handleEvent(const HIDPP::Report &event) -{ - switch(event.function()) - { - case HIDPP20::IWirelessDeviceStatus::StatusBroadcast: - { - auto status = HIDPP20::IWirelessDeviceStatus::statusBroadcastEvent(event); - if(status.ReconfNeeded) - dev->configure(); - break; - } - default: - { - log_printf(DEBUG, "Undocumented event %02x from WirelessDeviceStatus", event.function()); - break; - } - } -} - -void EventListener::removeEventHandlers () -{ - for (const auto &p: iterators) - dispatcher->unregisterEventHandler(p.second); - handlers.clear(); - iterators.clear(); -} - -EventListener::~EventListener() -{ - removeEventHandlers(); -} - -void EventListener::addEventHandler(std::unique_ptr &&handler) -{ - EventHandler *ptr = handler.get(); - for(uint8_t feature : handler->featureIndices()) - { - handlers.emplace(feature, std::move(handler)); - dispatcher->registerEventHandler(index, feature, [=](const HIDPP::Report &report) - { - ptr->handleEvent(report); - return true; - }); - } -} - -void SimpleListener::start() -{ - 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(); -} - -bool SimpleListener::event (EventHandler *handler, const HIDPP::Report &report) -{ - handler->handleEvent (report); - return true; -} - -void Device::stop() -{ - disconnected = true; - listener->stop(); -} - -void Device::pressButton(uint16_t cid) -{ - if(config->actions.find(cid) == config->actions.end()) - { - log_printf(DEBUG, "0x%x was pressed but no action was found.", cid); - return; - } - config->actions.find(cid)->second->press(); -} - -void Device::releaseButton(uint16_t cid) -{ - if(config->actions.find(cid) == config->actions.end()) - { - log_printf(DEBUG, "0x%x was released but no action was found.", cid); - return; - } - config->actions.find(cid)->second->release(); -} - -void Device::moveDiverted(uint16_t cid, HIDPP20::IReprogControlsV4::Move m) -{ - auto action = config->actions.find(cid); - if(action == config->actions.end()) - return; - switch(action->second->type) - { - case Action::Gestures: - ((GestureAction*)action->second)->move(m); - break; - default: - break; - } -} - -std::map Device::getFeatures() -{ - std::map _features; - HIDPP20::IFeatureSet ifs (hidpp_dev); - uint8_t feature_count = ifs.getCount(); - - for(uint8_t i = 0; i < feature_count; i++) - _features.insert( {i, ifs.getFeatureID(i) } ); - - return _features; + log_printf(INFO, "%s:%d woke up.", _path.c_str(), _index); } diff --git a/src/logid/Device.h b/src/logid/Device.h index 4a3ca6c..a9f22da 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -1,172 +1,26 @@ #ifndef LOGID_DEVICE_H #define LOGID_DEVICE_H -#include "Actions.h" -#include "DeviceMonitor.h" -#include "Configuration.h" - -#include -#include -#include -#include -#include -#include -#include +#include "backend/hidpp/defs.h" +#include "backend/hidpp20/Device.h" namespace logid { - class EventListener; - class DeviceConfig; - - class BlacklistedDevice : public std::exception - { - public: - BlacklistedDevice() = default; - virtual const char* what() - { - return "Blacklisted device"; - } - }; - + /* TODO: Implement HID++ 1.0 support + * Currently, the logid::Device class has a hardcoded requirement + * for an HID++ 2.0 device. + */ class Device { public: - Device(std::string p, const HIDPP::DeviceIndex i); - ~Device(); - - std::string name; - - bool init(); - void configure(); - void reset(); - - void pressButton(uint16_t cid); - void releaseButton(uint16_t cid); - void moveDiverted(uint16_t cid, HIDPP20::IReprogControlsV4::Move move); - - void waitForReceiver(); - void start(); - void stop(); - bool testConnection(); - - std::map getFeatures(); - - std::map features; - - const std::string path; - const HIDPP::DeviceIndex index; - HIDPP::Dispatcher* dispatcher; - HIDPP20::Device* hidpp_dev; - - std::mutex configuring; - std::atomic_bool disconnected; - bool initialized = false; - bool waiting_for_receiver = false; - - protected: - DeviceConfig* config; - EventListener* listener; - - void divert_buttons(); - void printCIDs(); - void setSmartShift(HIDPP20::ISmartShift::SmartshiftStatus ops); - void setHiresScroll(uint8_t flags); - void setDPI(int dpi); + Device(std::string path, backend::hidpp::DeviceIndex index); + void wakeup(); + void sleep(); + private: + backend::hidpp20::Device _hidpp20; + std::string _path; + backend::hidpp::DeviceIndex _index; }; - - class EventHandler - { - public: - virtual const HIDPP20::FeatureInterface *feature() const = 0; - virtual const std::vector featureIndices() const - { - return {feature()->index()}; - }; - virtual void handleEvent (const HIDPP::Report &event) = 0; - }; - class ButtonHandler : public EventHandler - { - public: - ButtonHandler (Device *d) : dev (d), _irc (HIDPP20::IReprogControls::auto_version(d->hidpp_dev)) { } - const HIDPP20::FeatureInterface *feature () const - { - return &_irc; - } - void handleEvent (const HIDPP::Report &event); - protected: - Device* dev; - HIDPP20::IReprogControls _irc; - std::vector states; - std::vector new_states; - }; - class ReceiverHandler : public EventHandler - { - public: - ReceiverHandler (Device *d) : dev (d) { } - const HIDPP20::FeatureInterface *feature () const - { - return nullptr; // This sounds like a horrible idea - } - virtual const std::vector featureIndices() const - { - return HIDPP10::IReceiver::Events; - } - void handleEvent (const HIDPP::Report &event); - protected: - Device* dev; - }; - class WirelessStatusHandler : public EventHandler - { - public: - WirelessStatusHandler (Device *d) : dev (d), _iws (d->hidpp_dev) { } - const HIDPP20::FeatureInterface *feature () const - { - return &_iws; - } - void handleEvent (const HIDPP::Report &event); - protected: - Device* dev; - HIDPP20::IWirelessDeviceStatus _iws; - }; - - class EventListener - { - HIDPP::Dispatcher *dispatcher; - HIDPP::DeviceIndex index; - std::map> handlers; - std::map iterators; - public: - EventListener (HIDPP::Dispatcher *dispatcher, HIDPP::DeviceIndex index): dispatcher (dispatcher), index (index) {} - - virtual void removeEventHandlers (); - virtual ~EventListener(); - virtual void addEventHandler (std::unique_ptr &&handler); - - virtual void start () = 0; - virtual void stop () = 0; - - protected: - virtual bool event (EventHandler* handler, const HIDPP::Report &report) = 0; - }; - class SimpleListener : public EventListener - { - HIDPP::SimpleDispatcher *dispatcher; - - public: - SimpleListener (HIDPP::SimpleDispatcher* dispatcher, HIDPP::DeviceIndex index): - EventListener (dispatcher, index), - dispatcher (dispatcher) - { - } - - bool stopped = false; - virtual void start(); - virtual void stop(); - - protected: - virtual bool event (EventHandler* handler, const HIDPP::Report &report); - }; - } #endif //LOGID_DEVICE_H diff --git a/src/logid/DeviceMonitor.cpp b/src/logid/DeviceMonitor.cpp index a24aec4..0f9beb4 100644 --- a/src/logid/DeviceMonitor.cpp +++ b/src/logid/DeviceMonitor.cpp @@ -2,6 +2,7 @@ #include #include "DeviceMonitor.h" +#include "Receiver.h" #include "util.h" #include "backend/hidpp10/Error.h" #include "backend/dj/Receiver.h" @@ -11,161 +12,65 @@ using namespace logid; using namespace logid::backend; -/* -void stopAndDeleteConnectedDevice (ConnectedDevice &connected_device) -{ - 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); -} - -DeviceMonitor::~DeviceMonitor() -{ - this->devices_mutex.lock(); - for (auto it = this->devices.begin(); it != this->devices.end(); it++) { - for (auto jt = it->second.begin(); jt != it->second.end(); jt++) { - stopAndDeleteConnectedDevice(jt->second); - } - } - this->devices_mutex.unlock(); -} - -Device* DeviceMonitor::insertNewDevice(const std::string &path, HIDPP::DeviceIndex 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()); - auto path_bucket = this->devices.emplace(path, std::map()).first; - path_bucket->second.emplace(index, ConnectedDevice{ - device, - std::thread([device]() { - device->start(); - }) - }); - this->devices_mutex.unlock(); - - return device; -} - -Device* DeviceMonitor::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->waitForReceiver(); - }) - }); - this->devices_mutex.unlock(); - - return device; -} - -void DeviceMonitor::stopAndDeleteAllDevicesIn (const std::string &path) -{ - this->devices_mutex.lock(); - auto path_bucket = this->devices.find(path); - if (path_bucket != this->devices.end()) - { - for (auto& index_bucket : path_bucket->second) { - stopAndDeleteConnectedDevice(index_bucket.second); - } - this->devices.erase(path_bucket); - } - this->devices_mutex.unlock(); -} - -void DeviceMonitor::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index) -{ - this->devices_mutex.lock(); - auto path_bucket = this->devices.find(path); - if (path_bucket != this->devices.end()) - { - auto index_bucket = path_bucket->second.find(index); - if (index_bucket != path_bucket->second.end()) - { - stopAndDeleteConnectedDevice(index_bucket->second); - path_bucket->second.erase(index_bucket); - } - } - this->devices_mutex.unlock(); - - log_printf(WARN, "Attempted to disconnect not previously connected device %d on %s", index, path.c_str()); -} -*/ - void DeviceMonitor::addDevice(std::string path) { + bool defaultExists = true; + bool isReceiver = false; try { - std::tuple version; - try - { - hidpp::Device device(path, hidpp::DefaultDevice); - - log_printf(DEBUG, "Detected HID++ device at %s", path.c_str()); - - version = device.version(); - log_printf(DEBUG, "HID++ version: %d.%d", std::get<0>(version), std::get<1>(version)); - } catch(std::system_error &e) { } - - if(version == std::make_tuple(1, 0)) - { - // This is a receiver - dj::Receiver receiver(path); - receiver.enumerate(); - receiver.listen(); - while(true) {} - } - /* - auto eventHandler = std::make_shared(); - eventHandler->condition = [device](backend::hidpp::Report& report)->bool - { - return true; - }; - eventHandler->callback = [device](backend::hidpp::Report& report)->void - { - log_printf(DEBUG, "Event on %s:%d", device->devicePath().c_str(), - device->deviceIndex()); - for(auto& i : report.rawReport()) - printf("%02x ", i); - printf("\n"); - }; - - device->addEventHandler("MONITOR_ALL", eventHandler); - - devices.push_back(device);*/ - - //std::thread([device]() { device->listen(); }).detach(); - } - catch(hidpp10::Error &e) - { - if(e.code() == hidpp10::Error::UnknownDevice) {} - else + hidpp::Device device(path, hidpp::DefaultDevice); + isReceiver = device.version() == std::make_tuple(1, 0); + } catch(hidpp10::Error &e) { + if(e.code() != hidpp10::Error::UnknownDevice) throw; + } catch(hidpp::Device::InvalidDevice &e) { // Ignore + defaultExists = false; + } catch(std::system_error &e) { + log_printf(WARN, "I/O error on %s: %s, skipping device.", + path.c_str(), e.what()); + return; } - catch(hidpp::Device::InvalidDevice &e) - { - log_printf(DEBUG, "Detected device at %s but %s", path.c_str(), e.what()); + + if(isReceiver) { + log_printf(INFO, "Detected receiver at %s", path.c_str()); + auto receiver = std::make_shared(path); + _receivers.emplace(path, receiver); + // receiver->listen(); + } else { + /* TODO: Error check? + * TODO: Can non-receivers only contain 1 device? + * If the device exists, it is guaranteed to be an HID++ 2.0 device */ + if(defaultExists) { + auto device = std::make_shared(path, hidpp::DefaultDevice); + _devices.emplace(path, device); + } else { + try { + auto device = std::make_shared(path, hidpp::CordedDevice); + _devices.emplace(path, device); + } catch(hidpp10::Error &e) { + if(e.code() != hidpp10::Error::UnknownDevice) + throw; + } catch(hidpp::Device::InvalidDevice &e) { // Ignore + } catch(std::system_error &e) { + // This error should have been thrown previously + log_printf(WARN, "I/O error on %s: %s", path.c_str(), + e.what()); + } + } } - /* - catch(std::system_error &e) - { - log_printf(WARN, "Failed to open %s: %s", path.c_str(), e.what()); - } */ } void DeviceMonitor::removeDevice(std::string path) { - log_printf(DEBUG, "Device %s disconnected", path.c_str()); - /* - ipc_server->removeReceiver(path); - this->stopAndDeleteAllDevicesIn(std::string(path)); - */ + auto receiver = _receivers.find(path); + + if(receiver != _receivers.end()) { + _receivers.erase(receiver); + log_printf(INFO, "Receiver on %s disconnected", path.c_str()); + } else { + auto device = _devices.find(path); + if(device != _devices.find(path)) { + _devices.erase(device); + log_printf(INFO, "Device on %s disconnected", path.c_str()); + } + } } diff --git a/src/logid/DeviceMonitor.h b/src/logid/DeviceMonitor.h index f95f642..dfd7650 100644 --- a/src/logid/DeviceMonitor.h +++ b/src/logid/DeviceMonitor.h @@ -7,38 +7,23 @@ #include "backend/raw/DeviceMonitor.h" #include "backend/hidpp/Device.h" - -#define MAX_CONNECTION_TRIES 10 -#define TIME_BETWEEN_CONNECTION_TRIES 500ms +#include "Device.h" +#include "Receiver.h" namespace logid { - class Device; - - struct ConnectedDevice { - Device *device; - std::thread associatedThread; - }; - class DeviceMonitor : public backend::raw::DeviceMonitor { public: - ~DeviceMonitor(); - - /* - 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); - */ + DeviceMonitor() = default; protected: void addDevice(std::string path) override; void removeDevice(std::string path) override; private: - std::mutex devices_mutex; - std::vector> devices; //tmp - //std::map> devices; + + std::map> _devices; + std::map> _receivers; }; extern DeviceMonitor* finder; diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp new file mode 100644 index 0000000..77d34c8 --- /dev/null +++ b/src/logid/Receiver.cpp @@ -0,0 +1,9 @@ +#include "Receiver.h" +#include "util.h" + +using namespace logid; + +Receiver::Receiver(std::string path) : _path (path) +{ + log_printf(DEBUG, "logid::Receiver created on %s", path.c_str()); +} \ No newline at end of file diff --git a/src/logid/Receiver.h b/src/logid/Receiver.h new file mode 100644 index 0000000..a01f37f --- /dev/null +++ b/src/logid/Receiver.h @@ -0,0 +1,17 @@ +#ifndef LOGID_RECEIVER_H +#define LOGID_RECEIVER_H + +#include + +namespace logid +{ + class Receiver + { + public: + Receiver(std::string path); + private: + std::string _path; + }; +} + +#endif //LOGID_RECEIVER_H \ No newline at end of file diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/logid/backend/dj/ReceiverMonitor.h b/src/logid/backend/dj/ReceiverMonitor.h new file mode 100644 index 0000000..f717da0 --- /dev/null +++ b/src/logid/backend/dj/ReceiverMonitor.h @@ -0,0 +1,6 @@ +#ifndef LOGID_BACKEND_DJ_RECEIVERMONITOR_H +#define LOGID_BACKEND_DJ_RECEIVERMONITOR_H + + + +#endif //LOGID_BACKEND_DJ_RECEIVERMONITOR_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Device.cpp b/src/logid/backend/hidpp20/Device.cpp index 917601c..0d93ecc 100644 --- a/src/logid/backend/hidpp20/Device.cpp +++ b/src/logid/backend/hidpp20/Device.cpp @@ -5,6 +5,18 @@ using namespace logid::backend::hidpp20; +Device::Device(std::string path, hidpp::DeviceIndex index) + : hidpp::Device(path, index) +{ + assert(std::get<0>(version()) >= 2); +} + +Device::Device(std::shared_ptr raw_device, hidpp::DeviceIndex index) + : hidpp::Device(raw_device, index) +{ + assert(std::get<0>(version()) >= 2); +} + std::vector Device::callFunction(uint8_t feature_index, uint8_t function, std::vector& params) { diff --git a/src/logid/backend/hidpp20/Device.h b/src/logid/backend/hidpp20/Device.h index 38f42eb..c0b2ff4 100644 --- a/src/logid/backend/hidpp20/Device.h +++ b/src/logid/backend/hidpp20/Device.h @@ -10,6 +10,9 @@ namespace hidpp20 { class Device : public hidpp::Device { public: + Device(std::string path, hidpp::DeviceIndex index); + Device(std::shared_ptr raw_device, hidpp::DeviceIndex index); + std::vector callFunction(uint8_t feature_index, uint8_t function, std::vector& params); diff --git a/src/logid/util.h b/src/logid/util.h index 09bea48..1478fed 100644 --- a/src/logid/util.h +++ b/src/logid/util.h @@ -1,7 +1,7 @@ #ifndef LOGID_UTIL_H #define LOGID_UTIL_H -//#include "Actions.h" +#include namespace logid { From f0c903d5393711d15e47ddfe86ca7b2befc31fbf Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 19 Jun 2020 22:01:14 -0400 Subject: [PATCH 16/81] Rename DeviceMonitor to DeviceManager --- src/logid/CMakeLists.txt | 2 +- src/logid/{DeviceMonitor.cpp => DeviceManager.cpp} | 6 +++--- src/logid/{DeviceMonitor.h => DeviceManager.h} | 6 +++--- src/logid/logid.cpp | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) rename src/logid/{DeviceMonitor.cpp => DeviceManager.cpp} (95%) rename src/logid/{DeviceMonitor.h => DeviceManager.h} (77%) diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 4aed930..8d2c461 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -11,7 +11,7 @@ find_package(PkgConfig REQUIRED) add_executable(logid logid.cpp util.cpp - DeviceMonitor.cpp + DeviceManager.cpp Device.cpp Receiver.cpp backend/Error.cpp diff --git a/src/logid/DeviceMonitor.cpp b/src/logid/DeviceManager.cpp similarity index 95% rename from src/logid/DeviceMonitor.cpp rename to src/logid/DeviceManager.cpp index 0f9beb4..c975c1e 100644 --- a/src/logid/DeviceMonitor.cpp +++ b/src/logid/DeviceManager.cpp @@ -1,7 +1,7 @@ #include #include -#include "DeviceMonitor.h" +#include "DeviceManager.h" #include "Receiver.h" #include "util.h" #include "backend/hidpp10/Error.h" @@ -12,7 +12,7 @@ using namespace logid; using namespace logid::backend; -void DeviceMonitor::addDevice(std::string path) +void DeviceManager::addDevice(std::string path) { bool defaultExists = true; bool isReceiver = false; @@ -59,7 +59,7 @@ void DeviceMonitor::addDevice(std::string path) } } -void DeviceMonitor::removeDevice(std::string path) +void DeviceManager::removeDevice(std::string path) { auto receiver = _receivers.find(path); diff --git a/src/logid/DeviceMonitor.h b/src/logid/DeviceManager.h similarity index 77% rename from src/logid/DeviceMonitor.h rename to src/logid/DeviceManager.h index dfd7650..e4134bc 100644 --- a/src/logid/DeviceMonitor.h +++ b/src/logid/DeviceManager.h @@ -13,10 +13,10 @@ namespace logid { - class DeviceMonitor : public backend::raw::DeviceMonitor + class DeviceManager : public backend::raw::DeviceMonitor { public: - DeviceMonitor() = default; + DeviceManager() = default; protected: void addDevice(std::string path) override; void removeDevice(std::string path) override; @@ -26,7 +26,7 @@ namespace logid std::map> _receivers; }; - extern DeviceMonitor* finder; + extern DeviceManager* finder; } #endif //LOGID_DEVICEFINDER_H \ No newline at end of file diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 562341f..8313965 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -4,7 +4,7 @@ #include #include "util.h" -#include "DeviceMonitor.h" +#include "DeviceManager.h" #include "logid.h" #define evdev_name "logid" @@ -21,7 +21,7 @@ std::string config_file = DEFAULT_CONFIG_FILE; LogLevel logid::global_verbosity = INFO; // Configuration* logid::global_config; -DeviceMonitor* logid::finder; +DeviceManager* logid::finder; bool logid::kill_logid = false; std::mutex logid::finder_reloading; @@ -166,7 +166,7 @@ int main(int argc, char** argv) */ // Scan devices, create listeners, handlers, etc. - finder = new DeviceMonitor(); + finder = new DeviceManager(); while(!kill_logid) { From f2a7b7b9472cb9616d4c2b3e087b1c0a87a07c47 Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 19 Jun 2020 22:51:22 -0400 Subject: [PATCH 17/81] Fix long report descriptor check --- src/logid/Device.cpp | 2 +- src/logid/DeviceManager.cpp | 2 ++ src/logid/backend/hidpp/Report.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index a5689eb..96a61db 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -6,7 +6,7 @@ using namespace logid; Device::Device(std::string path, backend::hidpp::DeviceIndex index) : _hidpp20 (path, index), _path (path), _index (index) { - log_printf(DEBUG, "logid::Device created on %s:%d"); + log_printf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index); } void Device::sleep() diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index c975c1e..e4fd9ca 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -14,6 +14,8 @@ using namespace logid::backend; void DeviceManager::addDevice(std::string path) { + log_printf(DEBUG, "Scanning %s for devices.", path.c_str()); + bool defaultExists = true; bool isReceiver = false; try { diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index ac8b173..d68ce1e 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -76,7 +76,7 @@ uint8_t hidpp::getSupportedReports(std::vector&& rdesc) if(it != rdesc.end()) ret |= HIDPP_REPORT_SHORT_SUPPORTED; - it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc.begin(), LongReportDesc2.end()); + it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc.begin(), LongReportDesc.end()); if(it == rdesc.end()) it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc2.begin(), LongReportDesc2.end()); if(it != rdesc.end()) From fb0b8e88ef815ebff71fdab7ea8a528270a9de47 Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 19 Jun 2020 23:48:16 -0400 Subject: [PATCH 18/81] Clean up temporary code --- src/logid/DeviceManager.cpp | 3 --- src/logid/backend/hidpp/Device.cpp | 3 --- src/logid/backend/raw/RawDevice.cpp | 8 ++++++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index e4fd9ca..58418d7 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -14,8 +14,6 @@ using namespace logid::backend; void DeviceManager::addDevice(std::string path) { - log_printf(DEBUG, "Scanning %s for devices.", path.c_str()); - bool defaultExists = true; bool isReceiver = false; try { @@ -36,7 +34,6 @@ void DeviceManager::addDevice(std::string path) log_printf(INFO, "Detected receiver at %s", path.c_str()); auto receiver = std::make_shared(path); _receivers.emplace(path, receiver); - // receiver->listen(); } else { /* TODO: Error check? * TODO: Can non-receivers only contain 1 device? diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 4994aef..d6cbdc8 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -84,9 +84,6 @@ void Device::_init() Device::~Device() { raw_device->removeEventHandler("DEV_" + std::to_string(_index)); - ///TODO: tmp - raw_device->stopListener(); - raw_device.reset(); } void Device::addEventHandler(const std::string& nickname, const std::shared_ptr& handler) diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index d7b9b60..ca76cc9 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -270,6 +270,14 @@ void RawDevice::listen() this->handleEvent(report); } + // Listener is stopped, handle I/O queue + while(!write_queue.empty()) + { + auto task = write_queue.front(); + (*task)(); + write_queue.pop(); + } + continue_listen = false; } From 6bfa52e5c16e7ef678d18f6ae97da34df8ee17dc Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 19 Jun 2020 23:51:32 -0400 Subject: [PATCH 19/81] Remove libhidpp submodule --- .gitmodules | 4 ---- src/logid/CMakeLists.txt | 2 +- src/logid/hidpp | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 .gitmodules delete mode 160000 src/logid/hidpp diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 052fd29..0000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "src/logid/hidpp"] - path = src/logid/hidpp - url = https://github.com/PixlOne/hidpp.git - branch = master diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 8d2c461..8488eb7 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -42,7 +42,7 @@ find_path(EVDEV_INCLUDE_DIR libevdev/libevdev.h find_library(EVDEV_LIBRARY NAMES evdev libevdev) -include_directories(${HIDPP_INCLUDE_DIR} ${EVDEV_INCLUDE_DIR} ${DBUSCXX_INCLUDE_DIR} ${LIBUDEV_INCLUDE_DIRECTORIES}) +include_directories(${EVDEV_INCLUDE_DIR} ${DBUSCXX_INCLUDE_DIR} ${LIBUDEV_INCLUDE_DIRECTORIES}) target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ ${DBUSCXX_LIBRARIES} ${LIBUDEV_LIBRARIES}) diff --git a/src/logid/hidpp b/src/logid/hidpp deleted file mode 160000 index c64ec3a..0000000 --- a/src/logid/hidpp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c64ec3a12b203024e48993c1aadf4618b3dbd37c From c04408c2dd1453c05e3f38222de4642b15c4ec86 Mon Sep 17 00:00:00 2001 From: pixl Date: Sat, 20 Jun 2020 03:16:16 -0400 Subject: [PATCH 20/81] Implement logid::backend::dj::ReceiverMonitor --- src/logid/CMakeLists.txt | 1 + src/logid/backend/dj/Receiver.cpp | 68 +++++++++++++++++------- src/logid/backend/dj/Receiver.h | 40 +++++++++----- src/logid/backend/dj/ReceiverMonitor.cpp | 30 +++++++++++ src/logid/backend/dj/ReceiverMonitor.h | 32 +++++++++++ src/logid/backend/dj/defs.h | 15 ++++++ 6 files changed, 155 insertions(+), 31 deletions(-) diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 8488eb7..5be8549 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(logid backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp backend/dj/Receiver.cpp + backend/dj/ReceiverMonitor.cpp backend/dj/Error.cpp backend/hidpp/Device.cpp backend/hidpp/Report.cpp diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp index 7d5aef4..d579a32 100644 --- a/src/logid/backend/dj/Receiver.cpp +++ b/src/logid/backend/dj/Receiver.cpp @@ -12,7 +12,13 @@ InvalidReceiver::InvalidReceiver(Reason reason) : _reason (reason) const char* InvalidReceiver::what() const noexcept { - return "Invalid receiver"; + switch(_reason) + { + case NoDJReports: + return "No DJ reports"; + default: + return "Invalid receiver"; + } } InvalidReceiver::Reason InvalidReceiver::code() const noexcept @@ -69,10 +75,11 @@ Receiver::notification_flags Receiver::getHidppNotifications() auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {}); notification_flags flags{}; - flags.device_battery_status = response[0] & (1 << 4); flags.receiver_wireless_notifications = response[1] & (1 << 0); flags.receiver_software_present = response[1] & (1 << 3); + + return flags; } void Receiver::enableHidppNotifications(notification_flags flags) @@ -142,7 +149,8 @@ std::map Receiver::getDeviceActivity() return device_activity; } -Receiver::pairing_info Receiver::getPairingInfo(hidpp::DeviceIndex index) +struct Receiver::PairingInfo + Receiver::getPairingInfo(hidpp::DeviceIndex index) { std::vector request(1); request[0] = index; @@ -150,17 +158,17 @@ Receiver::pairing_info Receiver::getPairingInfo(hidpp::DeviceIndex index) auto response = _hidpp10_device.getRegister(PairingInfo, request); - pairing_info info{}; - info.destination_id = response[0]; - info.report_interval = response[1]; + struct PairingInfo info{}; + info.destinationId = response[0]; + info.reportInterval = response[1]; info.pid = response[2]; info.pid |= (response[3] << 8); - info.device_type = response[6]; + info.deviceType = static_cast(response[6]); return info; } -Receiver::extended_pairing_info +struct Receiver::ExtendedPairingInfo Receiver::getExtendedPairingInfo(hidpp::DeviceIndex index) { std::vector request(1); @@ -169,16 +177,16 @@ Receiver::extended_pairing_info auto response = _hidpp10_device.getRegister(PairingInfo, request); - extended_pairing_info info{}; + ExtendedPairingInfo info{}; - info.serial_number = 0; + info.serialNumber = 0; for(uint8_t i = 0; i < 4; i++) - info.serial_number |= (response[i] << 8*i); + info.serialNumber |= (response[i] << 8*i); for(uint8_t i = 0; i < 4; i++) - info.report_types[i] = response[i + 4]; + info.reportTypes[i] = response[i + 4]; - info.power_switch_location = response[8] & 0xf; + info.powerSwitchLocation = response[8] & 0xf; return info; } @@ -201,18 +209,35 @@ std::string Receiver::getDeviceName(hidpp::DeviceIndex index) return name; } -hidpp::DeviceIndex Receiver::deviceConnectionEvent(hidpp::Report &report) -{ - assert(report.subId() == DeviceConnection); - return report.deviceIndex(); -} - hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(hidpp::Report& report) { assert(report.subId() == DeviceDisconnection); return report.deviceIndex(); } +Receiver::DeviceConnectionEvent Receiver::deviceConnectionEvent( + hidpp::Report &report) +{ + assert(report.subId() == DeviceConnection); + + DeviceConnectionEvent event{}; + + event.index = report.deviceIndex(); + event.unifying = ((report.paramBegin()[0] & 0b111) == 0x04); + + event.deviceType = static_cast( + report.paramBegin()[1] & 0x0f); + event.softwarePresent = report.paramBegin()[1] & (1<<4); + event.encrypted = report.paramBegin()[1] & (1<<5); + event.linkEstablished = report.paramBegin()[1] & (1<<6); + event.withPayload = report.paramBegin()[1] & (1<<7); + + event.pid = report.paramBegin()[3]; + event.pid |= (report.paramBegin()[2] << 8); + + return event; +} + void Receiver::handleDjEvent(Report& report) { if(report.feature() == DeviceConnection || @@ -252,4 +277,9 @@ void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function, void Receiver::listen() { std::thread{[=]() { raw_device->listen(); }}.detach(); +} + +void Receiver::stopListening() +{ + raw_device->stopListener(); } \ No newline at end of file diff --git a/src/logid/backend/dj/Receiver.h b/src/logid/backend/dj/Receiver.h index 24ffa19..3954a20 100644 --- a/src/logid/backend/dj/Receiver.h +++ b/src/logid/backend/dj/Receiver.h @@ -86,33 +86,49 @@ namespace dj std::map getDeviceActivity(); - struct pairing_info + struct PairingInfo { - uint8_t destination_id; - uint8_t report_interval; + uint8_t destinationId; + uint8_t reportInterval; uint16_t pid; - uint8_t device_type; ///TODO: Create enum for DeviceType + DeviceType::DeviceType deviceType; }; - struct extended_pairing_info + struct ExtendedPairingInfo { - uint32_t serial_number; - uint8_t report_types[4]; - uint8_t power_switch_location; ///TODO: Create enum + uint32_t serialNumber; + uint8_t reportTypes[4]; + uint8_t powerSwitchLocation; ///TODO: Make enum }; - pairing_info getPairingInfo(hidpp::DeviceIndex index); - extended_pairing_info getExtendedPairingInfo(hidpp::DeviceIndex index); + struct PairingInfo getPairingInfo(hidpp::DeviceIndex index); + struct ExtendedPairingInfo getExtendedPairingInfo(hidpp::DeviceIndex + index); std::string getDeviceName(hidpp::DeviceIndex index); - hidpp::DeviceIndex deviceConnectionEvent(hidpp::Report& report); - hidpp::DeviceIndex deviceDisconnectionEvent(hidpp::Report& report); + 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( + hidpp::Report& report); void handleDjEvent(dj::Report& report); void handleHidppEvent(hidpp::Report& report); void listen(); + void stopListening(); private: void sendDjRequest(hidpp::DeviceIndex index, uint8_t function, const std::vector&& params); diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp index e69de29..f7302b2 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/dj/ReceiverMonitor.cpp @@ -0,0 +1,30 @@ +#include "ReceiverMonitor.h" + +#include + +using namespace logid::backend::dj; + +ReceiverMonitor::ReceiverMonitor(std::string path) : _reciever (std::move(path)) +{ + Receiver::notification_flags notification_flags{ + true, + true, + true}; + _reciever.enableHidppNotifications(notification_flags); +} + +void ReceiverMonitor::run() +{ + _reciever.listen(); + enumerate(); +} + +void ReceiverMonitor::stop() +{ + _reciever.stopListening(); +} + +void ReceiverMonitor::enumerate() +{ + _reciever.enumerate(); +} \ No newline at end of file diff --git a/src/logid/backend/dj/ReceiverMonitor.h b/src/logid/backend/dj/ReceiverMonitor.h index f717da0..5625294 100644 --- a/src/logid/backend/dj/ReceiverMonitor.h +++ b/src/logid/backend/dj/ReceiverMonitor.h @@ -1,6 +1,38 @@ #ifndef LOGID_BACKEND_DJ_RECEIVERMONITOR_H #define LOGID_BACKEND_DJ_RECEIVERMONITOR_H +#include +#include +#include "Receiver.h" +#include "../hidpp/defs.h" +namespace logid { +namespace backend { +namespace dj +{ + // This class will run on the RawDevice thread, + class ReceiverMonitor + { + public: + ReceiverMonitor(std::string path); + + void enumerate(); + void run(); + void stop(); + + protected: + virtual void addDevice(hidpp::DeviceIndex index, uint16_t pid) = 0; + virtual void removeDevice(hidpp::DeviceIndex index) = 0; + + // Internal methods for derived class + void _pair(uint8_t timeout = 0); + void _stopPairing(); + + void _unpair(); + private: + Receiver _reciever; + }; + +}}} #endif //LOGID_BACKEND_DJ_RECEIVERMONITOR_H \ No newline at end of file diff --git a/src/logid/backend/dj/defs.h b/src/logid/backend/dj/defs.h index 5c0124e..0040432 100644 --- a/src/logid/backend/dj/defs.h +++ b/src/logid/backend/dj/defs.h @@ -16,6 +16,21 @@ namespace dj }; } + namespace DeviceType + { + enum DeviceType : uint8_t + { + Unknown = 0x00, + Keyboard = 0x01, + Mouse = 0x02, + Numpad = 0x03, + Presenter = 0x04, + /* 0x05-0x07 is reserved */ + Trackball = 0x08, + Touchpad = 0x09 + }; + } + static constexpr uint8_t ErrorFeature = 0x7f; static constexpr std::size_t HeaderLength = 3; From b05e525bbc8da67e0d35e822928600dc72809599 Mon Sep 17 00:00:00 2001 From: pixl Date: Sat, 20 Jun 2020 03:25:52 -0400 Subject: [PATCH 21/81] Add RawReport log level --- src/logid/backend/raw/RawDevice.cpp | 15 +++++++++++++++ src/logid/util.cpp | 2 ++ src/logid/util.h | 9 +++++---- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index ca76cc9..ef553cb 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -2,6 +2,7 @@ #include "../Error.h" #include "../hidpp/defs.h" #include "../dj/defs.h" +#include "../../util.h" #include #include @@ -193,6 +194,13 @@ int RawDevice::_sendReport(const std::vector& report) 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) + printf("%02x ", i); + printf("\n"); + } + return ret; } @@ -238,6 +246,13 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng if(0 == ret) throw backend::TimeoutError(); + if(logid::global_verbosity == LogLevel::RAWREPORT) { + printf("[RAWREPORT] %s IN: ", path.c_str()); + for(auto &i : report) + printf("%02x ", i); + printf("\n"); + } + return ret; } diff --git a/src/logid/util.cpp b/src/logid/util.cpp index 7b0218d..607b337 100644 --- a/src/logid/util.cpp +++ b/src/logid/util.cpp @@ -27,6 +27,7 @@ void logid::log_printf(LogLevel level, const char* format, ...) const char* logid::level_prefix(LogLevel level) { + if(level == RAWREPORT) return "RAWREPORT"; if(level == DEBUG) return "DEBUG"; if(level == INFO) return "INFO" ; if(level == WARN) return "WARN"; @@ -120,6 +121,7 @@ LogLevel logid::stringToLogLevel(std::string s) std::string original_str = s; std::transform(s.begin(), s.end(), s.begin(), ::tolower); + if(s == "rawreport") return RAWREPORT; if(s == "debug") return DEBUG; if(s == "info") return INFO; if(s == "warn" || s == "warning") return WARN; diff --git a/src/logid/util.h b/src/logid/util.h index 1478fed..de7690b 100644 --- a/src/logid/util.h +++ b/src/logid/util.h @@ -7,10 +7,11 @@ namespace logid { enum LogLevel { - DEBUG, - INFO, - WARN, - ERROR + RAWREPORT, + DEBUG, + INFO, + WARN, + ERROR }; extern LogLevel global_verbosity; From e40da5f0c09846d164617e3d05e47c1405b0f0c6 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 21 Jun 2020 05:33:33 -0400 Subject: [PATCH 22/81] 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 */ From 261d82390bbab1f706625b5306d4add563c26682 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 21 Jun 2020 15:42:44 -0400 Subject: [PATCH 23/81] Assert report ID and length before sending --- src/logid/backend/hidpp/Report.h | 3 ++- src/logid/backend/raw/RawDevice.cpp | 23 +++++++++++++++++++---- src/logid/backend/raw/RawDevice.h | 2 +- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index 82292c5..c3ab1d6 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -100,8 +100,9 @@ namespace logid::backend::hidpp bool isError20(hidpp20_error* error); std::vector rawReport () const { return _data; } - private: + static constexpr std::size_t HeaderLength = 4; + private: std::vector _data; }; } diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 00582d2..be5ece5 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -3,6 +3,7 @@ #include "../hidpp/defs.h" #include "../dj/defs.h" #include "../../util.h" +#include "../hidpp/Report.h" #include #include @@ -22,8 +23,24 @@ using namespace logid::backend::raw; using namespace logid::backend; using namespace std::chrono; -bool RawDevice::supportedReportID(uint8_t id) +bool RawDevice::supportedReport(uint8_t id, uint8_t length) { + switch(id) + { + case hidpp::ReportType::Short: + return length == (hidpp::ShortParamLength + + hidpp::Report::HeaderLength); + case hidpp::ReportType::Long: + return length == (hidpp::LongParamLength + + hidpp::Report::HeaderLength); + case dj::ReportType::Short: + return length == (dj::ShortParamLength + dj::HeaderLength); + case dj::ReportType::Long: + return length == (dj::LongParamLength + dj::HeaderLength); + default: + return false; + } + return (hidpp::ReportType::Short == id) || (hidpp::ReportType::Long == id) || (dj::ReportType::Short == id) || (dj::ReportType::Long == id); } @@ -116,8 +133,6 @@ std::vector RawDevice::sendReport(const std::vector& report) // DJ commands are not systematically acknowledged, do not expect a result. void RawDevice::sendReportNoResponse(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) @@ -200,7 +215,7 @@ int RawDevice::_sendReport(const std::vector& report) printf("\n"); } - assert(supportedReportID(report[0])); + assert(supportedReport(report[0], report.size())); std::lock_guard lock(dev_io); int ret = ::write(fd, report.data(), report.size()); diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index e03b800..4cb99c1 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -21,7 +21,7 @@ namespace raw class RawDevice { public: - static bool supportedReportID(uint8_t id); + static bool supportedReport(uint8_t id, uint8_t length); explicit RawDevice(std::string path); ~RawDevice(); From bd080e7ef6b7f54c14a2a9261d00ee63aa8a2786 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 21 Jun 2020 20:11:45 -0400 Subject: [PATCH 24/81] Fix bug described in 698971a Feels like a hacky workaround, may come back to this later. --- src/logid/backend/raw/RawDevice.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index be5ece5..917745e 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -208,6 +208,7 @@ std::vector RawDevice::_respondToReport int RawDevice::_sendReport(const std::vector& report) { + std::lock_guard lock(dev_io); if(logid::global_verbosity == LogLevel::RAWREPORT) { printf("[RAWREPORT] %s OUT: ", path.c_str()); for(auto &i : report) @@ -217,11 +218,15 @@ int RawDevice::_sendReport(const std::vector& report) assert(supportedReport(report[0], report.size())); - 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(ret == -1) { + ///TODO: This seems like a hacky solution + // Try again before failing + ret = ::write(fd, report.data(), report.size()); + if(ret == -1) + throw std::system_error(errno, std::system_category(), + "_sendReport write failed"); + } return ret; } From dd75df8c18c46361d85af3a1a8b862f132897bb4 Mon Sep 17 00:00:00 2001 From: pixl Date: Tue, 23 Jun 2020 22:35:37 -0400 Subject: [PATCH 25/81] Use consistent code style Only files that are currently used in logid are changed. --- src/logid/Device.cpp | 2 +- src/logid/DeviceManager.cpp | 8 +- src/logid/DeviceManager.h | 6 +- src/logid/backend/Error.cpp | 2 +- src/logid/backend/Error.h | 2 +- src/logid/backend/dj/Error.cpp | 21 +- src/logid/backend/dj/Error.h | 10 +- src/logid/backend/dj/Receiver.cpp | 107 +++++---- src/logid/backend/dj/Receiver.h | 20 +- src/logid/backend/dj/ReceiverMonitor.cpp | 13 +- src/logid/backend/dj/Report.cpp | 41 ++-- src/logid/backend/dj/Report.h | 6 +- src/logid/backend/hidpp/Device.cpp | 82 ++++--- src/logid/backend/hidpp/Device.h | 19 +- src/logid/backend/hidpp/Error.cpp | 0 src/logid/backend/hidpp/Error.h | 0 src/logid/backend/hidpp/Report.cpp | 103 ++++---- src/logid/backend/hidpp/Report.h | 22 +- src/logid/backend/hidpp/defs.h | 6 +- src/logid/backend/hidpp10/Device.cpp | 5 +- src/logid/backend/hidpp10/Error.cpp | 59 +++-- src/logid/backend/hidpp10/Error.h | 4 +- src/logid/backend/hidpp20/Error.cpp | 51 ++-- src/logid/backend/hidpp20/Error.h | 4 +- src/logid/backend/hidpp20/Feature.h | 2 +- .../backend/hidpp20/features/DeviceName.h | 4 +- src/logid/backend/hidpp20/features/Root.cpp | 3 +- src/logid/backend/hidpp20/features/Root.h | 6 +- src/logid/backend/raw/DeviceMonitor.cpp | 61 ++--- src/logid/backend/raw/DeviceMonitor.h | 14 +- src/logid/backend/raw/RawDevice.cpp | 222 +++++++++--------- src/logid/backend/raw/RawDevice.h | 39 +-- src/logid/backend/{ => raw}/defs.h | 14 +- src/logid/logid.cpp | 139 +++++------ 34 files changed, 560 insertions(+), 537 deletions(-) delete mode 100644 src/logid/backend/hidpp/Error.cpp delete mode 100644 src/logid/backend/hidpp/Error.h rename src/logid/backend/{ => raw}/defs.h (50%) diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 3550c09..3041542 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -5,7 +5,7 @@ using namespace logid; using namespace logid::backend; Device::Device(std::string path, backend::hidpp::DeviceIndex index) : - _hidpp20 (path, index), _path (path), _index (index) + _hidpp20 (path, index), _path (std::move(path)), _index (index) { log_printf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index); } diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index 1ee5854..8c6a35d 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -44,14 +44,16 @@ void DeviceManager::addDevice(std::string path) _devices.emplace(path, device); } else { try { - auto device = std::make_shared(path, hidpp::CordedDevice); + auto device = std::make_shared(path, + hidpp::CordedDevice); _devices.emplace(path, device); } 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()); + 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/DeviceManager.h b/src/logid/DeviceManager.h index e4134bc..57e9012 100644 --- a/src/logid/DeviceManager.h +++ b/src/logid/DeviceManager.h @@ -1,5 +1,5 @@ -#ifndef LOGID_DEVICEMONITOR_H -#define LOGID_DEVICEMONITOR_H +#ifndef LOGID_DEVICEMANAGER_H +#define LOGID_DEVICEMANAGER_H #include #include @@ -29,4 +29,4 @@ namespace logid extern DeviceManager* finder; } -#endif //LOGID_DEVICEFINDER_H \ No newline at end of file +#endif //LOGID_DEVICEMANAGER_H \ No newline at end of file diff --git a/src/logid/backend/Error.cpp b/src/logid/backend/Error.cpp index c224f76..f7f8d0b 100644 --- a/src/logid/backend/Error.cpp +++ b/src/logid/backend/Error.cpp @@ -1,6 +1,6 @@ #include "Error.h" -const char *logid::backend::TimeoutError::what() noexcept +const char *logid::backend::TimeoutError::what() const noexcept { return "Device timed out"; } diff --git a/src/logid/backend/Error.h b/src/logid/backend/Error.h index 20865cf..3cc515f 100644 --- a/src/logid/backend/Error.h +++ b/src/logid/backend/Error.h @@ -9,7 +9,7 @@ class TimeoutError: public std::exception { public: TimeoutError() = default; - virtual const char* what() noexcept; + const char* what() const noexcept override; }; }} diff --git a/src/logid/backend/dj/Error.cpp b/src/logid/backend/dj/Error.cpp index d3a4dbc..8a7f73f 100644 --- a/src/logid/backend/dj/Error.cpp +++ b/src/logid/backend/dj/Error.cpp @@ -8,14 +8,17 @@ Error::Error(uint8_t code) : _code (code) const char* Error::what() const noexcept { - switch(_code) - { - case Unknown: - return "Unknown"; - case KeepAliveTimeout: - return "Keep-alive timeout"; - default: - return std::string("Reserved error code " + std::to_string(_code)) - .c_str(); + switch(_code) { + case Unknown: + return "Unknown"; + case KeepAliveTimeout: + return "Keep-alive timeout"; + default: + return "Reserved"; } +} + +uint8_t Error::code() const noexcept +{ + return _code; } \ No newline at end of file diff --git a/src/logid/backend/dj/Error.h b/src/logid/backend/dj/Error.h index f462377..6c48f17 100644 --- a/src/logid/backend/dj/Error.h +++ b/src/logid/backend/dj/Error.h @@ -1,5 +1,5 @@ -#ifndef LOGID_HIDPP_BACKEND_DJ_ERROR_H -#define LOGID_HIDPP_BACKEND_DJ_ERROR_H +#ifndef LOGID_BACKEND_DJ_ERROR_H +#define LOGID_BACKEND_DJ_ERROR_H #include #include @@ -17,9 +17,9 @@ namespace dj KeepAliveTimeout = 0x01 }; - Error(uint8_t code); + explicit Error(uint8_t code); - virtual const char* what() const noexcept; + const char* what() const noexcept override; uint8_t code() const noexcept; private: @@ -27,4 +27,4 @@ namespace dj }; }}} -#endif //LOGID_HIDPP_BACKEND_DJ_ERROR_H +#endif //LOGID_BACKEND_DJ_ERROR_H \ No newline at end of file diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp index 847fe53..14bb4dd 100644 --- a/src/logid/backend/dj/Receiver.cpp +++ b/src/logid/backend/dj/Receiver.cpp @@ -12,12 +12,11 @@ InvalidReceiver::InvalidReceiver(Reason reason) : _reason (reason) const char* InvalidReceiver::what() const noexcept { - switch(_reason) - { - case NoDJReports: - return "No DJ reports"; - default: - return "Invalid receiver"; + switch(_reason) { + case NoDJReports: + return "No DJ reports"; + default: + return "Invalid receiver"; } } @@ -27,10 +26,10 @@ InvalidReceiver::Reason InvalidReceiver::code() const noexcept } Receiver::Receiver(std::string path) : - raw_device (std::make_shared(path)), - _hidpp10_device (raw_device, hidpp::DefaultDevice) + _raw_device (std::make_shared(std::move(path))), + _hidpp10_device (_raw_device, hidpp::DefaultDevice) { - if(!supportsDjReports(raw_device->reportDescriptor())) + if(!supportsDjReports(_raw_device->reportDescriptor())) throw InvalidReceiver(InvalidReceiver::NoDJReports); } @@ -39,28 +38,28 @@ void Receiver::enumerateDj() sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{}); } -Receiver::notification_flags Receiver::getHidppNotifications() +Receiver::NotificationFlags Receiver::getHidppNotifications() { auto response = _hidpp10_device.getRegister(EnableHidppNotifications, {}, hidpp::ReportType::Short); - notification_flags flags{}; - flags.device_battery_status = response[0] & (1 << 4); - flags.receiver_wireless_notifications = response[1] & (1 << 0); - flags.receiver_software_present = response[1] & (1 << 3); + NotificationFlags flags{}; + flags.deviceBatteryStatus = response[0] & (1 << 4); + flags.receiverWirelessNotifications = response[1] & (1 << 0); + flags.receiverSoftwarePresent = response[1] & (1 << 3); return flags; } -void Receiver::enableHidppNotifications(notification_flags flags) +void Receiver::enableHidppNotifications(NotificationFlags flags) { std::vector request(3); - if(flags.device_battery_status) + if(flags.deviceBatteryStatus) request[0] |= (1 << 4); - if(flags.receiver_wireless_notifications) + if(flags.receiverWirelessNotifications) request[1] |= 1; - if(flags.receiver_software_present) + if(flags.receiverSoftwarePresent) request[1] |= (1 << 3); _hidpp10_device.setRegister(EnableHidppNotifications, request, @@ -228,14 +227,14 @@ hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent( void Receiver::handleDjEvent(Report& report) { - for(auto& handler : dj_event_handlers) + for(auto& handler : _dj_event_handlers) if(handler.second->condition(report)) handler.second->callback(report); } void Receiver::handleHidppEvent(hidpp::Report &report) { - for(auto& handler : hidpp_event_handlers) + for(auto& handler : _hidpp_event_handlers) if(handler.second->condition(report)) handler.second->callback(report); } @@ -243,39 +242,39 @@ void Receiver::handleHidppEvent(hidpp::Report &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); + 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); + _dj_event_handlers.erase(nickname); } const std::map>& Receiver::djEventHandlers() { - return dj_event_handlers; + 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); + 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); + _hidpp_event_handlers.erase(nickname); } const std::map>& Receiver::hidppEventHandlers() { - return hidpp_event_handlers; + return _hidpp_event_handlers; } void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function, @@ -290,61 +289,61 @@ void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function, std::copy(params.begin(), params.end(), request.paramBegin()); - raw_device->sendReportNoResponse(request.rawData()); + _raw_device->sendReportNoResponse(request.rawData()); } void Receiver::listen() { - if(!raw_device->isListening()) - 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()) { + 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 + std::shared_ptr hidpp_handler = + std::make_shared(); + hidpp_handler->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_handler->callback = [this](std::vector& report) + ->void { hidpp::Report _report(report); this->handleHidppEvent(_report); }; - raw_device->addEventHandler("RECV_HIDPP", hidppRawEventHandler); + _raw_device->addEventHandler("RECV_HIDPP", hidpp_handler); } - 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 + if(_raw_device->eventHandlers().find("RECV_DJ") == + _raw_device->eventHandlers().end()) { + // Pass all DJ events with device index to handleDjEvent + std::shared_ptr dj_handler = + std::make_shared(); + dj_handler->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 + dj_handler->callback = [this](std::vector& report)->void { Report _report(report); this->handleDjEvent(_report); }; - raw_device->addEventHandler("RECV_DJ", djRawEventHandler); + _raw_device->addEventHandler("RECV_DJ", dj_handler); } } void Receiver::stopListening() { - raw_device->removeEventHandler("RECV_HIDPP"); - raw_device->removeEventHandler("RECV_DJ"); + _raw_device->removeEventHandler("RECV_HIDPP"); + _raw_device->removeEventHandler("RECV_DJ"); - if(raw_device->eventHandlers().empty()) - raw_device->stopListener(); + if(_raw_device->eventHandlers().empty()) + _raw_device->stopListener(); } std::shared_ptr Receiver::rawDevice() const { - return raw_device; + 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 0214e17..40c543e 100644 --- a/src/logid/backend/dj/Receiver.h +++ b/src/logid/backend/dj/Receiver.h @@ -25,7 +25,7 @@ namespace dj NoDJReports }; explicit InvalidReceiver(Reason reason); - virtual const char* what() const noexcept; + const char* what() const noexcept override; Reason code() const noexcept; private: Reason _reason; @@ -73,15 +73,15 @@ namespace dj PairingInfo = 0xb5 }; - struct notification_flags + struct NotificationFlags { - bool device_battery_status; - bool receiver_wireless_notifications; - bool receiver_software_present; + bool deviceBatteryStatus; + bool receiverWirelessNotifications; + bool receiverSoftwarePresent; }; - notification_flags getHidppNotifications(); - void enableHidppNotifications(notification_flags flags); + NotificationFlags getHidppNotifications(); + void enableHidppNotifications(NotificationFlags flags); void enumerateHidpp(); uint8_t getConnectionState(hidpp::DeviceIndex index); @@ -142,11 +142,11 @@ namespace dj void handleHidppEvent(hidpp::Report& report); std::map> - dj_event_handlers; + _dj_event_handlers; std::map> - hidpp_event_handlers; + _hidpp_event_handlers; - std::shared_ptr raw_device; + std::shared_ptr _raw_device; hidpp10::Device _hidpp10_device; }; } diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp index bdc8366..8d66397 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/dj/ReceiverMonitor.cpp @@ -13,7 +13,7 @@ ReceiverMonitor::ReceiverMonitor(std::string path) : _receiver ( assert(_receiver->djEventHandlers().find("RECVMON") == _receiver->djEventHandlers().end()); - Receiver::notification_flags notification_flags{ + Receiver::NotificationFlags notification_flags{ true, true, true}; @@ -25,16 +25,15 @@ void ReceiverMonitor::run() _receiver->listen(); if(_receiver->hidppEventHandlers().find("RECVMON") == - _receiver->hidppEventHandlers().end()) - { - std::shared_ptr eventHandler = + _receiver->hidppEventHandlers().end()) { + std::shared_ptr event_handler = std::make_shared(); - eventHandler->condition = [](hidpp::Report &report) -> bool { + event_handler->condition = [](hidpp::Report &report) -> bool { return (report.subId() == Receiver::DeviceConnection || report.subId() == Receiver::DeviceDisconnection); }; - eventHandler->callback = [this](hidpp::Report &report) -> void { + event_handler->callback = [this](hidpp::Report &report) -> void { /* Running in a new thread prevents deadlocks since the * receiver may be enumerating. */ @@ -48,7 +47,7 @@ void ReceiverMonitor::run() }, report}.detach(); }; - _receiver->addHidppEventHandler("RECVMON", eventHandler); + _receiver->addHidppEventHandler("RECVMON", event_handler); } enumerate(); diff --git a/src/logid/backend/dj/Report.cpp b/src/logid/backend/dj/Report.cpp index 0dff2fd..5f7d596 100644 --- a/src/logid/backend/dj/Report.cpp +++ b/src/logid/backend/dj/Report.cpp @@ -28,37 +28,36 @@ static const std::array DJReportDesc = { bool dj::supportsDjReports(std::vector&& rdesc) { - auto it = std::search(rdesc.begin(), rdesc.end(), DJReportDesc.begin(), DJReportDesc.end()); + auto it = std::search(rdesc.begin(), rdesc.end(), + DJReportDesc.begin(), DJReportDesc.end()); return it != rdesc.end(); } Report::Report(std::vector& data) : _data (data) { - switch(data[Offset::Type]) - { - case ReportType::Short: - _data.resize(HeaderLength+ShortParamLength); - break; - case ReportType::Long: - _data.resize(HeaderLength+LongParamLength); - break; - default: - assert(false); + switch(data[Offset::Type]) { + case ReportType::Short: + _data.resize(HeaderLength+ShortParamLength); + break; + case ReportType::Long: + _data.resize(HeaderLength+LongParamLength); + break; + default: + assert(false); } } Report::Report(Report::Type type, hidpp::DeviceIndex index, uint8_t feature) { - switch(type) - { - case ReportType::Short: - _data.resize(HeaderLength+ShortParamLength); - break; - case ReportType::Long: - _data.resize(HeaderLength+LongParamLength); - break; - default: - assert(false); + switch(type) { + case ReportType::Short: + _data.resize(HeaderLength+ShortParamLength); + break; + case ReportType::Long: + _data.resize(HeaderLength+LongParamLength); + break; + default: + assert(false); } _data[Offset::Type] = type; diff --git a/src/logid/backend/dj/Report.h b/src/logid/backend/dj/Report.h index 6f01bb7..36482fc 100644 --- a/src/logid/backend/dj/Report.h +++ b/src/logid/backend/dj/Report.h @@ -6,7 +6,9 @@ #include "defs.h" #include "../hidpp/defs.h" -namespace logid::backend::dj +namespace logid { +namespace backend { +namespace dj { namespace Offset { @@ -33,6 +35,6 @@ namespace logid::backend::dj private: std::vector _data; }; -} +}}} #endif //LOGID_BACKEND_DJ_REPORT_H diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index ea2d196..4c0d4cc 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -13,16 +13,15 @@ using namespace logid::backend::hidpp; const char* Device::InvalidDevice::what() const noexcept { - switch(_reason) - { - case NoHIDPPReport: - return "Invalid HID++ device"; - case InvalidRawDevice: - return "Invalid raw device"; - case Asleep: - return "Device asleep"; - default: - return "Invalid device"; + switch(_reason) { + case NoHIDPPReport: + return "Invalid HID++ device"; + case InvalidRawDevice: + return "Invalid raw device"; + case Asleep: + return "Device asleep"; + default: + return "Invalid device"; } } @@ -58,10 +57,25 @@ Device::Device(std::shared_ptr receiver, _init(); } +std::string Device::devicePath() const +{ + return _path; +} + +DeviceIndex Device::deviceIndex() const +{ + return _index; +} + +std::tuple Device::version() const +{ + return _version; +} + void Device::_init() { - supported_reports = getSupportedReports(_raw_device->reportDescriptor()); - if(!supported_reports) + _supported_reports = getSupportedReports(_raw_device->reportDescriptor()); + if(!_supported_reports) throw InvalidDevice(InvalidDevice::NoHIDPPReport); try { @@ -101,20 +115,20 @@ Device::~Device() void Device::addEventHandler(const std::string& nickname, const std::shared_ptr& handler) { - auto it = event_handlers.find(nickname); - assert(it == event_handlers.end()); + auto it = _event_handlers.find(nickname); + assert(it == _event_handlers.end()); - event_handlers.emplace(nickname, handler); + _event_handlers.emplace(nickname, handler); } void Device::removeEventHandler(const std::string& nickname) { - event_handlers.erase(nickname); + _event_handlers.erase(nickname); } void Device::handleEvent(Report& report) { - for(auto& handler : event_handlers) + for(auto& handler : _event_handlers) if(handler.second->condition(report)) handler.second->callback(report); } @@ -123,26 +137,26 @@ Report Device::sendReport(Report& report) { switch(report.type()) { - case Report::Type::Short: - if(!(supported_reports & HIDPP_REPORT_SHORT_SUPPORTED)) - report.setType(Report::Type::Long); - break; - case Report::Type::Long: - /* Report can be truncated, but that isn't a good idea. */ - assert(supported_reports & HIDPP_REPORT_LONG_SUPPORTED); + case Report::Type::Short: + if(!(_supported_reports & HIDPP_REPORT_SHORT_SUPPORTED)) + report.setType(Report::Type::Long); + break; + case Report::Type::Long: + /* Report can be truncated, but that isn't a good idea. */ + assert(_supported_reports & HIDPP_REPORT_LONG_SUPPORTED); } auto raw_response = _raw_device->sendReport(report.rawReport()); Report response(raw_response); - Report::hidpp10_error hidpp10Error{}; - if(response.isError10(&hidpp10Error)) - throw hidpp10::Error(hidpp10Error.error_code); + Report::Hidpp10Error hidpp10_error{}; + if(response.isError10(&hidpp10_error)) + throw hidpp10::Error(hidpp10_error.error_code); - Report::hidpp20_error hidpp20Error{}; - if(response.isError20(&hidpp20Error)) - throw hidpp10::Error(hidpp20Error.error_code); + Report::Hidpp20Error hidpp20_error{}; + if(response.isError20(&hidpp20_error)) + throw hidpp10::Error(hidpp20_error.error_code); return response; } @@ -153,20 +167,20 @@ void Device::listen() 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 + std::shared_ptr handler; + handler->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 + handler->callback = [this](std::vector& report)->void { Report _report(report); this->handleEvent(_report); }; - _raw_device->addEventHandler("DEV_" + std::to_string(_index), rawEventHandler); + _raw_device->addEventHandler("DEV_" + std::to_string(_index), handler); } void Device::stopListening() diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 460e65b..1372cfc 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -1,5 +1,5 @@ -#ifndef LOGID_HIDPP_DEVICE_H -#define LOGID_HIDPP_DEVICE_H +#ifndef LOGID_BACKEND_HIDPP_DEVICE_H +#define LOGID_BACKEND_HIDPP_DEVICE_H #include #include @@ -44,14 +44,15 @@ namespace hidpp }; explicit Device(const std::string& path, DeviceIndex index); - explicit Device(std::shared_ptr raw_device, 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; } - DeviceIndex deviceIndex() const { return _index; } - std::tuple version() const { return _version; } + std::string devicePath() const; + DeviceIndex deviceIndex() const; + std::tuple version() const; void listen(); // Runs asynchronously void stopListening(); @@ -70,14 +71,14 @@ namespace hidpp std::shared_ptr _receiver; std::string _path; DeviceIndex _index; - uint8_t supported_reports; + uint8_t _supported_reports; std::tuple _version; uint16_t _pid; std::string _name; - std::map> event_handlers; + std::map> _event_handlers; }; } } } -#endif //LOGID_HIDPP_DEVICE_H \ No newline at end of file +#endif //LOGID_BACKEND_HIDPP_DEVICE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp/Error.cpp b/src/logid/backend/hidpp/Error.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/logid/backend/hidpp/Error.h b/src/logid/backend/hidpp/Error.h deleted file mode 100644 index e69de29..0000000 diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index b928bc4..a8c0429 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -70,15 +70,19 @@ uint8_t hidpp::getSupportedReports(std::vector&& rdesc) { uint8_t ret = 0; - auto it = std::search(rdesc.begin(), rdesc.end(), ShortReportDesc.begin(), ShortReportDesc.end()); + auto it = std::search(rdesc.begin(), rdesc.end(), + ShortReportDesc.begin(), ShortReportDesc.end()); if(it == rdesc.end()) - it = std::search(rdesc.begin(), rdesc.end(), ShortReportDesc2.begin(), ShortReportDesc2.end()); + it = std::search(rdesc.begin(), rdesc.end(), + ShortReportDesc2.begin(), ShortReportDesc2.end()); if(it != rdesc.end()) ret |= HIDPP_REPORT_SHORT_SUPPORTED; - it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc.begin(), LongReportDesc.end()); + it = std::search(rdesc.begin(), rdesc.end(), + LongReportDesc.begin(), LongReportDesc.end()); if(it == rdesc.end()) - it = std::search(rdesc.begin(), rdesc.end(), LongReportDesc2.begin(), LongReportDesc2.end()); + it = std::search(rdesc.begin(), rdesc.end(), + LongReportDesc2.begin(), LongReportDesc2.end()); if(it != rdesc.end()) ret |= HIDPP_REPORT_LONG_SUPPORTED; @@ -98,16 +102,15 @@ const char *Report::InvalidReportLength::what() const noexcept Report::Report(Report::Type type, DeviceIndex device_index, uint8_t sub_id, uint8_t address) { - switch(type) - { - case Type::Short: - _data.resize(HeaderLength + ShortParamLength); - break; - case Type::Long: - _data.resize(HeaderLength + LongParamLength); - break; - default: - throw InvalidReportID(); + switch(type) { + case Type::Short: + _data.resize(HeaderLength + ShortParamLength); + break; + case Type::Long: + _data.resize(HeaderLength + LongParamLength); + break; + default: + throw InvalidReportID(); } _data[Offset::Type] = type; @@ -119,25 +122,25 @@ Report::Report(Report::Type type, DeviceIndex device_index, Report::Report(Report::Type type, DeviceIndex device_index, uint8_t feature_index, uint8_t function, uint8_t sw_id) { - assert(function <= functionMask); - assert(sw_id <= swIdMask); + assert(function <= 0x0f); + assert(sw_id <= 0x0f); - switch(type) - { - case Type::Short: - _data.resize(HeaderLength + ShortParamLength); - break; - case Type::Long: - _data.resize(HeaderLength + LongParamLength); - break; - default: - throw InvalidReportID(); + switch(type) { + case Type::Short: + _data.resize(HeaderLength + ShortParamLength); + break; + case Type::Long: + _data.resize(HeaderLength + LongParamLength); + break; + default: + throw InvalidReportID(); } _data[Offset::Type] = type; _data[Offset::DeviceIndex] = device_index; _data[Offset::Feature] = feature_index; - _data[Offset::Function] = (function & functionMask) << 4 | (sw_id & swIdMask); + _data[Offset::Function] = (function & 0x0f) << 4 | + (sw_id & 0x0f); } Report::Report(const std::vector& data) @@ -145,16 +148,15 @@ Report::Report(const std::vector& data) _data = data; // Truncating data is entirely valid here. - switch(_data[Offset::Type]) - { - case Type::Short: - _data.resize(HeaderLength + ShortParamLength); - break; - case Type::Long: - _data.resize(HeaderLength + LongParamLength); - break; - default: - throw InvalidReportID(); + switch(_data[Offset::Type]) { + case Type::Short: + _data.resize(HeaderLength + ShortParamLength); + break; + case Type::Long: + _data.resize(HeaderLength + LongParamLength); + break; + default: + throw InvalidReportID(); } } @@ -165,16 +167,15 @@ Report::Type Report::type() const void Report::setType(Report::Type type) { - switch(type) - { - case Type::Short: - _data.resize(HeaderLength + ShortParamLength); - break; - case Type::Long: - _data.resize(HeaderLength + LongParamLength); - break; - default: - throw InvalidReportID(); + switch(type) { + case Type::Short: + _data.resize(HeaderLength + ShortParamLength); + break; + case Type::Long: + _data.resize(HeaderLength + LongParamLength); + break; + default: + throw InvalidReportID(); } _data[Offset::Type] = type; @@ -218,7 +219,7 @@ uint8_t Report::function() const void Report::setFunction(uint8_t function) { _data[Offset::Function] &= 0x0f; - _data[Offset::Function] |= (function << 4) & 0x0f; + _data[Offset::Function] |= (function & 0x0f) << 4; } uint8_t Report::swId() const @@ -250,7 +251,7 @@ void Report::setParams(const std::vector& _params) _data[Offset::Parameters + i] = _params[i]; } -bool Report::isError10(Report::hidpp10_error *error) +bool Report::isError10(Report::Hidpp10Error *error) { assert(error != nullptr); @@ -265,8 +266,10 @@ bool Report::isError10(Report::hidpp10_error *error) return true; } -bool Report::isError20(Report::hidpp20_error* error) +bool Report::isError20(Report::Hidpp20Error* error) { + assert(error != nullptr); + if(_data[Offset::Type] != Type::Long || _data[Offset::Feature] != hidpp20::ErrorID) return false; diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index c3ab1d6..c3b6ecd 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -10,7 +10,9 @@ #define HIDPP_REPORT_LONG_SUPPORTED 1U<<1U /* Very long reports exist, however they have not been encountered so far */ -namespace logid::backend::hidpp +namespace logid { +namespace backend { +namespace hidpp { uint8_t getSupportedReports(std::vector&& rdesc); @@ -34,19 +36,17 @@ namespace logid::backend::hidpp { public: InvalidReportID() = default; - virtual const char* what() const noexcept; + const char* what() const noexcept override; }; class InvalidReportLength: public std::exception { public: InvalidReportLength() = default;; - virtual const char* what() const noexcept; + const char* what() const noexcept override; }; static constexpr std::size_t MaxDataLength = 20; - static constexpr uint8_t swIdMask = 0x0f; - static constexpr uint8_t functionMask = 0x0f; Report(Report::Type type, DeviceIndex device_index, uint8_t sub_id, @@ -85,19 +85,17 @@ namespace logid::backend::hidpp std::vector::iterator paramEnd() { return _data.end(); } void setParams(const std::vector& _params); - struct hidpp10_error + struct Hidpp10Error { uint8_t sub_id, address, error_code; }; + bool isError10(Hidpp10Error* error); - bool isError10(hidpp10_error* error); - - struct hidpp20_error + struct Hidpp20Error { uint8_t feature_index, function, software_id, error_code; }; - - bool isError20(hidpp20_error* error); + bool isError20(Hidpp20Error* error); std::vector rawReport () const { return _data; } @@ -105,6 +103,6 @@ namespace logid::backend::hidpp private: std::vector _data; }; -} +}}} #endif //LOGID_BACKEND_HIDPP_REPORT_H \ No newline at end of file diff --git a/src/logid/backend/hidpp/defs.h b/src/logid/backend/hidpp/defs.h index 295af39..d08886d 100644 --- a/src/logid/backend/hidpp/defs.h +++ b/src/logid/backend/hidpp/defs.h @@ -3,7 +3,9 @@ #define LOGID_HIDPP_SOFTWARE_ID 0 -namespace logid::backend::hidpp +namespace logid { +namespace backend { +namespace hidpp { namespace ReportType { @@ -28,6 +30,6 @@ namespace logid::backend::hidpp static constexpr std::size_t ShortParamLength = 3; static constexpr std::size_t LongParamLength = 16; -} +} } } #endif //LOGID_HIDPP_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/hidpp10/Device.cpp b/src/logid/backend/hidpp10/Device.cpp index e1f2d15..12531ee 100644 --- a/src/logid/backend/hidpp10/Device.cpp +++ b/src/logid/backend/hidpp10/Device.cpp @@ -41,10 +41,11 @@ 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 ¶ms) +std::vector Device::accessRegister(uint8_t sub_id, uint8_t address, + const std::vector ¶ms) { hidpp::Report::Type type = params.size() <= hidpp::ShortParamLength ? - hidpp::Report::Type::Short : hidpp::Report::Type::Long; + hidpp::Report::Type::Short : hidpp::Report::Type::Long; hidpp::Report request(type, deviceIndex(), sub_id, address); std::copy(params.begin(), params.end(), request.paramBegin()); diff --git a/src/logid/backend/hidpp10/Error.cpp b/src/logid/backend/hidpp10/Error.cpp index 30f6b3f..2206173 100644 --- a/src/logid/backend/hidpp10/Error.cpp +++ b/src/logid/backend/hidpp10/Error.cpp @@ -11,36 +11,35 @@ Error::Error(uint8_t code): _code(code) const char* Error::what() const noexcept { - switch(_code) - { - case Success: - return "Success"; - case InvalidSubID: - return "Invalid sub ID"; - case InvalidAddress: - return "Invalid address"; - case InvalidValue: - return "Invalid value"; - case ConnectFail: - return "Connection failure"; - case TooManyDevices: - return "Too many devices"; - case AlreadyExists: - return "Already exists"; - case Busy: - return "Busy"; - case UnknownDevice: - return "Unknown device"; - case ResourceError: - return "Resource error"; - case RequestUnavailable: - return "Request unavailable"; - case InvalidParameterValue: - return "Invalid parameter value"; - case WrongPINCode: - return "Wrong PIN code"; - default: - return std::string("Unknown error code " + std::to_string(_code)).c_str(); + switch(_code) { + case Success: + return "Success"; + case InvalidSubID: + return "Invalid sub ID"; + case InvalidAddress: + return "Invalid address"; + case InvalidValue: + return "Invalid value"; + case ConnectFail: + return "Connection failure"; + case TooManyDevices: + return "Too many devices"; + case AlreadyExists: + return "Already exists"; + case Busy: + return "Busy"; + case UnknownDevice: + return "Unknown device"; + case ResourceError: + return "Resource error"; + case RequestUnavailable: + return "Request unavailable"; + case InvalidParameterValue: + return "Invalid parameter value"; + case WrongPINCode: + return "Wrong PIN code"; + default: + return "Unknown error code"; } } diff --git a/src/logid/backend/hidpp10/Error.h b/src/logid/backend/hidpp10/Error.h index 71488b0..7021839 100644 --- a/src/logid/backend/hidpp10/Error.h +++ b/src/logid/backend/hidpp10/Error.h @@ -28,9 +28,9 @@ namespace hidpp10 { WrongPINCode = 0x0C }; - Error(uint8_t code); + explicit Error(uint8_t code); - virtual const char* what() const noexcept; + const char* what() const noexcept override; uint8_t code() const noexcept; private: diff --git a/src/logid/backend/hidpp20/Error.cpp b/src/logid/backend/hidpp20/Error.cpp index 33fdfe1..7941c9e 100644 --- a/src/logid/backend/hidpp20/Error.cpp +++ b/src/logid/backend/hidpp20/Error.cpp @@ -11,32 +11,31 @@ Error::Error(uint8_t code) : _code (code) const char* Error::what() const noexcept { - switch(_code) - { - case NoError: - return "No error"; - case Unknown: - return "Unknown"; - case InvalidArgument: - return "Invalid argument"; - case OutOfRange: - return "Out of range"; - case HardwareError: - return "Hardware error"; - case LogitechInternal: - return "Logitech internal feature"; - case InvalidFeatureIndex: - return "Invalid feature index"; - case InvalidFunctionID: - return "Invalid function ID"; - case Busy: - return "Busy"; - case Unsupported: - return "Unsupported"; - case UnknownDevice: - return "Unknown device"; - default: - return std::string("Unknown error code " + std::to_string(_code)).c_str(); + switch(_code) { + case NoError: + return "No error"; + case Unknown: + return "Unknown"; + case InvalidArgument: + return "Invalid argument"; + case OutOfRange: + return "Out of range"; + case HardwareError: + return "Hardware error"; + case LogitechInternal: + return "Logitech internal feature"; + case InvalidFeatureIndex: + return "Invalid feature index"; + case InvalidFunctionID: + return "Invalid function ID"; + case Busy: + return "Busy"; + case Unsupported: + return "Unsupported"; + case UnknownDevice: + return "Unknown device"; + default: + return "Unknown error code"; } } diff --git a/src/logid/backend/hidpp20/Error.h b/src/logid/backend/hidpp20/Error.h index 53fec65..533c4be 100644 --- a/src/logid/backend/hidpp20/Error.h +++ b/src/logid/backend/hidpp20/Error.h @@ -26,9 +26,9 @@ namespace hidpp20 { UnknownDevice = 10 }; - Error(uint8_t code); + explicit Error(uint8_t code); - virtual const char* what() const noexcept; + const char* what() const noexcept override; uint8_t code() const noexcept; private: diff --git a/src/logid/backend/hidpp20/Feature.h b/src/logid/backend/hidpp20/Feature.h index 8a96602..6dba742 100644 --- a/src/logid/backend/hidpp20/Feature.h +++ b/src/logid/backend/hidpp20/Feature.h @@ -11,7 +11,7 @@ namespace hidpp20 { { public: explicit UnsupportedFeature(uint16_t ID) : _f_id (ID) {} - virtual const char* what() const noexcept; + const char* what() const noexcept override; uint16_t code() const noexcept; private: uint16_t _f_id; diff --git a/src/logid/backend/hidpp20/features/DeviceName.h b/src/logid/backend/hidpp20/features/DeviceName.h index 39b7cca..deb6d03 100644 --- a/src/logid/backend/hidpp20/features/DeviceName.h +++ b/src/logid/backend/hidpp20/features/DeviceName.h @@ -21,7 +21,7 @@ namespace hidpp20 GetDeviceName = 1 }; - DeviceName(Device* device); + explicit DeviceName(Device* device); uint8_t getNameLength(); std::string getName(); @@ -33,7 +33,7 @@ namespace hidpp20 static const uint16_t ID = FeatureID::DEVICE_NAME; virtual uint16_t getID() { return ID; } - EssentialDeviceName(hidpp::Device* device); + explicit EssentialDeviceName(hidpp::Device* device); uint8_t getNameLength(); std::string getName(); diff --git a/src/logid/backend/hidpp20/features/Root.cpp b/src/logid/backend/hidpp20/features/Root.cpp index b5a6203..d7462ad 100644 --- a/src/logid/backend/hidpp20/features/Root.cpp +++ b/src/logid/backend/hidpp20/features/Root.cpp @@ -14,7 +14,8 @@ std::vector _genGetFeatureParams(uint16_t feature_id) return params; } -feature_info _genGetFeatureInfo(uint16_t feature_id, std::vector response) +feature_info _genGetFeatureInfo(uint16_t feature_id, + std::vector response) { feature_info info{}; info.feature_id = response[0]; diff --git a/src/logid/backend/hidpp20/features/Root.h b/src/logid/backend/hidpp20/features/Root.h index 9882aa0..295e5bf 100644 --- a/src/logid/backend/hidpp20/features/Root.h +++ b/src/logid/backend/hidpp20/features/Root.h @@ -21,7 +21,7 @@ namespace hidpp20 Ping = 1 }; - Root(Device* device); + explicit Root(Device* device); feature_info getFeature (uint16_t feature_id); std::tuple getVersion(); @@ -40,9 +40,9 @@ namespace hidpp20 static const uint16_t ID = FeatureID::ROOT; virtual uint16_t getID() { return ID; } - EssentialRoot(hidpp::Device* device); + explicit EssentialRoot(hidpp::Device* device); - feature_info getFeature (uint16_t feature_id); + feature_info getFeature(uint16_t feature_id); std::tuple getVersion(); }; }}} diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp index 69eac98..77aa293 100644 --- a/src/logid/backend/raw/DeviceMonitor.cpp +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -13,40 +13,39 @@ using namespace logid::backend::raw; DeviceMonitor::DeviceMonitor() { - if(-1 == pipe(monitor_pipe)) - throw std::system_error(errno, std::system_category(), "pipe creation failed"); + if(-1 == pipe(_pipe)) + throw std::system_error(errno, std::system_category(), + "pipe creation failed"); - udev_context = udev_new(); - if(!udev_context) + _udev_context = udev_new(); + if(!_udev_context) throw std::runtime_error("udev_new failed"); } DeviceMonitor::~DeviceMonitor() { - bool is_running = running.try_lock(); - if(is_running) - running.unlock(); - else - this->stop(); + this->stop(); - udev_unref(udev_context); + udev_unref(_udev_context); - for(int i : monitor_pipe) + for(int i : _pipe) close(i); } void DeviceMonitor::run() { int ret; - const std::lock_guard run_lock(running); + std::lock_guard lock(_running); - struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev_context, "udev"); + struct udev_monitor* monitor = udev_monitor_new_from_netlink(_udev_context, + "udev"); if(!monitor) throw std::runtime_error("udev_monitor_new_from_netlink failed"); - ret = udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw", nullptr); + ret = udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw", + nullptr); if (0 != ret) - throw std::system_error (-ret, std::system_category (), + throw std::system_error (-ret, std::system_category(), "udev_monitor_filter_add_match_subsystem_devtype"); ret = udev_monitor_enable_receiving(monitor); @@ -57,20 +56,27 @@ void DeviceMonitor::run() this->enumerate(); int fd = udev_monitor_get_fd(monitor); - while (true) { + + _run_monitor = true; + while (_run_monitor) { fd_set fds; FD_ZERO(&fds); - FD_SET(monitor_pipe[0], &fds); + FD_SET(_pipe[0], &fds); FD_SET(fd, &fds); - if (-1 == select (std::max (monitor_pipe[0], fd)+1, &fds, nullptr, nullptr, nullptr)) { + + if (-1 == select (std::max (_pipe[0], fd)+1, &fds, nullptr, + nullptr, nullptr)) { if (errno == EINTR) continue; - throw std::system_error (errno, std::system_category(), "udev_monitor select"); + throw std::system_error (errno, std::system_category(), + "udev_monitor select"); } + if (FD_ISSET(fd, &fds)) { struct udev_device *device = udev_monitor_receive_device(monitor); std::string action = udev_device_get_action(device); std::string devnode = udev_device_get_devnode(device); + if (action == "add") std::thread([this](const std::string name) { this->addDevice(name); @@ -79,12 +85,13 @@ void DeviceMonitor::run() std::thread([this](const std::string name) { this->removeDevice(name); }, devnode).detach(); + udev_device_unref (device); } - if (FD_ISSET(monitor_pipe[0], &fds)) { + if (FD_ISSET(_pipe[0], &fds)) { char c; - if (-1 == read(monitor_pipe[0], &c, sizeof (char))) - throw std::system_error (errno, std::system_category (), + if (-1 == read(_pipe[0], &c, sizeof (char))) + throw std::system_error (errno, std::system_category(), "read pipe"); break; } @@ -93,13 +100,14 @@ void DeviceMonitor::run() void DeviceMonitor::stop() { - + _run_monitor = false; + std::lock_guard lock(_running); } void DeviceMonitor::enumerate() { int ret; - struct udev_enumerate* udev_enum = udev_enumerate_new(udev_context); + struct udev_enumerate* udev_enum = udev_enumerate_new(_udev_context); ret = udev_enumerate_add_match_subsystem(udev_enum, "hidraw"); if(0 != ret) throw std::system_error(-ret, std::system_category(), @@ -112,11 +120,10 @@ void DeviceMonitor::enumerate() struct udev_list_entry* udev_enum_entry; udev_list_entry_foreach(udev_enum_entry, - udev_enumerate_get_list_entry(udev_enum)) - { + udev_enumerate_get_list_entry(udev_enum)) { const char* name = udev_list_entry_get_name(udev_enum_entry); - struct udev_device* device = udev_device_new_from_syspath(udev_context, + struct udev_device* device = udev_device_new_from_syspath(_udev_context, name); if(!device) throw std::runtime_error("udev_device_new_from_syspath failed"); diff --git a/src/logid/backend/raw/DeviceMonitor.h b/src/logid/backend/raw/DeviceMonitor.h index af2a0c6..b24ad19 100644 --- a/src/logid/backend/raw/DeviceMonitor.h +++ b/src/logid/backend/raw/DeviceMonitor.h @@ -3,13 +3,16 @@ #include #include +#include extern "C" { #include } -namespace logid::backend::raw +namespace logid { +namespace backend { +namespace raw { class DeviceMonitor { @@ -23,10 +26,11 @@ namespace logid::backend::raw virtual void addDevice(std::string device) = 0; virtual void removeDevice(std::string device) = 0; private: - struct udev* udev_context; - int monitor_pipe[2]; - std::mutex running; + struct udev* _udev_context; + int _pipe[2]; + std::atomic _run_monitor; + std::mutex _running; }; -} +}}} #endif //LOGID_BACKEND_RAW_DEVICEMONITOR_H \ No newline at end of file diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 917745e..bae0742 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -7,12 +7,13 @@ #include #include -#include +#include #define MAX_DATA_LENGTH 32 extern "C" { +#include #include #include #include @@ -25,105 +26,116 @@ using namespace std::chrono; bool RawDevice::supportedReport(uint8_t id, uint8_t length) { - switch(id) - { - case hidpp::ReportType::Short: - return length == (hidpp::ShortParamLength + - hidpp::Report::HeaderLength); - case hidpp::ReportType::Long: - return length == (hidpp::LongParamLength + - hidpp::Report::HeaderLength); - case dj::ReportType::Short: - return length == (dj::ShortParamLength + dj::HeaderLength); - case dj::ReportType::Long: - return length == (dj::LongParamLength + dj::HeaderLength); - default: - return false; + switch(id) { + case hidpp::ReportType::Short: + return length == (hidpp::ShortParamLength + + hidpp::Report::HeaderLength); + case hidpp::ReportType::Long: + return length == (hidpp::LongParamLength + + hidpp::Report::HeaderLength); + case dj::ReportType::Short: + return length == (dj::ShortParamLength + dj::HeaderLength); + case dj::ReportType::Long: + return length == (dj::LongParamLength + dj::HeaderLength); + default: + return false; } - - 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) +RawDevice::RawDevice(std::string path) : _path (std::move(path)), + _continue_listen (false) { int ret; - fd = ::open(path.c_str(), O_RDWR); - if (fd == -1) + _fd = ::open(_path.c_str(), O_RDWR); + if (_fd == -1) throw std::system_error(errno, std::system_category(), "RawDevice open failed"); hidraw_devinfo devinfo{}; - if (-1 == ::ioctl(fd, HIDIOCGRAWINFO, &devinfo)) - { + if (-1 == ::ioctl(_fd, HIDIOCGRAWINFO, &devinfo)) { int err = errno; - ::close(fd); + ::close(_fd); throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWINFO failed"); } - vid = devinfo.vendor; - pid = devinfo.product; + _vid = devinfo.vendor; + _pid = devinfo.product; char name_buf[256]; - if (-1 == (ret = ::ioctl(fd, HIDIOCGRAWNAME(sizeof(name_buf)), name_buf))) - { + if (-1 == (ret = ::ioctl(_fd, HIDIOCGRAWNAME(sizeof(name_buf)), name_buf) + )) { int err = errno; - ::close(fd); + ::close(_fd); throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRAWNAME failed"); } _name.assign(name_buf, ret - 1); hidraw_report_descriptor _rdesc{}; - if (-1 == ::ioctl(fd, HIDIOCGRDESCSIZE, &_rdesc.size)) - { + if (-1 == ::ioctl(_fd, HIDIOCGRDESCSIZE, &_rdesc.size)) { int err = errno; - ::close(fd); + ::close(_fd); throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESCSIZE failed"); } - if (-1 == ::ioctl(fd, HIDIOCGRDESC, &_rdesc)) - { + if (-1 == ::ioctl(_fd, HIDIOCGRDESC, &_rdesc)) { int err = errno; - ::close(fd); + ::close(_fd); throw std::system_error(err, std::system_category(), "RawDevice HIDIOCGRDESC failed"); } rdesc = std::vector(_rdesc.value, _rdesc.value + _rdesc.size); - if (-1 == ::pipe(dev_pipe)) - { + if (-1 == ::pipe(_pipe)) { int err = errno; - close(fd); + close(_fd); throw std::system_error(err, std::system_category(), "RawDevice pipe open failed"); } - continue_listen = false; + _continue_listen = false; } RawDevice::~RawDevice() { - if(fd != -1) + if(_fd != -1) { - ::close(fd); - ::close(dev_pipe[0]); - ::close(dev_pipe[1]); + ::close(_fd); + ::close(_pipe[0]); + ::close(_pipe[1]); } } +std::string RawDevice::hidrawPath() const +{ + return _path; +} + +std::string RawDevice::name() const +{ + return _name; +} + +uint16_t RawDevice::vendorId() const +{ + return _vid; +} + +uint16_t RawDevice::productId() const +{ + return _pid; +} std::vector RawDevice::sendReport(const std::vector& report) { /* If the listener will stop, handle I/O manually. * Otherwise, push to queue and wait for result. */ - if(continue_listen) - { + if(_continue_listen) { std::packaged_task()> task( [this, report]() { return this->_respondToReport(report); }); auto f = task.get_future(); - write_queue.push(&task); + _io_queue.push(&task); return f.get(); } else @@ -135,14 +147,13 @@ void RawDevice::sendReportNoResponse(const std::vector& report) { /* If the listener will stop, handle I/O manually. * Otherwise, push to queue and wait for result. */ - if(continue_listen) - { + if(_continue_listen) { std::packaged_task()> task([this, report]() { this->_sendReport(report); return std::vector(); }); auto f = task.get_future(); - write_queue.push(&task); + _io_queue.push(&task); f.get(); } else @@ -153,27 +164,23 @@ std::vector RawDevice::_respondToReport (const std::vector& request) { _sendReport(request); - while(true) - { + while(true) { std::vector response; _readReport(response, MAX_DATA_LENGTH); // All reports have the device index at byte 2 - if(response[1] != request[1]) - { - if(continue_listen) - this->handleEvent(response); + if(response[1] != request[1]) { + if(_continue_listen) + this->_handleEvent(response); continue; } if(hidpp::ReportType::Short == request[0] || - hidpp::ReportType::Long == request[0]) - { + hidpp::ReportType::Long == request[0]) { if(hidpp::ReportType::Short != response[0] && - hidpp::ReportType::Long != response[0]) - { - if(continue_listen) - this->handleEvent(response); + hidpp::ReportType::Long != response[0]) { + if(_continue_listen) + this->_handleEvent(response); continue; } @@ -183,17 +190,13 @@ std::vector RawDevice::_respondToReport bool others_match = true; for(int i = 2; i < 4; i++) - { if(response[i] != request[i]) others_match = false; - } if(others_match) return response; - } - else if(dj::ReportType::Short == request[0] || - dj::ReportType::Long == request[0]) - { + } else if(dj::ReportType::Short == request[0] || + dj::ReportType::Long == request[0]) { //Error; leave to device ot handle if(0x7f == response[2]) return response; @@ -201,16 +204,16 @@ std::vector RawDevice::_respondToReport return response; } - if(continue_listen) - this->handleEvent(response); + if(_continue_listen) + this->_handleEvent(response); } } int RawDevice::_sendReport(const std::vector& report) { - std::lock_guard lock(dev_io); + std::lock_guard lock(_dev_io); if(logid::global_verbosity == LogLevel::RAWREPORT) { - printf("[RAWREPORT] %s OUT: ", path.c_str()); + printf("[RAWREPORT] %s OUT: ", _path.c_str()); for(auto &i : report) printf("%02x ", i); printf("\n"); @@ -218,11 +221,11 @@ int RawDevice::_sendReport(const std::vector& report) assert(supportedReport(report[0], report.size())); - int ret = ::write(fd, report.data(), report.size()); + int ret = ::write(_fd, report.data(), report.size()); if(ret == -1) { ///TODO: This seems like a hacky solution // Try again before failing - ret = ::write(fd, report.data(), report.size()); + ret = ::write(_fd, report.data(), report.size()); if(ret == -1) throw std::system_error(errno, std::system_category(), "_sendReport write failed"); @@ -233,7 +236,7 @@ int RawDevice::_sendReport(const std::vector& report) int RawDevice::_readReport(std::vector& report, std::size_t maxDataLength) { - std::lock_guard lock(dev_io); + std::lock_guard lock(_dev_io); int ret; report.resize(maxDataLength); @@ -243,10 +246,10 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng fd_set fds; do { FD_ZERO(&fds); - FD_SET(fd, &fds); - FD_SET(dev_pipe[0], &fds); + FD_SET(_fd, &fds); + FD_SET(_pipe[0], &fds); - ret = select(std::max(fd, dev_pipe[0]) + 1, + ret = select(std::max(_fd, _pipe[0]) + 1, &fds, nullptr, nullptr, (HIDPP_IO_TIMEOUT.count() > 0 ? nullptr : &timeout)); } while(ret == -1 && errno == EINTR); @@ -255,19 +258,17 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng throw std::system_error(errno, std::system_category(), "_readReport select failed"); - if(FD_ISSET(fd, &fds)) - { - ret = read(fd, report.data(), report.size()); + 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"); report.resize(ret); } - if(FD_ISSET(dev_pipe[0], &fds)) - { + if(FD_ISSET(_pipe[0], &fds)) { char c; - ret = read(dev_pipe[0], &c, sizeof(char)); + ret = read(_pipe[0], &c, sizeof(char)); if(ret == -1) throw std::system_error(errno, std::system_category(), "_readReport read pipe failed"); @@ -277,7 +278,7 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng throw backend::TimeoutError(); if(logid::global_verbosity == LogLevel::RAWREPORT) { - printf("[RAWREPORT] %s IN: ", path.c_str()); + printf("[RAWREPORT] %s IN: ", _path.c_str()); for(auto &i : report) printf("%02x ", i); printf("\n"); @@ -289,82 +290,79 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng void RawDevice::interruptRead() { char c = 0; - if(-1 == write(dev_pipe[1], &c, sizeof(char))) + if(-1 == write(_pipe[1], &c, sizeof(char))) throw std::system_error(errno, std::system_category(), "interruptRead write pipe failed"); // Ensure I/O has halted - std::lock_guard lock(dev_io); + std::lock_guard lock(_dev_io); } void RawDevice::listen() { - std::lock_guard lock(listening); + std::lock_guard lock(_listening); - continue_listen = true; - while(continue_listen) - { - while(!write_queue.empty()) - { - auto task = write_queue.front(); + _continue_listen = true; + while(_continue_listen) { + while(!_io_queue.empty()) { + auto task = _io_queue.front(); (*task)(); - write_queue.pop(); + _io_queue.pop(); } std::vector report; _readReport(report, MAX_DATA_LENGTH); - this->handleEvent(report); + this->_handleEvent(report); } // Listener is stopped, handle I/O queue - while(!write_queue.empty()) - { - auto task = write_queue.front(); + while(!_io_queue.empty()) { + auto task = _io_queue.front(); (*task)(); - write_queue.pop(); + _io_queue.pop(); } - continue_listen = false; + _continue_listen = false; } void RawDevice::stopListener() { - continue_listen = false; + _continue_listen = false; interruptRead(); } void RawDevice::addEventHandler(const std::string& nickname, - const std::shared_ptr& handler) + const std::shared_ptr& handler) { - auto it = event_handlers.find(nickname); - assert(it == event_handlers.end()); - event_handlers.emplace(nickname, handler); + auto it = _event_handlers.find(nickname); + assert(it == _event_handlers.end()); + _event_handlers.emplace(nickname, handler); } void RawDevice::removeEventHandler(const std::string &nickname) { - event_handlers.erase(nickname); + _event_handlers.erase(nickname); } -const std::map>& +const std::map>& RawDevice::eventHandlers() { - return event_handlers; + return _event_handlers; } -void RawDevice::handleEvent(std::vector &report) +void RawDevice::_handleEvent(std::vector &report) { - for(auto& handler : event_handlers) + for(auto& handler : _event_handlers) if(handler.second->condition(report)) handler.second->callback(report); } bool RawDevice::isListening() { - bool ret = listening.try_lock(); + bool ret = _listening.try_lock(); if(ret) - listening.unlock(); + _listening.unlock(); return !ret; } diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index 4cb99c1..50e4f55 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -9,7 +9,7 @@ #include #include -#include "../defs.h" +#include "defs.h" #include "../../util/mutex_queue.h" #define HIDPP_IO_TIMEOUT std::chrono::seconds(2) @@ -25,11 +25,11 @@ namespace raw explicit RawDevice(std::string path); ~RawDevice(); - std::string hidrawPath() const { return path; } + std::string hidrawPath() const; - std::string name() const { return _name; } - uint16_t vendorId() const { return vid; } - uint16_t productId() const { return pid; } + std::string name() const; + uint16_t vendorId() const; + uint16_t productId() const; std::vector reportDescriptor() const { return rdesc; } @@ -42,34 +42,35 @@ namespace raw bool isListening(); void addEventHandler(const std::string& nickname, - const std::shared_ptr& handler); + const std::shared_ptr& handler); void removeEventHandler(const std::string& nickname); - const std::map>& + const std::map>& eventHandlers(); private: - std::mutex dev_io, listening; - std::string path; - int fd; - int dev_pipe[2]; - uint16_t vid; - uint16_t pid; + std::mutex _dev_io, _listening; + std::string _path; + int _fd; + int _pipe[2]; + uint16_t _vid; + uint16_t _pid; std::string _name; std::vector rdesc; - std::atomic continue_listen; + std::atomic _continue_listen; - std::map> - event_handlers; - void handleEvent(std::vector& report); + std::map> + _event_handlers; + void _handleEvent(std::vector& report); /* These will only be used internally and processed with a queue */ int _sendReport(const std::vector& report); int _readReport(std::vector& report, std::size_t maxDataLength); - std::vector _respondToReport(const std::vector& request); + std::vector _respondToReport(const std::vector& + request); - mutex_queue()>*> write_queue; + mutex_queue()>*> _io_queue; }; }}} diff --git a/src/logid/backend/defs.h b/src/logid/backend/raw/defs.h similarity index 50% rename from src/logid/backend/defs.h rename to src/logid/backend/raw/defs.h index 8ef5af1..0247e0f 100644 --- a/src/logid/backend/defs.h +++ b/src/logid/backend/raw/defs.h @@ -1,15 +1,19 @@ -#ifndef LOGID_BACKEND_DEFS_H -#define LOGID_BACKEND_DEFS_H +#ifndef LOGID_BACKEND_RAW_DEFS_H +#define LOGID_BACKEND_RAW_DEFS_H #include +#include +#include -namespace logid::backend +namespace logid { +namespace backend { +namespace raw { struct RawEventHandler { std::function& )> condition; std::function& )> callback; }; -} +}}} -#endif //LOGID_BACKEND_DEFS_H \ No newline at end of file +#endif //LOGID_BACKEND_RAW_DEFS_H \ No newline at end of file diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 8313965..eb53810 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -50,79 +50,71 @@ void logid::reload() } */ -void read_cli_options(int argc, char** argv) +void readCliOptions(int argc, char** argv) { - for(int i = 1; i < argc; i++) - { + for(int i = 1; i < argc; i++) { Option option = Option::None; - if(argv[i][0] == '-') // This argument is an option - { - switch(argv[i][1]) // Set option - { - case '-': // Full option name - { - std::string op_str = argv[i]; - if (op_str == "--verbose") option = Option::Verbose; - if (op_str == "--config") option = Option::Config; - if (op_str == "--help") option = Option::Help; - if (op_str == "--version") option = Option::Version; - break; - } - case 'v': // Verbosity - option = Option::Verbose; - break; - case 'V': //Version + if(argv[i][0] == '-') { + // This argument is an option + switch(argv[i][1]) { + case '-': { + // Full option name + std::string op_str = argv[i]; + if (op_str == "--verbose") option = Option::Verbose; + if (op_str == "--config") option = Option::Config; + if (op_str == "--help") option = Option::Help; + if (op_str == "--version") option = Option::Version; + break; + } + case 'v': // Verbosity + option = Option::Verbose; + break; + case 'V': //Version option = Option::Version; break; - case 'c': // Config file path - option = Option::Config; - break; - case 'h': // Help - option = Option::Help; - break; - default: - log_printf(WARN, "%s is not a valid option, ignoring.", argv[1]); + case 'c': // Config file path + option = Option::Config; + break; + case 'h': // Help + option = Option::Help; + break; + default: + log_printf(WARN, "%s is not a valid option, ignoring.", + argv[i]); } - switch(option) - { - case Option::Verbose: - { - if (++i >= argc) - { - global_verbosity = DEBUG; // Assume debug verbosity - break; - } - std::string loglevel = argv[i]; - try { global_verbosity = stringToLogLevel(argv[i]); } - catch (std::invalid_argument &e) - { - if (argv[i][0] == '-') - { - global_verbosity = DEBUG; // Assume debug verbosity - i--; // Go back to last argument to continue loop. - } - else - { - log_printf(WARN, e.what()); - printf("Valid verbosity levels are: Debug, Info, Warn/Warning, or Error.\n"); - exit(EXIT_FAILURE); - } - } + + switch(option) { + case Option::Verbose: { + if (++i >= argc) { + global_verbosity = DEBUG; // Assume debug verbosity break; } - case Option::Config: - { - if (++i >= argc) - { - log_printf(ERROR, "Config file is not specified."); + std::string loglevel = argv[i]; + try { + global_verbosity = stringToLogLevel(argv[i]); + } catch (std::invalid_argument &e) { + if (argv[i][0] == '-') { + global_verbosity = DEBUG; // Assume debug verbosity + i--; // Go back to last argument to continue loop. + } else { + log_printf(WARN, e.what()); + printf("Valid verbosity levels are: Debug, Info, " + "Warn/Warning, or Error.\n"); exit(EXIT_FAILURE); } - config_file = argv[i]; - break; } - case Option::Help: - { - printf(R"(logid version %s + break; + } + case Option::Config: { + if (++i >= argc) { + log_printf(ERROR, "Config file is not specified."); + exit(EXIT_FAILURE); + } + config_file = argv[i]; + break; + } + case Option::Help: + printf(R"(logid version %s Usage: %s [options] Possible options are: -v,--verbose [level] Set log level to debug/info/warn/error (leave blank for debug) @@ -130,16 +122,12 @@ Possible options are: -c,--config [file path] Change config file from default at %s -h,--help Print this message. )", LOGIOPS_VERSION, argv[0], DEFAULT_CONFIG_FILE); - - exit(EXIT_SUCCESS); - } - case Option::Version: - { - printf("%s\n", LOGIOPS_VERSION); - exit(EXIT_SUCCESS); - } - case Option::None: - break; + exit(EXIT_SUCCESS); + case Option::Version: + printf("%s\n", LOGIOPS_VERSION); + exit(EXIT_SUCCESS); + case Option::None: + break; } } } @@ -147,7 +135,7 @@ Possible options are: int main(int argc, char** argv) { - read_cli_options(argc, argv); + readCliOptions(argc, argv); /* // Read config @@ -168,8 +156,7 @@ int main(int argc, char** argv) // Scan devices, create listeners, handlers, etc. finder = new DeviceManager(); - while(!kill_logid) - { + while(!kill_logid) { finder_reloading.lock(); finder_reloading.unlock(); finder->run(); From 22455af6157f443d8e9a9d6ac6be11c117376fdd Mon Sep 17 00:00:00 2001 From: pixl Date: Tue, 23 Jun 2020 22:49:24 -0400 Subject: [PATCH 26/81] Add GPL copyright notice in all C++ source files --- src/logid/Actions.cpp | 18 ++++++++++++++++++ src/logid/Actions.h | 18 ++++++++++++++++++ src/logid/Configuration.cpp | 18 ++++++++++++++++++ src/logid/Configuration.h | 18 ++++++++++++++++++ src/logid/Device.cpp | 18 ++++++++++++++++++ src/logid/Device.h | 18 ++++++++++++++++++ src/logid/DeviceManager.cpp | 18 ++++++++++++++++++ src/logid/DeviceManager.h | 18 ++++++++++++++++++ src/logid/EvdevDevice.cpp | 18 ++++++++++++++++++ src/logid/EvdevDevice.h | 18 ++++++++++++++++++ src/logid/Receiver.cpp | 18 ++++++++++++++++++ src/logid/Receiver.h | 18 ++++++++++++++++++ src/logid/backend/Error.cpp | 18 ++++++++++++++++++ src/logid/backend/Error.h | 18 ++++++++++++++++++ src/logid/backend/dj/Error.cpp | 18 ++++++++++++++++++ src/logid/backend/dj/Error.h | 18 ++++++++++++++++++ src/logid/backend/dj/Receiver.cpp | 18 ++++++++++++++++++ src/logid/backend/dj/Receiver.h | 18 ++++++++++++++++++ src/logid/backend/dj/ReceiverMonitor.cpp | 18 ++++++++++++++++++ src/logid/backend/dj/ReceiverMonitor.h | 18 ++++++++++++++++++ src/logid/backend/dj/Report.cpp | 18 ++++++++++++++++++ src/logid/backend/dj/Report.h | 18 ++++++++++++++++++ src/logid/backend/dj/defs.h | 18 ++++++++++++++++++ src/logid/backend/hidpp/Device.cpp | 18 ++++++++++++++++++ src/logid/backend/hidpp/Device.h | 18 ++++++++++++++++++ src/logid/backend/hidpp/Report.cpp | 18 ++++++++++++++++++ src/logid/backend/hidpp/Report.h | 18 ++++++++++++++++++ src/logid/backend/hidpp/defs.h | 18 ++++++++++++++++++ src/logid/backend/hidpp10/Device.cpp | 18 ++++++++++++++++++ src/logid/backend/hidpp10/Device.h | 18 ++++++++++++++++++ src/logid/backend/hidpp10/Error.cpp | 18 ++++++++++++++++++ src/logid/backend/hidpp10/Error.h | 18 ++++++++++++++++++ src/logid/backend/hidpp10/defs.h | 18 ++++++++++++++++++ src/logid/backend/hidpp20/Device.cpp | 18 ++++++++++++++++++ src/logid/backend/hidpp20/Device.h | 18 ++++++++++++++++++ src/logid/backend/hidpp20/Error.cpp | 18 ++++++++++++++++++ src/logid/backend/hidpp20/Error.h | 18 ++++++++++++++++++ src/logid/backend/hidpp20/EssentialFeature.cpp | 18 ++++++++++++++++++ src/logid/backend/hidpp20/EssentialFeature.h | 18 ++++++++++++++++++ src/logid/backend/hidpp20/Feature.cpp | 18 ++++++++++++++++++ src/logid/backend/hidpp20/Feature.h | 18 ++++++++++++++++++ src/logid/backend/hidpp20/feature_defs.h | 18 ++++++++++++++++++ .../backend/hidpp20/features/DeviceName.cpp | 18 ++++++++++++++++++ .../backend/hidpp20/features/DeviceName.h | 18 ++++++++++++++++++ src/logid/backend/hidpp20/features/Root.cpp | 18 ++++++++++++++++++ src/logid/backend/hidpp20/features/Root.h | 18 ++++++++++++++++++ src/logid/backend/raw/DeviceMonitor.cpp | 18 ++++++++++++++++++ src/logid/backend/raw/DeviceMonitor.h | 18 ++++++++++++++++++ src/logid/backend/raw/RawDevice.cpp | 18 ++++++++++++++++++ src/logid/backend/raw/RawDevice.h | 18 ++++++++++++++++++ src/logid/backend/raw/defs.h | 18 ++++++++++++++++++ src/logid/logid.cpp | 18 ++++++++++++++++++ src/logid/logid.h | 18 ++++++++++++++++++ src/logid/util.cpp | 18 ++++++++++++++++++ src/logid/util.h | 18 ++++++++++++++++++ src/logid/util/mutex_queue.h | 18 ++++++++++++++++++ 56 files changed, 1008 insertions(+) diff --git a/src/logid/Actions.cpp b/src/logid/Actions.cpp index 747f44c..db4e1ed 100644 --- a/src/logid/Actions.cpp +++ b/src/logid/Actions.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include diff --git a/src/logid/Actions.h b/src/logid/Actions.h index a283e3e..93fdd44 100644 --- a/src/logid/Actions.h +++ b/src/logid/Actions.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_ACTIONS_H #define LOGID_ACTIONS_H #include "Device.h" diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index 9af319e..6e30e31 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index 7ab0868..53a3d6a 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_CONFIGURATION_H #define LOGID_CONFIGURATION_H diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 3041542..f245fc4 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include "util.h" #include "Device.h" diff --git a/src/logid/Device.h b/src/logid/Device.h index ccc78a1..8645697 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_DEVICE_H #define LOGID_DEVICE_H diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index 8c6a35d..7c40687 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include diff --git a/src/logid/DeviceManager.h b/src/logid/DeviceManager.h index 57e9012..50f7c31 100644 --- a/src/logid/DeviceManager.h +++ b/src/logid/DeviceManager.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_DEVICEMANAGER_H #define LOGID_DEVICEMANAGER_H diff --git a/src/logid/EvdevDevice.cpp b/src/logid/EvdevDevice.cpp index 5ff21c4..05eb3dc 100644 --- a/src/logid/EvdevDevice.cpp +++ b/src/logid/EvdevDevice.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include diff --git a/src/logid/EvdevDevice.h b/src/logid/EvdevDevice.h index c37c2aa..a37584c 100644 --- a/src/logid/EvdevDevice.h +++ b/src/logid/EvdevDevice.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_EVDEVDEVICE_H #define LOGID_EVDEVDEVICE_H diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index c2b6e1e..c718c11 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include "Receiver.h" #include "util.h" diff --git a/src/logid/Receiver.h b/src/logid/Receiver.h index 99aeebe..313bc91 100644 --- a/src/logid/Receiver.h +++ b/src/logid/Receiver.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_RECEIVER_H #define LOGID_RECEIVER_H diff --git a/src/logid/backend/Error.cpp b/src/logid/backend/Error.cpp index f7f8d0b..f15fc0e 100644 --- a/src/logid/backend/Error.cpp +++ b/src/logid/backend/Error.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include "Error.h" const char *logid::backend::TimeoutError::what() const noexcept diff --git a/src/logid/backend/Error.h b/src/logid/backend/Error.h index 3cc515f..d281a3b 100644 --- a/src/logid/backend/Error.h +++ b/src/logid/backend/Error.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_ERROR_H #define LOGID_BACKEND_ERROR_H diff --git a/src/logid/backend/dj/Error.cpp b/src/logid/backend/dj/Error.cpp index 8a7f73f..4277cf6 100644 --- a/src/logid/backend/dj/Error.cpp +++ b/src/logid/backend/dj/Error.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include "Error.h" using namespace logid::backend::dj; diff --git a/src/logid/backend/dj/Error.h b/src/logid/backend/dj/Error.h index 6c48f17..dc44067 100644 --- a/src/logid/backend/dj/Error.h +++ b/src/logid/backend/dj/Error.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_DJ_ERROR_H #define LOGID_BACKEND_DJ_ERROR_H diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp index 14bb4dd..08784f1 100644 --- a/src/logid/backend/dj/Receiver.cpp +++ b/src/logid/backend/dj/Receiver.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include "Report.h" #include "Receiver.h" diff --git a/src/logid/backend/dj/Receiver.h b/src/logid/backend/dj/Receiver.h index 40c543e..ce39330 100644 --- a/src/logid/backend/dj/Receiver.h +++ b/src/logid/backend/dj/Receiver.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_DJ_RECEIVER_H #define LOGID_BACKEND_DJ_RECEIVER_H diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp index 8d66397..5674b5f 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/dj/ReceiverMonitor.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include "ReceiverMonitor.h" #include diff --git a/src/logid/backend/dj/ReceiverMonitor.h b/src/logid/backend/dj/ReceiverMonitor.h index b2223c1..860aed7 100644 --- a/src/logid/backend/dj/ReceiverMonitor.h +++ b/src/logid/backend/dj/ReceiverMonitor.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_DJ_RECEIVERMONITOR_H #define LOGID_BACKEND_DJ_RECEIVERMONITOR_H diff --git a/src/logid/backend/dj/Report.cpp b/src/logid/backend/dj/Report.cpp index 5f7d596..49d2900 100644 --- a/src/logid/backend/dj/Report.cpp +++ b/src/logid/backend/dj/Report.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include diff --git a/src/logid/backend/dj/Report.h b/src/logid/backend/dj/Report.h index 36482fc..76439a5 100644 --- a/src/logid/backend/dj/Report.h +++ b/src/logid/backend/dj/Report.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_DJ_REPORT_H #define LOGID_BACKEND_DJ_REPORT_H diff --git a/src/logid/backend/dj/defs.h b/src/logid/backend/dj/defs.h index 0040432..d527d9b 100644 --- a/src/logid/backend/dj/defs.h +++ b/src/logid/backend/dj/defs.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_DJ_DEFS_H #define LOGID_BACKEND_DJ_DEFS_H diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 4c0d4cc..029dda4 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include "Device.h" diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 1372cfc..3ba524e 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_HIDPP_DEVICE_H #define LOGID_BACKEND_HIDPP_DEVICE_H diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index a8c0429..c1e20df 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index c3b6ecd..7e2c814 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_HIDPP_REPORT_H #define LOGID_BACKEND_HIDPP_REPORT_H diff --git a/src/logid/backend/hidpp/defs.h b/src/logid/backend/hidpp/defs.h index d08886d..a884e65 100644 --- a/src/logid/backend/hidpp/defs.h +++ b/src/logid/backend/hidpp/defs.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_HIDPP_DEFS_H #define LOGID_HIDPP_DEFS_H diff --git a/src/logid/backend/hidpp10/Device.cpp b/src/logid/backend/hidpp10/Device.cpp index 12531ee..d35d0d6 100644 --- a/src/logid/backend/hidpp10/Device.cpp +++ b/src/logid/backend/hidpp10/Device.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include "Device.h" diff --git a/src/logid/backend/hidpp10/Device.h b/src/logid/backend/hidpp10/Device.h index 688bab8..b7ff479 100644 --- a/src/logid/backend/hidpp10/Device.h +++ b/src/logid/backend/hidpp10/Device.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_HIDPP10_DEVICE_H #define LOGID_BACKEND_HIDPP10_DEVICE_H diff --git a/src/logid/backend/hidpp10/Error.cpp b/src/logid/backend/hidpp10/Error.cpp index 2206173..76c8415 100644 --- a/src/logid/backend/hidpp10/Error.cpp +++ b/src/logid/backend/hidpp10/Error.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include "Error.h" diff --git a/src/logid/backend/hidpp10/Error.h b/src/logid/backend/hidpp10/Error.h index 7021839..f5fffae 100644 --- a/src/logid/backend/hidpp10/Error.h +++ b/src/logid/backend/hidpp10/Error.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_HIDPP10_ERROR_H #define LOGID_BACKEND_HIDPP10_ERROR_H diff --git a/src/logid/backend/hidpp10/defs.h b/src/logid/backend/hidpp10/defs.h index 1d080f1..d3c06b8 100644 --- a/src/logid/backend/hidpp10/defs.h +++ b/src/logid/backend/hidpp10/defs.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_HIDPP10_DEFS_H #define LOGID_BACKEND_HIDPP10_DEFS_H diff --git a/src/logid/backend/hidpp20/Device.cpp b/src/logid/backend/hidpp20/Device.cpp index 0d93ecc..2e71ae7 100644 --- a/src/logid/backend/hidpp20/Device.cpp +++ b/src/logid/backend/hidpp20/Device.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include "Device.h" diff --git a/src/logid/backend/hidpp20/Device.h b/src/logid/backend/hidpp20/Device.h index c0b2ff4..871363d 100644 --- a/src/logid/backend/hidpp20/Device.h +++ b/src/logid/backend/hidpp20/Device.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_HIDPP20_DEVICE_H #define LOGID_HIDPP20_DEVICE_H diff --git a/src/logid/backend/hidpp20/Error.cpp b/src/logid/backend/hidpp20/Error.cpp index 7941c9e..19a0ad8 100644 --- a/src/logid/backend/hidpp20/Error.cpp +++ b/src/logid/backend/hidpp20/Error.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include "Error.h" diff --git a/src/logid/backend/hidpp20/Error.h b/src/logid/backend/hidpp20/Error.h index 533c4be..06b5132 100644 --- a/src/logid/backend/hidpp20/Error.h +++ b/src/logid/backend/hidpp20/Error.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_HIDPP20_ERROR_H #define LOGID_BACKEND_HIDPP20_ERROR_H diff --git a/src/logid/backend/hidpp20/EssentialFeature.cpp b/src/logid/backend/hidpp20/EssentialFeature.cpp index 3634cc5..bbca19f 100644 --- a/src/logid/backend/hidpp20/EssentialFeature.cpp +++ b/src/logid/backend/hidpp20/EssentialFeature.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include "EssentialFeature.h" #include "feature_defs.h" diff --git a/src/logid/backend/hidpp20/EssentialFeature.h b/src/logid/backend/hidpp20/EssentialFeature.h index 4e25165..f72a244 100644 --- a/src/logid/backend/hidpp20/EssentialFeature.h +++ b/src/logid/backend/hidpp20/EssentialFeature.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_HIDPP20_ESSENTIAL_FEATURE_H #define LOGID_HIDPP20_ESSENTIAL_FEATURE_H diff --git a/src/logid/backend/hidpp20/Feature.cpp b/src/logid/backend/hidpp20/Feature.cpp index 90ec9ae..bfcfeb7 100644 --- a/src/logid/backend/hidpp20/Feature.cpp +++ b/src/logid/backend/hidpp20/Feature.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include "Feature.h" #include "feature_defs.h" #include "features/Root.h" diff --git a/src/logid/backend/hidpp20/Feature.h b/src/logid/backend/hidpp20/Feature.h index 6dba742..f68cfa0 100644 --- a/src/logid/backend/hidpp20/Feature.h +++ b/src/logid/backend/hidpp20/Feature.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_HIDPP20_FEATURE_H #define LOGID_HIDPP20_FEATURE_H diff --git a/src/logid/backend/hidpp20/feature_defs.h b/src/logid/backend/hidpp20/feature_defs.h index f72db8f..eb0fc9d 100644 --- a/src/logid/backend/hidpp20/feature_defs.h +++ b/src/logid/backend/hidpp20/feature_defs.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_HIDPP20_FEATUREDEFS #define LOGID_BACKEND_HIDPP20_FEATUREDEFS diff --git a/src/logid/backend/hidpp20/features/DeviceName.cpp b/src/logid/backend/hidpp20/features/DeviceName.cpp index bc133e7..d6c44bc 100644 --- a/src/logid/backend/hidpp20/features/DeviceName.cpp +++ b/src/logid/backend/hidpp20/features/DeviceName.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include "DeviceName.h" diff --git a/src/logid/backend/hidpp20/features/DeviceName.h b/src/logid/backend/hidpp20/features/DeviceName.h index deb6d03..e21fe27 100644 --- a/src/logid/backend/hidpp20/features/DeviceName.h +++ b/src/logid/backend/hidpp20/features/DeviceName.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H #define LOGID_BACKEND_HIDPP20_FEATURE_DEVICENAME_H diff --git a/src/logid/backend/hidpp20/features/Root.cpp b/src/logid/backend/hidpp20/features/Root.cpp index d7462ad..ba5e91a 100644 --- a/src/logid/backend/hidpp20/features/Root.cpp +++ b/src/logid/backend/hidpp20/features/Root.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include "Root.h" using namespace logid::backend::hidpp20; diff --git a/src/logid/backend/hidpp20/features/Root.h b/src/logid/backend/hidpp20/features/Root.h index 295e5bf..6d699aa 100644 --- a/src/logid/backend/hidpp20/features/Root.h +++ b/src/logid/backend/hidpp20/features/Root.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H #define LOGID_BACKEND_HIDPP20_FEATURE_ROOT_H diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp index 77aa293..6a9027c 100644 --- a/src/logid/backend/raw/DeviceMonitor.cpp +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include "DeviceMonitor.h" #include diff --git a/src/logid/backend/raw/DeviceMonitor.h b/src/logid/backend/raw/DeviceMonitor.h index b24ad19..8239da9 100644 --- a/src/logid/backend/raw/DeviceMonitor.h +++ b/src/logid/backend/raw/DeviceMonitor.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_RAW_DEVICEMONITOR_H #define LOGID_BACKEND_RAW_DEVICEMONITOR_H diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index bae0742..a0af1c2 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include "RawDevice.h" #include "../Error.h" #include "../hidpp/defs.h" diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index 50e4f55..d12020a 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_RAWDEVICE_H #define LOGID_BACKEND_RAWDEVICE_H diff --git a/src/logid/backend/raw/defs.h b/src/logid/backend/raw/defs.h index 0247e0f..388d9c6 100644 --- a/src/logid/backend/raw/defs.h +++ b/src/logid/backend/raw/defs.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_BACKEND_RAW_DEFS_H #define LOGID_BACKEND_RAW_DEFS_H diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index eb53810..077b841 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include diff --git a/src/logid/logid.h b/src/logid/logid.h index 7498e5c..c73b577 100644 --- a/src/logid/logid.h +++ b/src/logid/logid.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_LOGID_H #define LOGID_LOGID_H diff --git a/src/logid/util.cpp b/src/logid/util.cpp index 607b337..ead4def 100644 --- a/src/logid/util.cpp +++ b/src/logid/util.cpp @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #include #include #include diff --git a/src/logid/util.h b/src/logid/util.h index de7690b..b3f7c2d 100644 --- a/src/logid/util.h +++ b/src/logid/util.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_UTIL_H #define LOGID_UTIL_H diff --git a/src/logid/util/mutex_queue.h b/src/logid/util/mutex_queue.h index 153b1a5..9efc927 100644 --- a/src/logid/util/mutex_queue.h +++ b/src/logid/util/mutex_queue.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef MUTEX_QUEUE_H #define MUTEX_QUEUE_H From 1d001237ba45b7ca5ee319f0b09d6e764b7dadb3 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 24 Jun 2020 00:53:04 -0400 Subject: [PATCH 27/81] Monitor wake up/sleep events with Receiver --- src/logid/Receiver.cpp | 20 ++++++++---- src/logid/Receiver.h | 4 +-- src/logid/backend/dj/Receiver.cpp | 39 +++++++++++++++--------- src/logid/backend/dj/Receiver.h | 14 +++++++-- src/logid/backend/dj/ReceiverMonitor.cpp | 2 ++ src/logid/backend/dj/ReceiverMonitor.h | 2 +- src/logid/backend/raw/DeviceMonitor.cpp | 2 +- 7 files changed, 55 insertions(+), 28 deletions(-) diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index c718c11..de28907 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -25,7 +25,7 @@ using namespace logid; using namespace logid::backend; -Receiver::Receiver(std::string path) : dj::ReceiverMonitor(path) +Receiver::Receiver(std::string path) : dj::ReceiverMonitor(path), _path (path) { log_printf(DEBUG, "logid::Receiver created on %s", path.c_str()); } @@ -33,8 +33,17 @@ Receiver::Receiver(std::string path) : dj::ReceiverMonitor(path) void Receiver::addDevice(hidpp::DeviceConnectionEvent event) { try { + auto dev = _devices.find(event.index); + if(dev != _devices.end()) { + if(event.linkEstablished) + dev->second->wakeup(); + else + dev->second->sleep(); + return; + } + if(!event.linkEstablished) - return; // Device is probably asleep, wait until it wakes up + return; hidpp::Device hidpp_device(receiver(), event); @@ -49,13 +58,12 @@ void Receiver::addDevice(hidpp::DeviceConnectionEvent event) 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()); + 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()); diff --git a/src/logid/Receiver.h b/src/logid/Receiver.h index 313bc91..029c80c 100644 --- a/src/logid/Receiver.h +++ b/src/logid/Receiver.h @@ -31,8 +31,8 @@ namespace logid Receiver(std::string path); protected: - virtual void addDevice(backend::hidpp::DeviceConnectionEvent event); - virtual void removeDevice(backend::hidpp::DeviceIndex index); + void addDevice(backend::hidpp::DeviceConnectionEvent event) override; + void removeDevice(backend::hidpp::DeviceIndex index) override; private: std::map> _devices; std::string _path; diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp index 08784f1..b3d183a 100644 --- a/src/logid/backend/dj/Receiver.cpp +++ b/src/logid/backend/dj/Receiver.cpp @@ -53,7 +53,7 @@ Receiver::Receiver(std::string path) : void Receiver::enumerateDj() { - sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{}); + _sendDjRequest(hidpp::DefaultDevice, GetPairedDevices,{}); } Receiver::NotificationFlags Receiver::getHidppNotifications() @@ -228,29 +228,29 @@ hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent( hidpp::DeviceConnectionEvent event{}; event.index = report.deviceIndex(); - event.unifying = ((report.paramBegin()[0] & 0b111) == 0x04); + event.unifying = ((report.address() & 0b111) == 0x04); event.deviceType = static_cast( - report.paramBegin()[1] & 0x0f); - event.softwarePresent = report.paramBegin()[1] & (1<<4); - event.encrypted = report.paramBegin()[1] & (1<<5); - event.linkEstablished = report.paramBegin()[1] & (1<<6); - event.withPayload = report.paramBegin()[1] & (1<<7); + report.paramBegin()[0] & 0x0f); + event.softwarePresent = report.paramBegin()[0] & (1<<4); + event.encrypted = report.paramBegin()[0] & (1<<5); + event.linkEstablished = !(report.paramBegin()[0] & (1<<6)); + event.withPayload = report.paramBegin()[0] & (1<<7); - event.pid = report.paramBegin()[3]; - event.pid |= (report.paramBegin()[2] << 8); + event.pid =(report.paramBegin()[2] << 8); + event.pid |= report.paramBegin()[1]; return event; } -void Receiver::handleDjEvent(Report& report) +void Receiver::_handleDjEvent(Report& report) { for(auto& handler : _dj_event_handlers) if(handler.second->condition(report)) handler.second->callback(report); } -void Receiver::handleHidppEvent(hidpp::Report &report) +void Receiver::_handleHidppEvent(hidpp::Report &report) { for(auto& handler : _hidpp_event_handlers) if(handler.second->condition(report)) @@ -295,7 +295,7 @@ Receiver::hidppEventHandlers() return _hidpp_event_handlers; } -void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function, +void Receiver::_sendDjRequest(hidpp::DeviceIndex index, uint8_t function, const std::vector&& params) { assert(params.size() <= LongParamLength); @@ -310,10 +310,19 @@ void Receiver::sendDjRequest(hidpp::DeviceIndex index, uint8_t function, _raw_device->sendReportNoResponse(request.rawData()); } +Receiver::ConnectionStatusEvent Receiver::connectionStatusEvent(Report& report) +{ + assert(report.feature() == ConnectionStatus); + ConnectionStatusEvent event{}; + event.index = report.index(); + event.linkLost = report.paramBegin()[0]; + return event; +} + void Receiver::listen() { if(!_raw_device->isListening()) - std::thread{[=]() { _raw_device->listen(); }}.detach(); + std::thread{[this]() { this->_raw_device->listen(); }}.detach(); if(_raw_device->eventHandlers().find("RECV_HIDPP") == _raw_device->eventHandlers().end()) { @@ -328,7 +337,7 @@ void Receiver::listen() hidpp_handler->callback = [this](std::vector& report) ->void { hidpp::Report _report(report); - this->handleHidppEvent(_report); + this->_handleHidppEvent(_report); }; _raw_device->addEventHandler("RECV_HIDPP", hidpp_handler); } @@ -346,7 +355,7 @@ void Receiver::listen() dj_handler->callback = [this](std::vector& report)->void { Report _report(report); - this->handleDjEvent(_report); + this->_handleDjEvent(_report); }; _raw_device->addEventHandler("RECV_DJ", dj_handler); } diff --git a/src/logid/backend/dj/Receiver.h b/src/logid/backend/dj/Receiver.h index ce39330..97aca4a 100644 --- a/src/logid/backend/dj/Receiver.h +++ b/src/logid/backend/dj/Receiver.h @@ -69,6 +69,14 @@ namespace dj void enumerateDj(); + struct ConnectionStatusEvent + { + hidpp::DeviceIndex index; + bool linkLost; + }; + + ConnectionStatusEvent connectionStatusEvent(dj::Report& report); + /* The following functions deal with HID++ 1.0 features. * While these are not technically DJ functions, it is redundant * to have a separate hidpp10::Receiver class for these functions. @@ -153,11 +161,11 @@ namespace dj std::shared_ptr rawDevice() const; private: - void sendDjRequest(hidpp::DeviceIndex index, uint8_t function, + void _sendDjRequest(hidpp::DeviceIndex index, uint8_t function, const std::vector&& params); - void handleDjEvent(dj::Report& report); - void handleHidppEvent(hidpp::Report& report); + void _handleDjEvent(dj::Report& report); + void _handleHidppEvent(hidpp::Report& report); std::map> _dj_event_handlers; diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp index 5674b5f..6447f75 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/dj/ReceiverMonitor.cpp @@ -73,6 +73,8 @@ void ReceiverMonitor::run() void ReceiverMonitor::stop() { + _receiver->removeHidppEventHandler("RECVMON"); + _receiver->stopListening(); } diff --git a/src/logid/backend/dj/ReceiverMonitor.h b/src/logid/backend/dj/ReceiverMonitor.h index 860aed7..73d7938 100644 --- a/src/logid/backend/dj/ReceiverMonitor.h +++ b/src/logid/backend/dj/ReceiverMonitor.h @@ -32,7 +32,7 @@ namespace dj class ReceiverMonitor { public: - ReceiverMonitor(std::string path); + explicit ReceiverMonitor(std::string path); void enumerate(); void run(); diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp index 6a9027c..8d4beda 100644 --- a/src/logid/backend/raw/DeviceMonitor.cpp +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -149,7 +149,7 @@ void DeviceMonitor::enumerate() std::string devnode = udev_device_get_devnode(device); udev_device_unref(device); - std::thread([this](const std::string name) { + std::thread([this](const std::string& name) { this->addDevice(name); }, devnode).detach(); } From 152b9e6cfd73b952880d5186e775c6c472cb2114 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 24 Jun 2020 02:05:05 -0400 Subject: [PATCH 28/81] Add interface for device features in logid::Device --- src/logid/Device.h | 2 ++ src/logid/features/DeviceFeature.h | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 src/logid/features/DeviceFeature.h diff --git a/src/logid/Device.h b/src/logid/Device.h index 8645697..9b4e130 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -21,6 +21,7 @@ #include "backend/hidpp/defs.h" #include "backend/hidpp20/Device.h" +#include "features/DeviceFeature.h" namespace logid { @@ -40,6 +41,7 @@ namespace logid backend::hidpp20::Device _hidpp20; std::string _path; backend::hidpp::DeviceIndex _index; + std::vector> _features; }; } diff --git a/src/logid/features/DeviceFeature.h b/src/logid/features/DeviceFeature.h new file mode 100644 index 0000000..762ff21 --- /dev/null +++ b/src/logid/features/DeviceFeature.h @@ -0,0 +1,15 @@ +#ifndef LOGID_FEATURES_DEVICEFEATURE_H +#define LOGID_FEATURES_DEVICEFEATURE_H + +namespace logid { +namespace features +{ + class DeviceFeature + { + public: + virtual void configure() = 0; + virtual void listen() = 0; + }; +}} + +#endif //LOGID_FEATURES_DEVICEFEATURE_H From 60e47572d6de112f15e13a226f735b98ee72aa43 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 24 Jun 2020 02:10:29 -0400 Subject: [PATCH 29/81] Add GPL copyright notice to DeviceFeature.h --- src/logid/features/DeviceFeature.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/logid/features/DeviceFeature.h b/src/logid/features/DeviceFeature.h index 762ff21..accef5e 100644 --- a/src/logid/features/DeviceFeature.h +++ b/src/logid/features/DeviceFeature.h @@ -1,3 +1,21 @@ +/* + * 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 . + * + */ + #ifndef LOGID_FEATURES_DEVICEFEATURE_H #define LOGID_FEATURES_DEVICEFEATURE_H From 7ae46c938d983878ed21ce75ca8496f50e573e01 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 24 Jun 2020 02:33:26 -0400 Subject: [PATCH 30/81] Implement FeatureSet feature --- src/logid/CMakeLists.txt | 1 + src/logid/backend/hidpp20/Feature.cpp | 4 +- .../backend/hidpp20/features/FeatureSet.cpp | 51 +++++++++++++++++++ .../backend/hidpp20/features/FeatureSet.h | 48 +++++++++++++++++ 4 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 src/logid/backend/hidpp20/features/FeatureSet.cpp create mode 100644 src/logid/backend/hidpp20/features/FeatureSet.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index a51ed2b..043f859 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(logid backend/hidpp20/Feature.cpp backend/hidpp20/EssentialFeature.cpp backend/hidpp20/features/Root.cpp + backend/hidpp20/features/FeatureSet.cpp backend/hidpp20/features/DeviceName.cpp backend/dj/Report.cpp util/mutex_queue.h) diff --git a/src/logid/backend/hidpp20/Feature.cpp b/src/logid/backend/hidpp20/Feature.cpp index bfcfeb7..5f32bbb 100644 --- a/src/logid/backend/hidpp20/Feature.cpp +++ b/src/logid/backend/hidpp20/Feature.cpp @@ -44,8 +44,8 @@ Feature::Feature(Device* dev, uint16_t _id) : _device (dev) if(_id) { std::vector getFunc_req(2); - getFunc_req[0] = _id & 0xff; - getFunc_req[1] = (_id >> 8) & 0xff; + getFunc_req[0] = (_id >> 8) & 0xff; + getFunc_req[1] = _id & 0xff; auto getFunc_resp = this->callFunction(Root::GetFeature, getFunc_req); _index = getFunc_resp[0]; diff --git a/src/logid/backend/hidpp20/features/FeatureSet.cpp b/src/logid/backend/hidpp20/features/FeatureSet.cpp new file mode 100644 index 0000000..092b809 --- /dev/null +++ b/src/logid/backend/hidpp20/features/FeatureSet.cpp @@ -0,0 +1,51 @@ +/* + * 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 . + * + */ +#include "FeatureSet.h" + +using namespace logid::backend::hidpp20; + +FeatureSet::FeatureSet(Device *device) : Feature(device, ID) +{ +} + +uint8_t FeatureSet::getFeatureCount() +{ + std::vector params(0); + auto response = callFunction(GetFeatureCount, params); + return response[0]; +} + +uint16_t FeatureSet::getFeature(uint8_t feature_index) +{ + std::vector params(1); + params[0] = feature_index; + auto response = callFunction(GetFeature, params); + + uint16_t feature_id = (response[0] << 8); + feature_id |= response[1]; + return feature_id; +} + +std::map FeatureSet::getFeatures() +{ + uint8_t feature_count = getFeatureCount(); + std::map features; + for(uint8_t i = 0; i < feature_count; i++) + features[i] = getFeature(i); + return features; +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/FeatureSet.h b/src/logid/backend/hidpp20/features/FeatureSet.h new file mode 100644 index 0000000..27a814e --- /dev/null +++ b/src/logid/backend/hidpp20/features/FeatureSet.h @@ -0,0 +1,48 @@ +/* + * 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 . + * + */ +#ifndef LOGID_BACKEND_HIDPP20_FEATURE_FEATURESET_H +#define LOGID_BACKEND_HIDPP20_FEATURE_FEATURESET_H + +#include "../Feature.h" +#include "../feature_defs.h" + +namespace logid { +namespace backend { +namespace hidpp20 +{ + class FeatureSet : public Feature + { + public: + static const uint16_t ID = FeatureID::FEATURE_SET; + virtual uint16_t getID() { return ID; } + + enum Function : uint8_t + { + GetFeatureCount = 0, + GetFeature = 1 + }; + + explicit FeatureSet(Device* device); + + uint8_t getFeatureCount(); + uint16_t getFeature(uint8_t feature_index); + std::map getFeatures(); + }; +}}} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_FEATURESET_H From ef0a0fab8d34fae36c5a21ae8d5c7aafe04ee648 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 24 Jun 2020 03:39:52 -0400 Subject: [PATCH 31/81] Support Reset (0x0020) feature --- src/logid/CMakeLists.txt | 1 + src/logid/backend/hidpp20/features/Reset.cpp | 42 +++++++++++++++++ src/logid/backend/hidpp20/features/Reset.h | 47 ++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 src/logid/backend/hidpp20/features/Reset.cpp create mode 100644 src/logid/backend/hidpp20/features/Reset.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 043f859..86d9086 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable(logid backend/hidpp20/features/Root.cpp backend/hidpp20/features/FeatureSet.cpp backend/hidpp20/features/DeviceName.cpp + backend/hidpp20/features/Reset.cpp backend/dj/Report.cpp util/mutex_queue.h) diff --git a/src/logid/backend/hidpp20/features/Reset.cpp b/src/logid/backend/hidpp20/features/Reset.cpp new file mode 100644 index 0000000..3512055 --- /dev/null +++ b/src/logid/backend/hidpp20/features/Reset.cpp @@ -0,0 +1,42 @@ +/* + * 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 . + * + */ +#include "Reset.h" + +using namespace logid::backend::hidpp20; + +Reset::Reset(Device *device) : Feature(device, ID) +{ +} + +uint16_t Reset::getProfile() +{ + std::vector params(0); + auto results = callFunction(GetProfile, params); + + uint16_t profile = results[1]; + profile |= (results[0] << 8); + return profile; +} + +void Reset::reset(uint16_t profile) +{ + std::vector params(2); + params[0] = (profile >> 8) & 0xff; + params[1] = profile & 0xff; + callFunction(ResetToProfile, params); +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/Reset.h b/src/logid/backend/hidpp20/features/Reset.h new file mode 100644 index 0000000..a3b6e14 --- /dev/null +++ b/src/logid/backend/hidpp20/features/Reset.h @@ -0,0 +1,47 @@ +/* + * 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 . + * + */ +#ifndef LOGID_BACKEND_HIDPP20_FEATURE_RESET_H +#define LOGID_BACKEND_HIDPP20_FEATURE_RESET_H + +#include "../Feature.h" +#include "../feature_defs.h" + +namespace logid { +namespace backend { +namespace hidpp20 +{ + class Reset : public Feature + { + public: + static const uint16_t ID = FeatureID::RESET; + virtual uint16_t getID() { return ID; } + + enum Function : uint8_t + { + GetProfile = 0, + ResetToProfile = 1 + }; + + explicit Reset(Device* device); + + uint16_t getProfile(); + void reset(uint16_t profile = 0); + }; +}}} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_RESET_H From d84363019b4fe162485bd430df4774d1d239cc91 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 24 Jun 2020 05:48:03 -0400 Subject: [PATCH 32/81] Create a safe thread wrapper --- src/logid/CMakeLists.txt | 4 +- src/logid/util/ExceptionHandler.cpp | 43 +++++++++++++++++ src/logid/util/ExceptionHandler.h | 29 ++++++++++++ src/logid/util/thread.cpp | 72 +++++++++++++++++++++++++++++ src/logid/util/thread.h | 57 +++++++++++++++++++++++ 5 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 src/logid/util/ExceptionHandler.cpp create mode 100644 src/logid/util/ExceptionHandler.h create mode 100644 src/logid/util/thread.cpp create mode 100644 src/logid/util/thread.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 86d9086..bf7f61a 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -33,7 +33,9 @@ add_executable(logid backend/hidpp20/features/DeviceName.cpp backend/hidpp20/features/Reset.cpp backend/dj/Report.cpp - util/mutex_queue.h) + util/mutex_queue.h + util/thread.cpp + util/ExceptionHandler.cpp) set_target_properties(logid PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/src/logid/util/ExceptionHandler.cpp b/src/logid/util/ExceptionHandler.cpp new file mode 100644 index 0000000..3019806 --- /dev/null +++ b/src/logid/util/ExceptionHandler.cpp @@ -0,0 +1,43 @@ +/* + * 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 . + * + */ +#include +#include "../util.h" +#include "ExceptionHandler.h" +#include "../backend/hidpp10/Error.h" +#include "../backend/hidpp20/Error.h" + +using namespace logid; + +void ExceptionHandler::Default(std::exception& error) +{ + try { + throw error; + } catch(backend::hidpp10::Error& e) { + log_printf(WARN, "HID++ 1.0 error ignored on detached thread: %s", + error.what()); + } catch(backend::hidpp20::Error& e) { + log_printf(WARN, "HID++ 2.0 error ignored on detached thread: %s", + error.what()); + } catch(std::system_error& e) { + log_printf(WARN, "System error ignored on detached thread: %s", + error.what()); + } catch(std::exception& e) { + log_printf(WARN, "Error ignored on detached thread: %s", + error.what()); + } +} \ No newline at end of file diff --git a/src/logid/util/ExceptionHandler.h b/src/logid/util/ExceptionHandler.h new file mode 100644 index 0000000..daf9bea --- /dev/null +++ b/src/logid/util/ExceptionHandler.h @@ -0,0 +1,29 @@ +/* + * 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 . + * + */ +#ifndef LOGID_EXCEPTIONHANDLER_H +#define LOGID_EXCEPTIONHANDLER_H + +#include + +namespace logid { +namespace ExceptionHandler +{ + void Default(std::exception& e); +}} + +#endif //LOGID_EXCEPTIONHANDLER_H diff --git a/src/logid/util/thread.cpp b/src/logid/util/thread.cpp new file mode 100644 index 0000000..3460bd7 --- /dev/null +++ b/src/logid/util/thread.cpp @@ -0,0 +1,72 @@ +/* + * 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 . + * + */ +#include "thread.h" + +using namespace logid; + +thread::thread(const std::function& function, + const std::function& exception_handler) + : _function (std::make_shared>(function)), + _exception_handler (std::make_shared> (exception_handler)) +{ +} + +thread::~thread() +{ + if(_thread) + if(_thread->joinable()) + _thread->detach(); +} + +void thread::spawn(const std::function& function, + const std::function& exception_handler) +{ + std::thread([function, exception_handler](){ + thread t(function, exception_handler); + t.runSync(); + }).detach(); +} + +void thread::run() +{ + _thread = std::make_shared([f=this->_function, + eh=this->_exception_handler]() { + try { + (*f)(); + } catch (std::exception& e) { + (*eh)(e); + } + }); +} + +void thread::wait() +{ + if(_thread) + if(_thread->joinable()) + _thread->join(); +} + +void thread::runSync() +{ + try { + (*_function)(); + } catch(std::exception& e) { + (*_exception_handler)(e); + } +} \ No newline at end of file diff --git a/src/logid/util/thread.h b/src/logid/util/thread.h new file mode 100644 index 0000000..311a1f6 --- /dev/null +++ b/src/logid/util/thread.h @@ -0,0 +1,57 @@ +/* + * 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 . + * + */ +#ifndef LOGID_THREAD_H +#define LOGID_THREAD_H + +#include +#include +#include +#include "ExceptionHandler.h" + +namespace logid +{ + class thread + { + public: + explicit thread(const std::function& function, + const std::function& + exception_handler={[](std::exception& e) + {ExceptionHandler::Default(e);}}); + + ~thread(); + + /* This function spawns a new thread and forgets about it, + * safe equivalent to std::thread{...}.detach() + */ + static void spawn(const std::function& function, + const std::function& + exception_handler={[](std::exception& e) + {ExceptionHandler::Default(e);}}); + + void run(); + void wait(); + void runSync(); + private: + std::shared_ptr> _function; + std::shared_ptr> + _exception_handler; + std::shared_ptr _thread = nullptr; + }; +} + +#endif //LOGID_THREAD_H From 4ba9248038e7d46fc1494e26411afe7c01f1c346 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 24 Jun 2020 17:31:16 -0400 Subject: [PATCH 33/81] Use safe thread class instead of std::thread --- src/logid/backend/dj/Receiver.cpp | 11 ++++++++--- src/logid/backend/dj/Receiver.h | 4 ++-- src/logid/backend/dj/ReceiverMonitor.cpp | 20 ++++++++++++++++---- src/logid/backend/hidpp/Device.cpp | 6 +++++- src/logid/backend/hidpp/Report.cpp | 22 +++++++++++++++++++++- src/logid/backend/hidpp/Report.h | 11 +++++------ src/logid/backend/raw/DeviceMonitor.cpp | 23 +++++++++++++++++------ 7 files changed, 74 insertions(+), 23 deletions(-) diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp index b3d183a..e32a1dd 100644 --- a/src/logid/backend/dj/Receiver.cpp +++ b/src/logid/backend/dj/Receiver.cpp @@ -20,6 +20,7 @@ #include "Report.h" #include "Receiver.h" #include "Error.h" +#include "../../util/thread.h" using namespace logid::backend::dj; using namespace logid::backend; @@ -214,13 +215,14 @@ std::string Receiver::getDeviceName(hidpp::DeviceIndex index) return name; } -hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(hidpp::Report& report) +hidpp::DeviceIndex Receiver::deviceDisconnectionEvent(const hidpp::Report& +report) { assert(report.subId() == DeviceDisconnection); return report.deviceIndex(); } -hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent( +hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(const hidpp::Report &report) { assert(report.subId() == DeviceConnection); @@ -322,7 +324,10 @@ Receiver::ConnectionStatusEvent Receiver::connectionStatusEvent(Report& report) void Receiver::listen() { if(!_raw_device->isListening()) - std::thread{[this]() { this->_raw_device->listen(); }}.detach(); + ///TODO: Kill RawDevice? + thread::spawn({[raw=this->_raw_device]() { + raw->listen(); + }}); if(_raw_device->eventHandlers().find("RECV_HIDPP") == _raw_device->eventHandlers().end()) { diff --git a/src/logid/backend/dj/Receiver.h b/src/logid/backend/dj/Receiver.h index 97aca4a..86430b2 100644 --- a/src/logid/backend/dj/Receiver.h +++ b/src/logid/backend/dj/Receiver.h @@ -140,9 +140,9 @@ namespace dj std::string getDeviceName(hidpp::DeviceIndex index); static hidpp::DeviceIndex deviceDisconnectionEvent( - hidpp::Report& report); + const hidpp::Report& report); static hidpp::DeviceConnectionEvent deviceConnectionEvent( - hidpp::Report& report); + const hidpp::Report& report); void listen(); void stopListening(); diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp index 6447f75..4199178 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/dj/ReceiverMonitor.cpp @@ -17,6 +17,8 @@ */ #include "ReceiverMonitor.h" +#include "../../util/thread.h" +#include "../../util.h" #include #include @@ -55,14 +57,24 @@ void ReceiverMonitor::run() /* Running in a new thread prevents deadlocks since the * receiver may be enumerating. */ - std::thread{[this](hidpp::Report report) { + thread::spawn({[this, report]() { if (report.subId() == Receiver::DeviceConnection) - this->addDevice(this->_receiver->deviceConnectionEvent( - report)); + this->addDevice(this->_receiver->deviceConnectionEvent + (report)); else if (report.subId() == Receiver::DeviceDisconnection) this->removeDevice(this->_receiver-> deviceDisconnectionEvent(report)); - }, report}.detach(); + }}, {[report, path=this->_receiver->rawDevice()->hidrawPath()] + (std::exception& e) { + if(report.subId() == Receiver::DeviceConnection) + log_printf(ERROR, "Failed to add device %d to receiver " + "on %s: %s", report.deviceIndex(), + path.c_str(), e.what()); + else if(report.subId() == Receiver::DeviceDisconnection) + log_printf(ERROR, "Failed to remove device %d from " + "receiver on %s: %s", report.deviceIndex() + ,path.c_str(), e.what()); + }}); }; _receiver->addHidppEventHandler("RECVMON", event_handler); diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 029dda4..110f134 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -18,6 +18,7 @@ #include #include +#include "../../util/thread.h" #include "Device.h" #include "Report.h" #include "../hidpp20/features/Root.h" @@ -182,7 +183,10 @@ Report Device::sendReport(Report& report) void Device::listen() { if(!_raw_device->isListening()) - std::thread{[=]() { _raw_device->listen(); }}.detach(); + ///TODO: Kill RawDevice? + thread::spawn({[raw=this->_raw_device]() { + raw->listen(); + }}); // Pass all HID++ events with device index to this device. std::shared_ptr handler; diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index c1e20df..8cc5c9b 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -199,7 +199,7 @@ void Report::setType(Report::Type type) _data[Offset::Type] = type; } -hidpp::DeviceIndex Report::deviceIndex() +hidpp::DeviceIndex Report::deviceIndex() const { return static_cast(_data[Offset::DeviceIndex]); } @@ -261,6 +261,26 @@ void Report::setAddress(uint8_t address) _data[Offset::Address] = address; } +std::vector::iterator Report::paramBegin() +{ + return _data.begin() + Offset::Parameters; +} + +std::vector::iterator Report::paramEnd() +{ + return _data.end(); +} + +std::vector::const_iterator Report::paramBegin() const +{ + return _data.begin() + Offset::Parameters; +} + +std::vector::const_iterator Report::paramEnd() const +{ + return _data.end(); +} + void Report::setParams(const std::vector& _params) { assert(_params.size() <= _data.size()-HeaderLength); diff --git a/src/logid/backend/hidpp/Report.h b/src/logid/backend/hidpp/Report.h index 7e2c814..e406a6d 100644 --- a/src/logid/backend/hidpp/Report.h +++ b/src/logid/backend/hidpp/Report.h @@ -78,7 +78,7 @@ namespace hidpp Report::Type type() const; void setType(Report::Type type); - logid::backend::hidpp::DeviceIndex deviceIndex(); + logid::backend::hidpp::DeviceIndex deviceIndex() const; void setDeviceIndex(hidpp::DeviceIndex index); uint8_t feature() const; @@ -96,11 +96,10 @@ namespace hidpp uint8_t address() const; void setAddress(uint8_t address); - std::vector::iterator paramBegin() - { - return _data.begin() + Offset::Parameters; - } - std::vector::iterator paramEnd() { return _data.end(); } + std::vector::iterator paramBegin(); + std::vector::iterator paramEnd(); + std::vector::const_iterator paramBegin() const; + std::vector::const_iterator paramEnd() const; void setParams(const std::vector& _params); struct Hidpp10Error diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp index 8d4beda..133b471 100644 --- a/src/logid/backend/raw/DeviceMonitor.cpp +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -17,6 +17,8 @@ */ #include "DeviceMonitor.h" +#include "../../util/thread.h" +#include "../../util.h" #include #include @@ -96,13 +98,19 @@ void DeviceMonitor::run() std::string devnode = udev_device_get_devnode(device); if (action == "add") - std::thread([this](const std::string name) { + thread::spawn([this, name=devnode]() { this->addDevice(name); - }, devnode).detach(); + }, [name=devnode](std::exception& e){ + log_printf(WARN, "Error adding device %s: %s", + name.c_str(), e.what()); + }); else if (action == "remove") - std::thread([this](const std::string name) { + thread::spawn([this, name=devnode]() { this->removeDevice(name); - }, devnode).detach(); + }, [name=devnode](std::exception& e){ + log_printf(WARN, "Error removing device %s: %s", + name.c_str(), e.what()); + }); udev_device_unref (device); } @@ -149,9 +157,12 @@ void DeviceMonitor::enumerate() std::string devnode = udev_device_get_devnode(device); udev_device_unref(device); - std::thread([this](const std::string& name) { + thread::spawn([this, name=devnode]() { this->addDevice(name); - }, devnode).detach(); + }, [name=devnode](std::exception& e){ + log_printf(ERROR, "Error adding device %s: %s", + name.c_str(), e.what()); + }); } udev_enumerate_unref(udev_enum); From 1106133f3c4cf4fffcd1c4bb6109a52aff4730ad Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 24 Jun 2020 18:10:25 -0400 Subject: [PATCH 34/81] Move logger into util/log.h --- src/logid/CMakeLists.txt | 2 +- src/logid/Device.cpp | 10 +-- src/logid/DeviceManager.cpp | 14 ++--- src/logid/Receiver.cpp | 10 +-- src/logid/backend/dj/ReceiverMonitor.cpp | 6 +- src/logid/backend/raw/DeviceMonitor.cpp | 8 +-- src/logid/backend/raw/RawDevice.cpp | 6 +- src/logid/logid.cpp | 16 ++--- src/logid/util.cpp | 26 -------- src/logid/util.h | 16 ----- src/logid/util/ExceptionHandler.cpp | 10 +-- src/logid/util/log.cpp | 80 ++++++++++++++++++++++++ src/logid/util/log.h | 41 ++++++++++++ 13 files changed, 162 insertions(+), 83 deletions(-) create mode 100644 src/logid/util/log.cpp create mode 100644 src/logid/util/log.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index bf7f61a..529c5f7 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -10,7 +10,7 @@ find_package(PkgConfig REQUIRED) add_executable(logid logid.cpp - util.cpp + util/log.cpp DeviceManager.cpp Device.cpp Receiver.cpp diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index f245fc4..4959afb 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -16,7 +16,7 @@ * */ -#include "util.h" +#include "util/log.h" #include "Device.h" using namespace logid; @@ -25,22 +25,22 @@ using namespace logid::backend; Device::Device(std::string path, backend::hidpp::DeviceIndex index) : _hidpp20 (path, index), _path (std::move(path)), _index (index) { - log_printf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index); + logPrintf(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); + logPrintf(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); + logPrintf(INFO, "%s:%d fell asleep.", _path.c_str(), _index); } void Device::wakeup() { - log_printf(INFO, "%s:%d woke up.", _path.c_str(), _index); + logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index); } diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index 7c40687..95448b7 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -21,7 +21,7 @@ #include "DeviceManager.h" #include "Receiver.h" -#include "util.h" +#include "util/log.h" #include "backend/hidpp10/Error.h" #include "backend/dj/Receiver.h" @@ -43,13 +43,13 @@ void DeviceManager::addDevice(std::string path) } catch(hidpp::Device::InvalidDevice &e) { // Ignore defaultExists = false; } catch(std::system_error &e) { - log_printf(WARN, "I/O error on %s: %s, skipping device.", + logPrintf(WARN, "I/O error on %s: %s, skipping device.", path.c_str(), e.what()); return; } if(isReceiver) { - log_printf(INFO, "Detected receiver at %s", path.c_str()); + logPrintf(INFO, "Detected receiver at %s", path.c_str()); auto receiver = std::make_shared(path); receiver->run(); _receivers.emplace(path, receiver); @@ -69,13 +69,13 @@ void DeviceManager::addDevice(std::string path) if(e.code() != hidpp10::Error::UnknownDevice) throw; else - log_printf(WARN, + logPrintf(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 - log_printf(WARN, "I/O error on %s: %s", path.c_str(), + logPrintf(WARN, "I/O error on %s: %s", path.c_str(), e.what()); } } @@ -88,12 +88,12 @@ void DeviceManager::removeDevice(std::string path) if(receiver != _receivers.end()) { _receivers.erase(receiver); - log_printf(INFO, "Receiver on %s disconnected", path.c_str()); + logPrintf(INFO, "Receiver on %s disconnected", path.c_str()); } else { auto device = _devices.find(path); if(device != _devices.find(path)) { _devices.erase(device); - log_printf(INFO, "Device on %s disconnected", path.c_str()); + logPrintf(INFO, "Device on %s disconnected", path.c_str()); } } } diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index de28907..365cb20 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -18,7 +18,7 @@ #include #include "Receiver.h" -#include "util.h" +#include "util/log.h" #include "backend/hidpp10/Error.h" #include "backend/hidpp20/Error.h" @@ -27,7 +27,7 @@ using namespace logid::backend; Receiver::Receiver(std::string path) : dj::ReceiverMonitor(path), _path (path) { - log_printf(DEBUG, "logid::Receiver created on %s", path.c_str()); + logPrintf(DEBUG, "logid::Receiver created on %s", path.c_str()); } void Receiver::addDevice(hidpp::DeviceConnectionEvent event) @@ -50,7 +50,7 @@ void Receiver::addDevice(hidpp::DeviceConnectionEvent event) auto version = hidpp_device.version(); if(std::get<0>(version) < 2) { - log_printf(INFO, "Unsupported HID++ 1.0 device on %s:%d connected.", + logPrintf(INFO, "Unsupported HID++ 1.0 device on %s:%d connected.", _path.c_str(), event.index); return; } @@ -61,11 +61,11 @@ void Receiver::addDevice(hidpp::DeviceConnectionEvent event) _devices.emplace(event.index, device); } catch(hidpp10::Error &e) { - log_printf(ERROR, + logPrintf(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 " + logPrintf(ERROR, "Caught HID++ 2.0 error while trying to initialize " "%s:%d: %s", _path.c_str(), event.index, e.what()); } } diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp index 4199178..a983171 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/dj/ReceiverMonitor.cpp @@ -18,7 +18,7 @@ #include "ReceiverMonitor.h" #include "../../util/thread.h" -#include "../../util.h" +#include "../../util/log.h" #include #include @@ -67,11 +67,11 @@ void ReceiverMonitor::run() }}, {[report, path=this->_receiver->rawDevice()->hidrawPath()] (std::exception& e) { if(report.subId() == Receiver::DeviceConnection) - log_printf(ERROR, "Failed to add device %d to receiver " + logPrintf(ERROR, "Failed to add device %d to receiver " "on %s: %s", report.deviceIndex(), path.c_str(), e.what()); else if(report.subId() == Receiver::DeviceDisconnection) - log_printf(ERROR, "Failed to remove device %d from " + logPrintf(ERROR, "Failed to remove device %d from " "receiver on %s: %s", report.deviceIndex() ,path.c_str(), e.what()); }}); diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp index 133b471..fcbc6db 100644 --- a/src/logid/backend/raw/DeviceMonitor.cpp +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -18,7 +18,7 @@ #include "DeviceMonitor.h" #include "../../util/thread.h" -#include "../../util.h" +#include "../../util/log.h" #include #include @@ -101,14 +101,14 @@ void DeviceMonitor::run() thread::spawn([this, name=devnode]() { this->addDevice(name); }, [name=devnode](std::exception& e){ - log_printf(WARN, "Error adding device %s: %s", + logPrintf(WARN, "Error adding device %s: %s", name.c_str(), e.what()); }); else if (action == "remove") thread::spawn([this, name=devnode]() { this->removeDevice(name); }, [name=devnode](std::exception& e){ - log_printf(WARN, "Error removing device %s: %s", + logPrintf(WARN, "Error removing device %s: %s", name.c_str(), e.what()); }); @@ -160,7 +160,7 @@ void DeviceMonitor::enumerate() thread::spawn([this, name=devnode]() { this->addDevice(name); }, [name=devnode](std::exception& e){ - log_printf(ERROR, "Error adding device %s: %s", + logPrintf(ERROR, "Error adding device %s: %s", name.c_str(), e.what()); }); } diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index a0af1c2..9719f49 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -20,7 +20,7 @@ #include "../Error.h" #include "../hidpp/defs.h" #include "../dj/defs.h" -#include "../../util.h" +#include "../../util/log.h" #include "../hidpp/Report.h" #include @@ -230,7 +230,7 @@ std::vector RawDevice::_respondToReport int RawDevice::_sendReport(const std::vector& report) { std::lock_guard lock(_dev_io); - if(logid::global_verbosity == LogLevel::RAWREPORT) { + if(logid::global_loglevel == LogLevel::RAWREPORT) { printf("[RAWREPORT] %s OUT: ", _path.c_str()); for(auto &i : report) printf("%02x ", i); @@ -295,7 +295,7 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng if(0 == ret) throw backend::TimeoutError(); - if(logid::global_verbosity == LogLevel::RAWREPORT) { + if(logid::global_loglevel == LogLevel::RAWREPORT) { printf("[RAWREPORT] %s IN: ", _path.c_str()); for(auto &i : report) printf("%02x ", i); diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 077b841..2d885f4 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -21,7 +21,7 @@ #include #include -#include "util.h" +#include "util/log.h" #include "DeviceManager.h" #include "logid.h" @@ -37,7 +37,7 @@ using namespace logid; std::string config_file = DEFAULT_CONFIG_FILE; -LogLevel logid::global_verbosity = INFO; +LogLevel logid::global_loglevel = INFO; // Configuration* logid::global_config; DeviceManager* logid::finder; @@ -97,25 +97,25 @@ void readCliOptions(int argc, char** argv) option = Option::Help; break; default: - log_printf(WARN, "%s is not a valid option, ignoring.", + logPrintf(WARN, "%s is not a valid option, ignoring.", argv[i]); } switch(option) { case Option::Verbose: { if (++i >= argc) { - global_verbosity = DEBUG; // Assume debug verbosity + global_loglevel = DEBUG; // Assume debug verbosity break; } std::string loglevel = argv[i]; try { - global_verbosity = stringToLogLevel(argv[i]); + global_loglevel = toLogLevel(argv[i]); } catch (std::invalid_argument &e) { if (argv[i][0] == '-') { - global_verbosity = DEBUG; // Assume debug verbosity + global_loglevel = DEBUG; // Assume debug verbosity i--; // Go back to last argument to continue loop. } else { - log_printf(WARN, e.what()); + logPrintf(WARN, e.what()); printf("Valid verbosity levels are: Debug, Info, " "Warn/Warning, or Error.\n"); exit(EXIT_FAILURE); @@ -125,7 +125,7 @@ void readCliOptions(int argc, char** argv) } case Option::Config: { if (++i >= argc) { - log_printf(ERROR, "Config file is not specified."); + logPrintf(ERROR, "Config file is not specified."); exit(EXIT_FAILURE); } config_file = argv[i]; diff --git a/src/logid/util.cpp b/src/logid/util.cpp index ead4def..03383d4 100644 --- a/src/logid/util.cpp +++ b/src/logid/util.cpp @@ -28,32 +28,6 @@ using namespace logid; -void logid::log_printf(LogLevel level, const char* format, ...) -{ - if(global_verbosity > level) return; - - va_list vargs; - va_start(vargs, format); - - FILE* stream = stdout; - if(level == ERROR || level == WARN) stream = stderr; - - fprintf(stream, "[%s] ", level_prefix(level)); - vfprintf(stream, format, vargs); - fprintf(stream, "\n"); -} - -const char* logid::level_prefix(LogLevel level) -{ - if(level == RAWREPORT) return "RAWREPORT"; - if(level == DEBUG) return "DEBUG"; - if(level == INFO) return "INFO" ; - if(level == WARN) return "WARN"; - if(level == ERROR) return "ERROR"; - - return "DEBUG"; -} - /* Direction logid::getDirection(int x, int y) { diff --git a/src/logid/util.h b/src/logid/util.h index b3f7c2d..24ed02b 100644 --- a/src/logid/util.h +++ b/src/logid/util.h @@ -23,28 +23,12 @@ namespace logid { - enum LogLevel - { - RAWREPORT, - DEBUG, - INFO, - WARN, - ERROR - }; - - extern LogLevel global_verbosity; - - void log_printf(LogLevel level, const char* format, ...); - - const char* level_prefix(LogLevel level); - /* Direction getDirection(int x, int y); Direction stringToDirection(std::string s); GestureMode stringToGestureMode(std::string s); Action stringToAction(std::string s); */ - LogLevel stringToLogLevel(std::string s); } #endif //LOGID_UTIL_H \ No newline at end of file diff --git a/src/logid/util/ExceptionHandler.cpp b/src/logid/util/ExceptionHandler.cpp index 3019806..859783f 100644 --- a/src/logid/util/ExceptionHandler.cpp +++ b/src/logid/util/ExceptionHandler.cpp @@ -16,7 +16,7 @@ * */ #include -#include "../util.h" +#include "log.h" #include "ExceptionHandler.h" #include "../backend/hidpp10/Error.h" #include "../backend/hidpp20/Error.h" @@ -28,16 +28,16 @@ void ExceptionHandler::Default(std::exception& error) try { throw error; } catch(backend::hidpp10::Error& e) { - log_printf(WARN, "HID++ 1.0 error ignored on detached thread: %s", + logPrintf(WARN, "HID++ 1.0 error ignored on detached thread: %s", error.what()); } catch(backend::hidpp20::Error& e) { - log_printf(WARN, "HID++ 2.0 error ignored on detached thread: %s", + logPrintf(WARN, "HID++ 2.0 error ignored on detached thread: %s", error.what()); } catch(std::system_error& e) { - log_printf(WARN, "System error ignored on detached thread: %s", + logPrintf(WARN, "System error ignored on detached thread: %s", error.what()); } catch(std::exception& e) { - log_printf(WARN, "Error ignored on detached thread: %s", + logPrintf(WARN, "Error ignored on detached thread: %s", error.what()); } } \ No newline at end of file diff --git a/src/logid/util/log.cpp b/src/logid/util/log.cpp new file mode 100644 index 0000000..af7bfe4 --- /dev/null +++ b/src/logid/util/log.cpp @@ -0,0 +1,80 @@ +/* + * 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 . + * + */ + +#include +#include +#include +#include +#include +#include "log.h" + +using namespace logid; + +void logid::logPrintf(LogLevel level, const char* format, ...) +{ + if(global_loglevel > level) return; + + va_list vargs; + va_start(vargs, format); + + FILE* stream = stdout; + if(level == ERROR || level == WARN) + stream = stderr; + + fprintf(stream, "[%s] ", levelPrefix(level)); + vfprintf(stream, format, vargs); + fprintf(stream, "\n"); +} + +const char* logid::levelPrefix(LogLevel level) +{ + switch(level) { + case RAWREPORT: + return "RAWREPORT"; + case DEBUG: + return "DEBUG"; + case INFO: + return "INFO"; + case WARN: + return "WARN"; + case ERROR: + return "ERROR"; + default: + return "UNKNOWN"; + } +} + + +LogLevel logid::toLogLevel(std::string s) +{ + std::string original_str = s; + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + + if(s == "rawreport") + return RAWREPORT; + if(s == "debug") + return DEBUG; + if(s == "info") + return INFO; + if(s == "warn" || s == "warning") + return WARN; + if(s == "error") + return ERROR; + + throw std::invalid_argument(original_str + " is an invalid log level."); +} \ No newline at end of file diff --git a/src/logid/util/log.h b/src/logid/util/log.h new file mode 100644 index 0000000..fad0b66 --- /dev/null +++ b/src/logid/util/log.h @@ -0,0 +1,41 @@ +/* + * 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 . + * + */ +#ifndef LOGID_LOG_H +#define LOGID_LOG_H + +#include + +namespace logid +{ + enum LogLevel + { + RAWREPORT, + DEBUG, + INFO, + WARN, + ERROR + }; + + extern LogLevel global_loglevel; + + void logPrintf(LogLevel level, const char *format, ...); + const char *levelPrefix(LogLevel level); + LogLevel toLogLevel(std::string s); +} + +#endif //LOGID_LOG_H From 5f76ccc4ac9fa673b2851c8a5310c5cb7354deed Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 24 Jun 2020 20:17:32 -0400 Subject: [PATCH 35/81] Implement AdjustableDPI feature --- src/logid/CMakeLists.txt | 1 + .../hidpp20/features/AdjustableDPI.cpp | 88 +++++++++++++++++++ .../backend/hidpp20/features/AdjustableDPI.h | 60 +++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 src/logid/backend/hidpp20/features/AdjustableDPI.cpp create mode 100644 src/logid/backend/hidpp20/features/AdjustableDPI.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 529c5f7..653d2b9 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -32,6 +32,7 @@ add_executable(logid backend/hidpp20/features/FeatureSet.cpp backend/hidpp20/features/DeviceName.cpp backend/hidpp20/features/Reset.cpp + backend/hidpp20/features/AdjustableDPI.cpp backend/dj/Report.cpp util/mutex_queue.h util/thread.cpp diff --git a/src/logid/backend/hidpp20/features/AdjustableDPI.cpp b/src/logid/backend/hidpp20/features/AdjustableDPI.cpp new file mode 100644 index 0000000..722d6cb --- /dev/null +++ b/src/logid/backend/hidpp20/features/AdjustableDPI.cpp @@ -0,0 +1,88 @@ +/* + * 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 . + * + */ +#include "AdjustableDPI.h" + +using namespace logid::backend::hidpp20; + +AdjustableDPI::AdjustableDPI(Device* dev) : Feature(dev, ID) +{ +} + +uint8_t AdjustableDPI::getSensorCount() +{ + std::vector params(0); + auto response = callFunction(GetSensorCount, params); + return response[0]; +} + +AdjustableDPI::SensorDPIList AdjustableDPI::getSensorDPIList(uint8_t sensor) +{ + SensorDPIList dpi_list{}; + std::vector params(1); + params[0] = sensor; + auto response = callFunction(GetSensorDPIList, params); + + dpi_list.dpiStep = false; + for(std::size_t i = 1; i < response.size(); i+=2) { + uint16_t dpi = response[i + 1]; + dpi |= (response[i] << 8); + if(!dpi) + break; + if(dpi >= 0xe000) { + dpi_list.isRange = true; + dpi_list.dpiStep = dpi - 0xe000; + } else { + dpi_list.dpis.push_back(dpi); + } + } + + return dpi_list; +} + +uint16_t AdjustableDPI::getDefaultSensorDPI(uint8_t sensor) +{ + std::vector params(1); + params[0] = sensor; + auto response = callFunction(GetSensorDPI, params); + + uint16_t default_dpi = response[4]; + default_dpi |= (response[3] << 8); + + return default_dpi; +} + +uint16_t AdjustableDPI::getSensorDPI(uint8_t sensor) +{ + std::vector params(1); + params[0] = sensor; + auto response = callFunction(GetSensorDPI, params); + + uint16_t dpi = response[2]; + dpi |= (response[1] << 8); + + return dpi; +} + +void AdjustableDPI::setSensorDPI(uint8_t sensor, uint16_t dpi) +{ + std::vector params(3); + params[0] = sensor; + params[1] = (dpi >> 8); + params[2] = (dpi & 0xFF); + callFunction(SetSensorDPI, params); +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/AdjustableDPI.h b/src/logid/backend/hidpp20/features/AdjustableDPI.h new file mode 100644 index 0000000..9a76d0f --- /dev/null +++ b/src/logid/backend/hidpp20/features/AdjustableDPI.h @@ -0,0 +1,60 @@ +/* + * 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 . + * + */ +#ifndef LOGID_BACKEND_HIDPP20_FEATURE_ADJUSTABLEDPI_H +#define LOGID_BACKEND_HIDPP20_FEATURE_ADJUSTABLEDPI_H + +#include "../feature_defs.h" +#include "../Feature.h" + +namespace logid { +namespace backend { +namespace hidpp20 +{ + class AdjustableDPI : public Feature + { + public: + static const uint16_t ID = FeatureID::ADJUSTABLE_DPI; + virtual uint16_t getID() { return ID; } + + enum Function { + GetSensorCount = 0, + GetSensorDPIList = 1, + GetSensorDPI = 2, + SetSensorDPI = 3 + }; + + AdjustableDPI(Device* dev); + + uint8_t getSensorCount(); + + struct SensorDPIList + { + std::vector dpis; + bool isRange; + uint16_t dpiStep; + }; + SensorDPIList getSensorDPIList(uint8_t sensor); + + uint16_t getDefaultSensorDPI(uint8_t sensor); + uint16_t getSensorDPI(uint8_t sensor); + + void setSensorDPI(uint8_t sensor, uint16_t dpi); + }; +}}} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_ADJUSTABLEDPI_H From 181be50f885314b04f7419df7c9913b198376590 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 24 Jun 2020 22:27:19 -0400 Subject: [PATCH 36/81] Use unique pointer for global device manager Changed from raw pointer to a unique_ptr, also renamed from finder to device_manager --- src/logid/DeviceManager.h | 2 +- src/logid/logid.cpp | 12 ++++++------ src/logid/logid.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/logid/DeviceManager.h b/src/logid/DeviceManager.h index 50f7c31..6305963 100644 --- a/src/logid/DeviceManager.h +++ b/src/logid/DeviceManager.h @@ -44,7 +44,7 @@ namespace logid std::map> _receivers; }; - extern DeviceManager* finder; + extern std::unique_ptr device_manager; } #endif //LOGID_DEVICEMANAGER_H \ No newline at end of file diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 2d885f4..35274e1 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -39,10 +39,10 @@ std::string config_file = DEFAULT_CONFIG_FILE; LogLevel logid::global_loglevel = INFO; // Configuration* logid::global_config; -DeviceManager* logid::finder; +std::unique_ptr logid::device_manager; bool logid::kill_logid = false; -std::mutex logid::finder_reloading; +std::mutex logid::device_manager_reload; enum class Option { @@ -172,12 +172,12 @@ int main(int argc, char** argv) */ // Scan devices, create listeners, handlers, etc. - finder = new DeviceManager(); + device_manager = std::make_unique(); while(!kill_logid) { - finder_reloading.lock(); - finder_reloading.unlock(); - finder->run(); + device_manager_reload.lock(); + device_manager_reload.unlock(); + device_manager->run(); } return EXIT_SUCCESS; diff --git a/src/logid/logid.h b/src/logid/logid.h index c73b577..850721f 100644 --- a/src/logid/logid.h +++ b/src/logid/logid.h @@ -26,7 +26,7 @@ namespace logid // void reload(); extern bool kill_logid; - extern std::mutex finder_reloading; + extern std::mutex device_manager_reload; } #endif //LOGID_LOGID_H \ No newline at end of file From f6b93b94afe29e7ecff0a5f9b77d68c50bfec4ad Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 2 Jul 2020 00:38:40 -0400 Subject: [PATCH 37/81] Split Configuration into several classes Each feature should handle its own configuration. --- src/logid/CMakeLists.txt | 1 + src/logid/Configuration.cpp | 520 +++-------------------------- src/logid/Configuration.h | 47 +-- src/logid/Device.cpp | 41 ++- src/logid/Device.h | 21 ++ src/logid/backend/hidpp/Device.cpp | 10 + src/logid/backend/hidpp/Device.h | 3 + src/logid/features/DeviceFeature.h | 17 + src/logid/logid.cpp | 13 +- 9 files changed, 154 insertions(+), 519 deletions(-) diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 653d2b9..440f4f2 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(logid DeviceManager.cpp Device.cpp Receiver.cpp + Configuration.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index 6e30e31..9bb1e14 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -16,521 +16,79 @@ * */ -#include +#include #include #include -#include -#include -#include -#include -#include -#include #include "Configuration.h" -#include "util.h" +#include "util/log.h" using namespace logid; using namespace libconfig; -Configuration::Configuration(const char *config_file) +Configuration::Configuration(const std::string& config_file) { - //Read config file - try - { - cfg.readFile(config_file); - } - catch(const FileIOException &e) - { - log_printf(ERROR, "I/O Error while reading %s: %s", config_file, e.what()); + try { + _config.readFile(config_file.c_str()); + } catch(const FileIOException &e) { + logPrintf(ERROR, "I/O Error while reading %s: %s", config_file.c_str(), + e.what()); + throw e; + } catch(const ParseException &e) { + logPrintf(ERROR, "Parse error in %s, line %d: %s", e.getFile(), + e.getLine(), e.getError()); throw e; } - catch(const ParseException &e) - { - log_printf(ERROR, "Parse error in %s, line %d: %s", e.getFile(), e.getLine(), e.getError()); - throw e; - } - const Setting &root = cfg.getRoot(); - try - { - auto& _blacklist = root.lookup("blacklist"); - if(_blacklist.isArray() || _blacklist.isList()) - { - int len = _blacklist.getLength(); - for(int i = 0; i < len; i++) - { - if(!_blacklist[i].isNumber()) { - log_printf(WARN, "Line %d: blacklist must only contain " - "PIDs", _blacklist[i].getSourceLine()); - if(_blacklist.isArray()) - break; - if(_blacklist.isList()) - continue; - } - blacklist.push_back((int)_blacklist[i]); - } - } - else - { - log_printf(WARN, "Line %d: blacklist must be an array or list, " - "ignnoring.", _blacklist.getSourceLine()); - } - } + const Setting &root = _config.getRoot(); + Setting* devices; + + try { devices = &root["devices"]; } catch(const SettingNotFoundException &e) { - } - - Setting* _devices; - - try { _devices = &root["devices"]; } - catch(const SettingNotFoundException &e) - { - log_printf(WARN, "No devices listed in config file."); + logPrintf(WARN, "No devices listed in config file."); return; } - for(int i = 0; i < _devices->getLength(); i++) + for(int i = 0; i < devices->getLength(); i++) { - const Setting &device = (*_devices)[i]; + const Setting &device = (*devices)[i]; std::string name; - try - { - if(!device.lookupValue("name", name)) - { - log_printf(WARN, "Line %d: 'name' must be a string, skipping device.", device["name"].getSourceLine()); + try { + if(!device.lookupValue("name", name)) { + logPrintf(WARN, "Line %d: 'name' must be a string, skipping " + "device.", device["name"].getSourceLine()); continue; } - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "Line %d: Missing 'name' field, skipping device.", device.getSourceLine()); + } catch(SettingNotFoundException &e) { + logPrintf(WARN, "Line %d: Missing 'name' field, skipping device." + , device.getSourceLine()); continue; } - devices.insert({name, new DeviceConfig(device)}); + _device_paths.insert({name, device.getPath()}); } } -DeviceConfig::DeviceConfig(const libconfig::Setting &root) +libconfig::Setting& Configuration::getSetting(std::string path) { - try - { - int d; - if(!root.lookupValue("dpi", d)) - throw SettingTypeException(root["dpi"]); - dpi = new int(d); - } - catch(const SettingNotFoundException &e) { } - catch(const SettingTypeException &e) - { - log_printf(WARN, "Line %d: DPI must me an integer; not setting.", root["dpi"].getSourceLine()); - } - - try - { - const Setting& hr = root["hiresscroll"]; - uint8_t hss = 0; - try - { - bool b; - if(!hr.lookupValue("hires", b)) - throw SettingTypeException(root["hires"]); - if(b) hss |= HIDPP20::IHiresScroll::HiRes; - } - catch(SettingNotFoundException &e) { } - catch(SettingTypeException &e) - { - log_printf(INFO, "Line %d: hires field must be a boolean", hr["hires"].getSourceLine()); - } - - try - { - bool b; - if(!hr.lookupValue("invert", b)) - throw SettingTypeException(root["invert"]); - if(b) hss |= HIDPP20::IHiresScroll::Inverted; - } - catch(SettingNotFoundException &e) { } - catch(SettingTypeException &e) - { - log_printf(INFO, "Line %d: invert field must be a boolean", hr["invert"].getSourceLine()); - } - - try - { - bool b; - if(!hr.lookupValue("target", b)) - throw SettingTypeException(root["target"]); - if(b) hss |= HIDPP20::IHiresScroll::Target; - } - catch(SettingNotFoundException &e) { } - catch(SettingTypeException &e) - { - log_printf(INFO, "Line %d: target field must be a boolean", hr["target"].getSourceLine()); - } - - hiresscroll = new uint8_t(hss); - } - catch(const SettingNotFoundException &e) - { - log_printf(INFO, "Missing hiresscroll option, not setting."); - } - catch(const SettingTypeException &e) - { - log_printf(WARN, "Line %d: hiresscroll should be an object", root["hiresscroll"].getSourceLine()); - } - - try - { - const Setting& ss = root["smartshift"]; - smartshift = new HIDPP20::ISmartShift::SmartshiftStatus {}; - bool on; - int threshold; - try - { - if (ss.lookupValue("on", on)) smartshift->Active = new bool(on); - else log_printf(WARN, "Line %d: on field must be a boolean", ss["on"].getSourceLine()); - } - catch(const SettingNotFoundException &e) { } - - try - { - if (ss.lookupValue("threshold", threshold)) - { - if(threshold < 0) - { - threshold = 1; - log_printf(INFO, "Smartshift threshold must be > 0 or < 100, setting to 1."); - } - if(threshold >= 100) - { - threshold = 99; - log_printf(INFO, "Smartshift threshold must be > 0 or < 100, setting to 99."); - } - smartshift->AutoDisengage = new uint8_t(threshold); - smartshift->DefaultAutoDisengage = new uint8_t(threshold); - } - else log_printf(WARN, "Line %d: threshold must be an integer", ss["threshold"].getSourceLine()); - } - catch(const SettingNotFoundException &e) { } - } - catch(const SettingNotFoundException &e) { } - catch(const SettingTypeException &e) - { - log_printf(WARN, "Line %d: smartshift field must be an object", root["smartshift"].getSourceLine()); - } - - Setting* buttons; - try - { - buttons = &root["buttons"]; - } - catch(const SettingNotFoundException &e) - { - log_printf(WARN, "No button configuration found, reverting to null config."); - new std::map(); - return; - } - - for(int i = 0; i < buttons->getLength(); i++) - { - const Setting &button = (*buttons)[i]; - - int cid; - try { button.lookupValue("cid", cid); } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "Entry on line %d is missing a cid", button.getSourceLine()); - continue; - } - - if(actions.find(cid) != actions.end()) - { - log_printf(WARN, "Duplicate entries for cid 0x%x, skipping entry on line %d", cid, button.getSourceLine()); - continue; - } - - Setting* action_config; - try { action_config = &button["action"]; } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "cid 0x%x is missing an action, not diverting!", cid); - continue; - } - - Action action_type; - try - { - std::string action_type_str; - action_config->lookupValue("type", action_type_str); - action_type = stringToAction(action_type_str); - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "cid 0x%x is missing an action type, not diverting!", cid); - continue; - } - catch(std::invalid_argument &e) - { - log_printf(WARN, "Line %d: %s", (*action_config)["type"].getSourceLine(), e.what()); - continue; - } - - try { actions.insert({cid, parse_action(action_type, action_config)}); } - catch(std::exception &e) { log_printf(ERROR, "%s", e.what()); } - } + return _config.lookup(path); } -ButtonAction* logid::parse_action(Action type, const Setting* action_config, bool is_gesture) +std::string Configuration::getDevice(std::string name) { - if(type == Action::None) return new NoAction(); - if(type == Action::Keypress) - { - std::vector keys; - try - { - const Setting &keys_config = (*action_config)["keys"]; - for (int i = 0; i < keys_config.getLength(); i++) - { - int keycode = libevdev_event_code_from_name(EV_KEY, keys_config[i]); - if(keycode == -1) - { - const char* keyname = keys_config[i]; - log_printf(WARN, "%s is not a valid keycode, skipping", keyname); - } - else keys.push_back(keycode); - } - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "Expected keys parameter on line %d", action_config->getSourceLine()); - } - - return new KeyAction(keys); - } - else if(type == Action::Gestures) - { - if(is_gesture) - { - log_printf(WARN, "Line %d: Recursive gesture, defaulting to no action.", action_config->getSourceLine()); - return new NoAction(); - } - std::map gestures; - - Setting* gestures_config; - - try { gestures_config = &(*action_config)["gestures"]; } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "No gestures parameter for line %d, skipping", action_config->getSourceLine()); - throw e; - } - - for(int i = 0; i < gestures_config->getLength(); i++) - { - const Setting &gesture_config = (*gestures_config)[i]; - - std::string direction_str; - Direction direction; - try - { - gesture_config.lookupValue("direction", direction_str); - direction = stringToDirection(direction_str); - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "No direction set on line %d", gesture_config.getSourceLine()); - continue; - } - catch(std::invalid_argument &e) - { - log_printf(WARN, "Line %d: %s", gesture_config["direction"].getSourceLine(), e.what()); - continue; - } - - if(gestures.find(direction) != gestures.end()) - { - log_printf(WARN, "Entry on line %d is a duplicate, skipping...", gesture_config["direction"].getSourceLine()); - continue; - } - - GestureMode mode; - try - { - std::string mode_str; - gesture_config.lookupValue("mode", mode_str); - mode = stringToGestureMode(mode_str); - } - catch (SettingNotFoundException &e) - { - log_printf(INFO, "Gesture mode on line %d not found, defaulting to OnRelease", gesture_config.getSourceLine()); - mode = GestureMode::OnRelease; - } - - if(mode == GestureMode::NoPress) - { - gestures.insert({direction, new Gesture(new NoAction(), mode)}); - continue; - } - - if(mode == GestureMode::Axis) - { - Gesture::axis_info axis; - try - { - std::string axis_str; - if(!gesture_config.lookupValue("axis", axis_str)) - throw SettingTypeException(gesture_config["axis"]); - axis.code = libevdev_event_code_from_name(EV_REL, axis_str.c_str()); - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "Line %d: No axis found, defaulting to no action.", gesture_config.getSourceLine()); - gestures.insert({direction, new Gesture(new NoAction(), GestureMode::NoPress)}); - continue; - } - catch(SettingTypeException &e) - { - log_printf(WARN, "Line %d: Axis must be a string (e.g. 'REL_WHEEL')", gesture_config["axis"].getSourceLine()); - gestures.insert({direction, new Gesture(new NoAction(), GestureMode::NoPress)}); - continue; - } - - axis.multiplier = 1; - try - { - if(!gesture_config.lookupValue("axis_multiplier", axis.multiplier)) - { - int im = 1; - if(!gesture_config.lookupValue("axis_multiplier", im)) - throw SettingTypeException(gesture_config["axis_multiplier"]); - axis.multiplier = (float)im; - } - } - catch(SettingNotFoundException &e) { } - catch(SettingTypeException &e) - { - log_printf(WARN, "Line %d: axis_multiplier must be a float/integer", gesture_config["axis_multiplier"].getSourceLine()); - continue; - } - - gestures.insert({direction, new Gesture(new NoAction(), GestureMode::Axis, &axis)}); - continue; - } - - Setting* g_action; - try { g_action = &gesture_config["action"]; } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "No action set for %s", direction_str.c_str()); - continue; - } - - Action g_type; - try - { - std::string type_str; - g_action->lookupValue("type", type_str); - g_type = stringToAction(type_str); - } - catch(SettingNotFoundException &e) - { - log_printf(INFO, "Missing an action type on line %d, skipping", g_action->getSourceLine()); - continue; - } - catch(std::invalid_argument &e) - { - log_printf(WARN, "Line %d: %s", (*g_action)["type"].getSourceLine(), e.what()); - continue; - } - - ButtonAction* ba; - - try { ba = parse_action(g_type, g_action, true); } - catch(std::exception &e) { continue; } - - if(mode == GestureMode::OnFewPixels) - { - try - { - int pp; - if(!gesture_config.lookupValue("pixels", pp)) - throw SettingTypeException(gesture_config["pixels"]); - gestures.insert({direction, new Gesture(ba, mode, &pp)}); - } - catch(SettingNotFoundException &e) - { - log_printf(WARN, "Line %d: OnFewPixels requires a 'pixels' field.", gesture_config.getSourceLine()); - } - catch(SettingTypeException &e) - { - log_printf(WARN, "Line %d: pixels must be an integer", gesture_config["pixels"].getSourceLine()); - continue; - } - } - else gestures.insert({direction, new Gesture(ba, mode)}); - } - - return new GestureAction(gestures); - } - else if(type == Action::ToggleSmartshift) return new SmartshiftAction(); - else if(type == Action::ToggleHiresScroll) return new HiresScrollAction(); - else if(type == Action::CycleDPI) - { - std::vector dpis; - try - { - const Setting &keys_config = (*action_config)["dpis"]; - for (int i = 0; i < keys_config.getLength(); i++) - { - dpis.push_back((int)keys_config[i]); - } - } - catch(SettingNotFoundException &e) - { - log_printf(ERROR, "Line %d: CycleDPI action is missing 'dpis' field, defaulting to NoAction.", action_config->getSourceLine()); - } - - return new CycleDPIAction(dpis); - } - else if(type == Action::ChangeDPI) - { - int inc; - try - { - action_config->lookupValue("inc", inc); - } - catch(SettingNotFoundException &e) - { - log_printf(ERROR, "Line %d: ChangeDPI action is missing an 'inc' field, defaulting to NoAction.",action_config->getSourceLine()); - return new NoAction(); - } - - return new ChangeDPIAction(inc); - } - - log_printf(ERROR, "This shouldn't have happened. Unhandled action type? Defaulting to NoAction"); - return new NoAction(); + auto it = _device_paths.find(name); + if(it == _device_paths.end()) + throw DeviceNotFound(name); + else + return it->second; } -DeviceConfig::DeviceConfig(DeviceConfig* dc, Device* dev) : baseConfig (false) +Configuration::DeviceNotFound::DeviceNotFound(std::string name) : + _name (std::move(name)) { - dpi = dc->dpi; - smartshift = dc->smartshift; - hiresscroll = dc->hiresscroll; - for(auto it : dc->actions) - actions.insert( { it.first, it.second->copy(dev) } ); } -DeviceConfig::DeviceConfig() +const char * Configuration::DeviceNotFound::what() { - dpi = nullptr; - hiresscroll = nullptr; - smartshift = nullptr; - actions = {}; -} - -DeviceConfig::~DeviceConfig() -{ - for(auto it : this->actions) - delete(it.second); + return _name.c_str(); } diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index 53a3d6a..b9d5200 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -21,43 +21,32 @@ #include #include -#include -#include "Actions.h" +#include namespace logid { - class DeviceConfig; - class ButtonAction; - enum class Action; - - class DeviceConfig - { - public: - DeviceConfig(); - ~DeviceConfig(); - DeviceConfig(DeviceConfig* dc, Device* dev); - DeviceConfig(const libconfig::Setting& root); - const int* dpi = nullptr; - HIDPP20::ISmartShift::SmartshiftStatus* smartshift = nullptr; - const uint8_t* hiresscroll = nullptr; - std::map actions; - const bool baseConfig = true; - }; - class Configuration { public: - Configuration(const char* config_file); - Configuration() {} - std::map devices; - std::vector blacklist; + explicit Configuration(const std::string& config_file); + Configuration() = default; + libconfig::Setting& getSetting(std::string path); + std::string getDevice(std::string name); + + class DeviceNotFound : public std::exception + { + public: + explicit DeviceNotFound(std::string name); + virtual const char* what(); + private: + std::string _name; + }; private: - libconfig::Config cfg; + std::map _device_paths; + libconfig::Config _config; }; - ButtonAction* parse_action(Action action, const libconfig::Setting* action_config, bool is_gesture=false); - - extern Configuration* global_config; + extern std::shared_ptr global_config; } -#endif //LOGID_CONFIGURATION_H \ No newline at end of file +#endif //LOGID_CONFIGURATION_H diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 4959afb..da8746a 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -23,16 +23,28 @@ using namespace logid; using namespace logid::backend; Device::Device(std::string path, backend::hidpp::DeviceIndex index) : - _hidpp20 (path, index), _path (std::move(path)), _index (index) + _hidpp20 (path, index), _path (std::move(path)), _index (index), + _config (global_config, this) { - logPrintf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index); + ///TODO: Initialize features } Device::Device(const std::shared_ptr& raw_device, hidpp::DeviceIndex index) : _hidpp20(raw_device, index), _path - (raw_device->hidrawPath()), _index (index) + (raw_device->hidrawPath()), _index (index), + _config (global_config, this) { - logPrintf(DEBUG, "logid::Device created on %s:%d", _path.c_str(), _index); + ///TODO: Initialize features +} + +std::string Device::name() +{ + return _hidpp20.name(); +} + +uint16_t Device::pid() +{ + return _hidpp20.pid(); } void Device::sleep() @@ -44,3 +56,24 @@ void Device::wakeup() { logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index); } + +DeviceConfig& Device::config() +{ + return _config; +} + +DeviceConfig::DeviceConfig(std::shared_ptr config, Device* + device) : _device (device), _config (config) +{ + try { + _root_setting = config->getDevice(device->name()); + } catch(Configuration::DeviceNotFound& e) { + logPrintf(INFO, "Device %s not configured, using default config.", + device->name().c_str()); + } +} + +std::string DeviceConfig::getSetting(std::string path) +{ + return _root_setting + '/' + path; +} diff --git a/src/logid/Device.h b/src/logid/Device.h index 9b4e130..5b5fddd 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -22,9 +22,23 @@ #include "backend/hidpp/defs.h" #include "backend/hidpp20/Device.h" #include "features/DeviceFeature.h" +#include "Configuration.h" namespace logid { + class Device; + + class DeviceConfig + { + public: + DeviceConfig(std::shared_ptr config, Device* device); + std::string getSetting(std::string path); + private: + Device* _device; + std::string _root_setting; + std::shared_ptr _config; + }; + /* TODO: Implement HID++ 1.0 support * Currently, the logid::Device class has a hardcoded requirement * for an HID++ 2.0 device. @@ -35,6 +49,12 @@ namespace logid Device(std::string path, backend::hidpp::DeviceIndex index); Device(const std::shared_ptr& raw_device, backend::hidpp::DeviceIndex index); + + std::string name(); + uint16_t pid(); + + DeviceConfig& config(); + void wakeup(); void sleep(); private: @@ -42,6 +62,7 @@ namespace logid std::string _path; backend::hidpp::DeviceIndex _index; std::vector> _features; + DeviceConfig _config; }; } diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 110f134..654630f 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -180,6 +180,16 @@ Report Device::sendReport(Report& report) return response; } +std::string Device::name() const +{ + return _name; +} + +uint16_t Device::pid() const +{ + return _pid; +} + void Device::listen() { if(!_raw_device->isListening()) diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index 3ba524e..cc6cade 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -72,6 +72,9 @@ namespace hidpp DeviceIndex deviceIndex() const; std::tuple version() const; + std::string name() const; + uint16_t pid() const; + void listen(); // Runs asynchronously void stopListening(); diff --git a/src/logid/features/DeviceFeature.h b/src/logid/features/DeviceFeature.h index accef5e..f3ae122 100644 --- a/src/logid/features/DeviceFeature.h +++ b/src/logid/features/DeviceFeature.h @@ -20,13 +20,30 @@ #define LOGID_FEATURES_DEVICEFEATURE_H namespace logid { + class Device; namespace features { class DeviceFeature { public: + explicit DeviceFeature(Device* dev) : _device (dev) + { + } virtual void configure() = 0; virtual void listen() = 0; + class Config + { + public: + explicit Config(Device* dev) : _device (dev) + { + } + protected: + virtual const std::string configPath() = 0; + Device* _device; + std::string root_setting; + }; + private: + Device* _device; }; }} diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 35274e1..ac6a446 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -38,7 +38,7 @@ using namespace logid; std::string config_file = DEFAULT_CONFIG_FILE; LogLevel logid::global_loglevel = INFO; -// Configuration* logid::global_config; +std::shared_ptr logid::global_config; std::unique_ptr logid::device_manager; bool logid::kill_logid = false; @@ -155,12 +155,15 @@ int main(int argc, char** argv) { readCliOptions(argc, argv); - /* // Read config - try { global_config = new Configuration(config_file.c_str()); } - catch (std::exception &e) { global_config = new Configuration(); } - + try { + global_config = std::make_shared(config_file); + } + catch (std::exception &e) { + global_config = std::make_shared(); + } + /* //Create an evdev device called 'logid' try { global_evdev = new EvdevDevice(evdev_name); } catch(std::system_error& e) From 07b8fc1af4d0a7029b1413e0c086af57ac6969a5 Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 2 Jul 2020 03:34:14 -0400 Subject: [PATCH 38/81] Add DPI feature --- src/logid/CMakeLists.txt | 1 + src/logid/Configuration.cpp | 6 +- src/logid/Device.cpp | 28 +++++-- src/logid/Device.h | 8 +- src/logid/features/DPI.cpp | 114 +++++++++++++++++++++++++++++ src/logid/features/DPI.h | 49 +++++++++++++ src/logid/features/DeviceFeature.h | 7 +- 7 files changed, 199 insertions(+), 14 deletions(-) create mode 100644 src/logid/features/DPI.cpp create mode 100644 src/logid/features/DPI.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 440f4f2..733ed0d 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -15,6 +15,7 @@ add_executable(logid Device.cpp Receiver.cpp Configuration.cpp + features/DPI.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index 9bb1e14..9846951 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -44,14 +44,12 @@ Configuration::Configuration(const std::string& config_file) Setting* devices; try { devices = &root["devices"]; } - catch(const SettingNotFoundException &e) - { + catch(const SettingNotFoundException &e) { logPrintf(WARN, "No devices listed in config file."); return; } - for(int i = 0; i < devices->getLength(); i++) - { + for(int i = 0; i < devices->getLength(); i++) { const Setting &device = (*devices)[i]; std::string name; try { diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index da8746a..ba2949b 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -17,6 +17,7 @@ */ #include "util/log.h" +#include "features/DPI.h" #include "Device.h" using namespace logid; @@ -26,7 +27,7 @@ Device::Device(std::string path, backend::hidpp::DeviceIndex index) : _hidpp20 (path, index), _path (std::move(path)), _index (index), _config (global_config, this) { - ///TODO: Initialize features + _init(); } Device::Device(const std::shared_ptr& raw_device, @@ -34,7 +35,19 @@ Device::Device(const std::shared_ptr& raw_device, (raw_device->hidrawPath()), _index (index), _config (global_config, this) { - ///TODO: Initialize features + _init(); +} + +void Device::_init() +{ + ///TODO: Surely there's a better way of doing this + try { + _features.push_back(std::make_shared(this)); + } catch (backend::hidpp20::UnsupportedFeature& e) { + } + + for(auto& feature: _features) + feature->configure(); } std::string Device::name() @@ -62,7 +75,12 @@ DeviceConfig& Device::config() return _config; } -DeviceConfig::DeviceConfig(std::shared_ptr config, Device* +hidpp20::Device& Device::hidpp20() +{ + return _hidpp20; +} + +DeviceConfig::DeviceConfig(const std::shared_ptr& config, Device* device) : _device (device), _config (config) { try { @@ -73,7 +91,7 @@ DeviceConfig::DeviceConfig(std::shared_ptr config, Device* } } -std::string DeviceConfig::getSetting(std::string path) +libconfig::Setting& DeviceConfig::getSetting(std::string path) { - return _root_setting + '/' + path; + return _config->getSetting(_root_setting + '/' + path); } diff --git a/src/logid/Device.h b/src/logid/Device.h index 5b5fddd..d1226cb 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -31,8 +31,9 @@ namespace logid class DeviceConfig { public: - DeviceConfig(std::shared_ptr config, Device* device); - std::string getSetting(std::string path); + DeviceConfig(const std::shared_ptr& config, Device* + device); + libconfig::Setting& getSetting(std::string path); private: Device* _device; std::string _root_setting; @@ -54,10 +55,13 @@ namespace logid uint16_t pid(); DeviceConfig& config(); + backend::hidpp20::Device& hidpp20(); void wakeup(); void sleep(); private: + void _init(); + backend::hidpp20::Device _hidpp20; std::string _path; backend::hidpp::DeviceIndex _index; diff --git a/src/logid/features/DPI.cpp b/src/logid/features/DPI.cpp new file mode 100644 index 0000000..5d1bfd2 --- /dev/null +++ b/src/logid/features/DPI.cpp @@ -0,0 +1,114 @@ +/* + * 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 . + * + */ +#include +#include +#include "DPI.h" +#include "../Device.h" +#include "../util/log.h" + +using namespace logid::features; +using namespace logid::backend; + +uint16_t getClosestDPI(hidpp20::AdjustableDPI::SensorDPIList& dpi_list, + uint16_t dpi) +{ + if(dpi_list.isRange) { + const uint16_t min = *std::min_element(dpi_list.dpis.begin(), + dpi_list.dpis.end()); + const uint16_t max = *std::max_element(dpi_list.dpis.begin(), + dpi_list.dpis.end()); + if(!((dpi-min) % dpi_list.dpiStep) && dpi >= min && dpi <= max) + return dpi; + else if(dpi > max) + return max; + else if(dpi < min) + return min; + else + return min + round((double)(dpi-min)/dpi_list.dpiStep)*dpi_list + .dpiStep; + } else { + if(std::find(dpi_list.dpis.begin(), dpi_list.dpis.end(), dpi) + != dpi_list.dpis.end()) + return dpi; + else { + auto it = std::min_element(dpi_list.dpis.begin(), dpi_list.dpis + .end(), [dpi](uint16_t a, uint16_t b) { + return (dpi - a) < (dpi - b); + }); + if(it == dpi_list.dpis.end()) + return 0; + else + return *it; + } + } +} + +DPI::DPI(Device* device) : DeviceFeature(device), _config (device), + _adjustable_dpi (&device->hidpp20()) +{ +} + +void DPI::configure() +{ + const uint8_t sensors = _adjustable_dpi.getSensorCount(); + for(uint8_t i = 0; i < _config.getSensorCount() && i < sensors; i++) { + auto dpi = _config.getDPI(i); + if(dpi) { + auto dpi_list = _adjustable_dpi.getSensorDPIList(i); + _adjustable_dpi.setSensorDPI(i, getClosestDPI(dpi_list, + dpi)); + } + } +} + +void DPI::listen() +{ +} + +/* Some devices have multiple sensors, but an older config format + * only supports a single DPI. The dpi setting can be an array or + * an integer. + */ +DPI::Config::Config(Device *dev) : DeviceFeature::Config(dev) +{ + try { + auto& config_root = dev->config().getSetting("dpi"); + if(config_root.isNumber()) { + int dpi = config_root; + _dpis.push_back(dpi); + } else if(config_root.isArray()) { + for(int i = 0; i < config_root.getLength(); i++) + _dpis.push_back((int)config_root[i]); + } else { + logPrintf(WARN, "Line %d: dpi is improperly formatted", + config_root.getSourceLine()); + } + } catch(libconfig::SettingNotFoundException& e) { + // DPI not configured, use default + } +} + +uint8_t DPI::Config::getSensorCount() +{ + return _dpis.size(); +} + +uint16_t DPI::Config::getDPI(uint8_t sensor) +{ + return _dpis[sensor]; +} diff --git a/src/logid/features/DPI.h b/src/logid/features/DPI.h new file mode 100644 index 0000000..5fa346f --- /dev/null +++ b/src/logid/features/DPI.h @@ -0,0 +1,49 @@ +/* + * 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 . + * + */ +#ifndef LOGID_FEATURES_DPI_H +#define LOGID_FEATURES_DPI_H + +#include "../backend/hidpp20/features/AdjustableDPI.h" +#include "DeviceFeature.h" + +namespace logid { +namespace features +{ + class DPI : public DeviceFeature + { + public: + explicit DPI(Device* dev); + virtual void configure(); + virtual void listen(); + + class Config : public DeviceFeature::Config + { + public: + explicit Config(Device* dev); + uint16_t getDPI(uint8_t sensor); + uint8_t getSensorCount(); + protected: + std::vector _dpis; + }; + private: + Config _config; + backend::hidpp20::AdjustableDPI _adjustable_dpi; + }; + }} + +#endif //LOGID_FEATURE_DPI_H diff --git a/src/logid/features/DeviceFeature.h b/src/logid/features/DeviceFeature.h index f3ae122..5bccee1 100644 --- a/src/logid/features/DeviceFeature.h +++ b/src/logid/features/DeviceFeature.h @@ -19,6 +19,8 @@ #ifndef LOGID_FEATURES_DEVICEFEATURE_H #define LOGID_FEATURES_DEVICEFEATURE_H +#include + namespace logid { class Device; namespace features @@ -38,11 +40,10 @@ namespace features { } protected: - virtual const std::string configPath() = 0; Device* _device; - std::string root_setting; }; - private: + + protected: Device* _device; }; }} From 5abf777e00ccc86214480193c5798fb0c5363892 Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 2 Jul 2020 14:50:40 -0400 Subject: [PATCH 39/81] Add SmartShift hidpp20 feature --- src/logid/CMakeLists.txt | 1 + .../backend/hidpp20/features/SmartShift.cpp | 47 ++++++++++++++++ .../backend/hidpp20/features/SmartShift.h | 54 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/logid/backend/hidpp20/features/SmartShift.cpp create mode 100644 src/logid/backend/hidpp20/features/SmartShift.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 733ed0d..4d0962e 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -35,6 +35,7 @@ add_executable(logid backend/hidpp20/features/DeviceName.cpp backend/hidpp20/features/Reset.cpp backend/hidpp20/features/AdjustableDPI.cpp + backend/hidpp20/features/SmartShift.cpp backend/dj/Report.cpp util/mutex_queue.h util/thread.cpp diff --git a/src/logid/backend/hidpp20/features/SmartShift.cpp b/src/logid/backend/hidpp20/features/SmartShift.cpp new file mode 100644 index 0000000..2170f10 --- /dev/null +++ b/src/logid/backend/hidpp20/features/SmartShift.cpp @@ -0,0 +1,47 @@ +/* + * 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 . + * + */ +#include "SmartShift.h" + +using namespace logid::backend::hidpp20; + +SmartShift::SmartShift(Device* dev) : Feature(dev, ID) +{ +} + +SmartShift::SmartshiftStatus SmartShift::getStatus() +{ + std::vector params(0); + SmartshiftStatus status{}; + auto response = callFunction(GetStatus, params); + status.active = response[0]-1; + status.autoDisengage = response[1]; + status.defaultAutoDisengage = response[2]; + return status; +} + +void SmartShift::setStatus(SmartshiftStatus status) +{ + std::vector params(3); + if(status.setActive) + params[0] = status.active + 1; + if(status.setAutoDisengage) + params[1] = status.autoDisengage; + if(status.setDefaultAutoDisengage) + params[2] = status.defaultAutoDisengage; + callFunction(SetStatus, params); +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/SmartShift.h b/src/logid/backend/hidpp20/features/SmartShift.h new file mode 100644 index 0000000..95717f5 --- /dev/null +++ b/src/logid/backend/hidpp20/features/SmartShift.h @@ -0,0 +1,54 @@ +/* + * 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 . + * + */ +#ifndef LOGID_BACKEND_HIDPP20_FEATURE_SMARTSHIFT_H +#define LOGID_BACKEND_HIDPP20_FEATURE_SMARTSHIFT_H + +#include "../feature_defs.h" +#include "../Feature.h" + +namespace logid { +namespace backend { +namespace hidpp20 +{ + class SmartShift : public Feature + { + public: + static const uint16_t ID = FeatureID::SMART_SHIFT; + virtual uint16_t getID() { return ID; } + + enum Function { + GetStatus = 0, + SetStatus = 1 + }; + + SmartShift(Device* dev); + + struct SmartshiftStatus + { + bool active; + uint8_t autoDisengage; + uint8_t defaultAutoDisengage; + bool setActive, setAutoDisengage, setDefaultAutoDisengage; + }; + + SmartshiftStatus getStatus(); + void setStatus(SmartshiftStatus status); + }; +}}} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_SMARTSHIFT_H From 4e33ad7593112ba69f733cd8682403f5c37d95b4 Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 2 Jul 2020 15:27:30 -0400 Subject: [PATCH 40/81] Add SmartShift Device feature --- src/logid/CMakeLists.txt | 1 + src/logid/Device.cpp | 8 +-- src/logid/Device.h | 11 ++++ .../backend/hidpp20/features/SmartShift.h | 2 +- src/logid/features/DPI.h | 4 +- src/logid/features/SmartShift.cpp | 65 +++++++++++++++++++ src/logid/features/SmartShift.h | 48 ++++++++++++++ 7 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 src/logid/features/SmartShift.cpp create mode 100644 src/logid/features/SmartShift.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 4d0962e..aa9570d 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable(logid Receiver.cpp Configuration.cpp features/DPI.cpp + features/SmartShift.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index ba2949b..d7b02f5 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -19,6 +19,7 @@ #include "util/log.h" #include "features/DPI.h" #include "Device.h" +#include "features/SmartShift.h" using namespace logid; using namespace logid::backend; @@ -40,11 +41,8 @@ Device::Device(const std::shared_ptr& raw_device, void Device::_init() { - ///TODO: Surely there's a better way of doing this - try { - _features.push_back(std::make_shared(this)); - } catch (backend::hidpp20::UnsupportedFeature& e) { - } + _addFeature(); + _addFeature(); for(auto& feature: _features) feature->configure(); diff --git a/src/logid/Device.h b/src/logid/Device.h index d1226cb..1206097 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -21,6 +21,7 @@ #include "backend/hidpp/defs.h" #include "backend/hidpp20/Device.h" +#include "backend/hidpp20/Feature.h" #include "features/DeviceFeature.h" #include "Configuration.h" @@ -62,6 +63,16 @@ namespace logid private: void _init(); + /* Adds a feature without calling an error if unsupported */ + template + void _addFeature() + { + try { + _features.push_back(std::make_shared(this)); + } catch (backend::hidpp20::UnsupportedFeature& e) { + } + } + backend::hidpp20::Device _hidpp20; std::string _path; backend::hidpp::DeviceIndex _index; diff --git a/src/logid/backend/hidpp20/features/SmartShift.h b/src/logid/backend/hidpp20/features/SmartShift.h index 95717f5..92f4fb1 100644 --- a/src/logid/backend/hidpp20/features/SmartShift.h +++ b/src/logid/backend/hidpp20/features/SmartShift.h @@ -36,7 +36,7 @@ namespace hidpp20 SetStatus = 1 }; - SmartShift(Device* dev); + explicit SmartShift(Device* dev); struct SmartshiftStatus { diff --git a/src/logid/features/DPI.h b/src/logid/features/DPI.h index 5fa346f..b4c5ca6 100644 --- a/src/logid/features/DPI.h +++ b/src/logid/features/DPI.h @@ -15,8 +15,8 @@ * along with this program. If not, see . * */ -#ifndef LOGID_FEATURES_DPI_H -#define LOGID_FEATURES_DPI_H +#ifndef LOGID_FEATURE_DPI_H +#define LOGID_FEATURE_DPI_H #include "../backend/hidpp20/features/AdjustableDPI.h" #include "DeviceFeature.h" diff --git a/src/logid/features/SmartShift.cpp b/src/logid/features/SmartShift.cpp new file mode 100644 index 0000000..5a0bba8 --- /dev/null +++ b/src/logid/features/SmartShift.cpp @@ -0,0 +1,65 @@ +/* + * 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 . + * + */ +#include "SmartShift.h" +#include "../Device.h" +#include "../util/log.h" + +using namespace logid::features; +using namespace logid::backend; + +SmartShift::SmartShift(Device* device) : DeviceFeature(device), _config + (device), _smartshift(&device->hidpp20()) +{ +} + +void SmartShift::configure() +{ + _smartshift.setStatus(_config.getSettings()); +} + +void SmartShift::listen() +{ +} + +SmartShift::Config::Config(Device *dev) : DeviceFeature::Config(dev), _status() +{ + try { + auto& config_root = dev->config().getSetting("smartshift"); + if(!config_root.isGroup()) { + logPrintf(WARN, "Line %d: smartshift must be an object", + config_root.getSourceLine()); + return; + } + _status.setActive = config_root.lookupValue("on", _status.active); + int tmp; + _status.setAutoDisengage = config_root.lookupValue("threshold", tmp); + if(_status.setAutoDisengage) + _status.autoDisengage = tmp; + _status.setDefaultAutoDisengage = config_root.lookupValue + ("default_threshold", tmp); + if(_status.setDefaultAutoDisengage) + _status.defaultAutoDisengage = tmp; + } catch(libconfig::SettingNotFoundException& e) { + // DPI not configured, use default + } +} + +hidpp20::SmartShift::SmartshiftStatus SmartShift::Config::getSettings() +{ + return _status; +} \ No newline at end of file diff --git a/src/logid/features/SmartShift.h b/src/logid/features/SmartShift.h new file mode 100644 index 0000000..98f1811 --- /dev/null +++ b/src/logid/features/SmartShift.h @@ -0,0 +1,48 @@ +/* + * 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 . + * + */ +#ifndef LOGID_FEATURE_SMARTSHIFT_H +#define LOGID_FEATURE_SMARTSHIFT_H + +#include "../backend/hidpp20/features/SmartShift.h" +#include "DeviceFeature.h" + +namespace logid { +namespace features +{ + class SmartShift : public DeviceFeature + { + public: + explicit SmartShift(Device* dev); + virtual void configure(); + virtual void listen(); + + class Config : public DeviceFeature::Config + { + public: + explicit Config(Device* dev); + backend::hidpp20::SmartShift::SmartshiftStatus getSettings(); + protected: + backend::hidpp20::SmartShift::SmartshiftStatus _status; + }; + private: + Config _config; + backend::hidpp20::SmartShift _smartshift; + }; +}} + +#endif //LOGID_FEATURE_SMARTSHIFT_H From b445b979d3062652807c4dbf7952c434404f0a5c Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 2 Jul 2020 15:42:36 -0400 Subject: [PATCH 41/81] Remove some useless debug output --- src/logid/Device.cpp | 3 +++ src/logid/Receiver.cpp | 1 - src/logid/backend/hidpp/Device.cpp | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index d7b02f5..dfde69f 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -41,6 +41,9 @@ Device::Device(const std::shared_ptr& raw_device, void Device::_init() { + logPrintf(INFO, "Device found: %s on %s:%d", name().c_str(), + hidpp20().devicePath().c_str(), _index); + _addFeature(); _addFeature(); diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index 365cb20..1efa1ea 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -27,7 +27,6 @@ using namespace logid::backend; Receiver::Receiver(std::string path) : dj::ReceiverMonitor(path), _path (path) { - logPrintf(DEBUG, "logid::Receiver created on %s", path.c_str()); } void Receiver::addDevice(hidpp::DeviceConnectionEvent event) diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 654630f..680ed3f 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -49,7 +49,6 @@ Device::InvalidDevice::Reason Device::InvalidDevice::code() const noexcept return _reason; } -/// TODO: Initialize a single RawDevice for each path. Device::Device(const std::string& path, DeviceIndex index): _raw_device (std::make_shared(path)), _receiver (nullptr), _path (path), _index (index) From c382ba1c0b2d61f15d36e0c9967ff74fcf051ac9 Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 2 Jul 2020 16:04:18 -0400 Subject: [PATCH 42/81] Add getFeature function to Device --- src/logid/Device.cpp | 6 +++--- src/logid/Device.h | 24 +++++++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index dfde69f..9e9212c 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -44,11 +44,11 @@ void Device::_init() logPrintf(INFO, "Device found: %s on %s:%d", name().c_str(), hidpp20().devicePath().c_str(), _index); - _addFeature(); - _addFeature(); + _addFeature("dpi"); + _addFeature("smartshift"); for(auto& feature: _features) - feature->configure(); + feature.second->configure(); } std::string Device::name() diff --git a/src/logid/Device.h b/src/logid/Device.h index 1206097..ad047f8 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -24,6 +24,7 @@ #include "backend/hidpp20/Feature.h" #include "features/DeviceFeature.h" #include "Configuration.h" +#include "util/log.h" namespace logid { @@ -60,15 +61,31 @@ namespace logid void wakeup(); void sleep(); + + template + std::shared_ptr getFeature(std::string name) { + auto it = _features.find(name); + if(it == _features.end()) + return nullptr; + try { + return std::dynamic_pointer_cast> + (it->second); + } catch(std::bad_cast& e) { + logPrintf(ERROR, "bad_cast while getting device feature %s: " + "%s", name.c_str(), e.what()); + return nullptr; + } + } + private: void _init(); /* Adds a feature without calling an error if unsupported */ template - void _addFeature() + void _addFeature(std::string name) { try { - _features.push_back(std::make_shared(this)); + _features.emplace(name, std::make_shared(this)); } catch (backend::hidpp20::UnsupportedFeature& e) { } } @@ -76,7 +93,8 @@ namespace logid backend::hidpp20::Device _hidpp20; std::string _path; backend::hidpp::DeviceIndex _index; - std::vector> _features; + std::map> + _features; DeviceConfig _config; }; } From 3129cda5812d0d91b1f3d0e6e23bca03a08156ef Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 2 Jul 2020 16:57:38 -0400 Subject: [PATCH 43/81] Add hidpp20 ReprogControls support --- src/logid/CMakeLists.txt | 1 + .../hidpp20/features/ReprogControls.cpp | 133 +++++++++++++++ .../backend/hidpp20/features/ReprogControls.h | 158 ++++++++++++++++++ 3 files changed, 292 insertions(+) create mode 100644 src/logid/backend/hidpp20/features/ReprogControls.cpp create mode 100644 src/logid/backend/hidpp20/features/ReprogControls.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index aa9570d..a1904c3 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -37,6 +37,7 @@ add_executable(logid backend/hidpp20/features/Reset.cpp backend/hidpp20/features/AdjustableDPI.cpp backend/hidpp20/features/SmartShift.cpp + backend/hidpp20/features/ReprogControls.cpp backend/dj/Report.cpp util/mutex_queue.h util/thread.cpp diff --git a/src/logid/backend/hidpp20/features/ReprogControls.cpp b/src/logid/backend/hidpp20/features/ReprogControls.cpp new file mode 100644 index 0000000..d9835bd --- /dev/null +++ b/src/logid/backend/hidpp20/features/ReprogControls.cpp @@ -0,0 +1,133 @@ +/* + * 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 . + * + */ +#include +#include "ReprogControls.h" + +using namespace logid::backend::hidpp20; + +#define DEFINE_REPROG(x, base) \ +x::x(Device* dev) : base(dev, ID) \ +{ \ +} \ +x::x(Device* dev, uint16_t _id) : base(dev, _id) \ +{ \ +} + +#define MAKE_REPROG(x, dev) \ +try { \ + return x(dev); \ +} catch(UnsupportedFeature &e) {\ +} + +// Define all of the ReprogControls versions +DEFINE_REPROG(ReprogControls, Feature); +DEFINE_REPROG(ReprogControlsV2, ReprogControls); +DEFINE_REPROG(ReprogControlsV2_2, ReprogControlsV2); +DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2); +DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3); + +ReprogControls ReprogControls::autoVersion(Device *dev) +{ + MAKE_REPROG(ReprogControlsV4, dev); + MAKE_REPROG(ReprogControlsV3, dev); + MAKE_REPROG(ReprogControlsV2_2, dev); + MAKE_REPROG(ReprogControlsV2, dev); + + // If base version cannot be made, throw error + return ReprogControls(dev); +} + +uint8_t ReprogControls::getControlCount() +{ + std::vector params(0); + auto response = callFunction(GetControlCount, params); + return response[0]; +} + +ReprogControls::ControlInfo ReprogControls::getControlInfo(uint8_t index) +{ + std::vector params(1); + ControlInfo info{}; + params[0] = index; + auto response = callFunction(GetControlInfo, params); + + info.controlID = response[1]; + info.controlID |= response[0] << 8; + info.taskID = response[3]; + info.taskID |= response[2] << 8; + info.flags = response[4]; + info.position = response[5]; + info.group = response[6]; + info.groupMask = response[7]; + info.additionalFlags = response[8]; + return info; +} + +ReprogControls::ControlInfo ReprogControls::getControlReporting(uint16_t cid) +{ + std::vector 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 ReprogControls::setControlReporting(uint8_t cid, ControlInfo info) +{ + std::vector 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); +} + +std::set ReprogControls::divertedButtonEvent( + const hidpp::Report& report) +{ + assert(report.function() == DivertedButtonEvent); + std::set buttons; + uint8_t cids = std::distance(report.paramBegin(), report.paramEnd())/2; + for(uint8_t i = 0; i < cids; i++) { + uint16_t cid = report.paramBegin()[2*i + 1]; + cid |= report.paramBegin()[2*i] << 8; + if(cid) + buttons.insert(cid); + else + break; + } + return buttons; +} + +ReprogControls::Move ReprogControls::divertedRawXYEvent(const hidpp::Report + &report) +{ + assert(report.function() == DivertedRawXYEvent); + Move move{}; + move.x = report.paramBegin()[1]; + move.x |= report.paramBegin()[0] << 8; + move.y = report.paramBegin()[3]; + move.y |= report.paramBegin()[2] << 8; + return move; +} diff --git a/src/logid/backend/hidpp20/features/ReprogControls.h b/src/logid/backend/hidpp20/features/ReprogControls.h new file mode 100644 index 0000000..3197040 --- /dev/null +++ b/src/logid/backend/hidpp20/features/ReprogControls.h @@ -0,0 +1,158 @@ +/* + * 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 . + * + */ +#ifndef LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H +#define LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H + +#include "../feature_defs.h" +#include "../Feature.h" + +namespace logid { +namespace backend { +namespace hidpp20 +{ + class ReprogControls : public Feature + { + public: + static const uint16_t ID = FeatureID::REPROG_CONTROLS; + virtual uint16_t getID() { return ID; } + + virtual bool supportsRawXY() { return false; } + + enum Function { + GetControlCount = 0, + GetControlInfo = 1, + GetControlReporting = 2, + SetControlReporting = 3 + }; + + enum Event { + DivertedButtonEvent = 0, + DivertedRawXYEvent = 1 + }; + + explicit ReprogControls(Device* dev); + + virtual uint8_t getControlCount(); + + struct ControlInfo + { + uint16_t controlID; + uint16_t taskID; + uint8_t flags; + uint8_t position; // F key position, 0 if not an Fx key + uint8_t group; + uint8_t groupMask; + uint8_t additionalFlags; + }; + + enum ControlInfoFlags: uint8_t + { + MouseButton = 1, //Mouse button + FKey = 1<<1, //Fx key + Hotkey = 1<<2, + FnToggle = 1<<3, + ReprogHint = 1<<4, + TemporaryDivertable = 1<<5, + PerisentlyDiverable = 1<<6, + Virtual = 1<<7 + }; + enum ControlInfoAdditionalFlags: uint8_t { + RawXY = 1<<0 + }; + + virtual ControlInfo getControlInfo(uint8_t index); + + enum ControlReportingFlags: uint8_t { + TemporaryDiverted = 1<<0, + ChangeTemporaryDivert = 1<<1, + PersistentDiverted = 1<<2, + ChangePersistentDivert = 1<<3, + RawXYDiverted = 1<<4, + ChangeRawXYDivert = 1<<5 + }; + + // Onlu controlId and flags will be set + virtual ControlInfo getControlReporting(uint16_t cid); + + // Only controlId (for remap) and flags will be read + virtual void setControlReporting(uint8_t cid, ControlInfo info); + + static std::set divertedButtonEvent(const hidpp::Report& + report); + + struct Move + { + int16_t x; + int16_t y; + }; + + static Move divertedRawXYEvent(const hidpp::Report& report); + + static ReprogControls autoVersion(Device *dev); + protected: + ReprogControls(Device* dev, uint16_t _id); + }; + + class ReprogControlsV2 : public ReprogControls + { + public: + static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2; + uint16_t getID() override { return ID; } + + explicit ReprogControlsV2(Device* dev); + protected: + ReprogControlsV2(Device* dev, uint16_t _id); + }; + + class ReprogControlsV2_2 : public ReprogControlsV2 + { + public: + static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2_2; + uint16_t getID() override { return ID; } + + explicit ReprogControlsV2_2(Device* dev); + protected: + ReprogControlsV2_2(Device* dev, uint16_t _id); + }; + + class ReprogControlsV3 : public ReprogControlsV2_2 + { + public: + static const uint16_t ID = FeatureID::REPROG_CONTROLS_V3; + uint16_t getID() override { return ID; } + + explicit ReprogControlsV3(Device* dev); + protected: + ReprogControlsV3(Device* dev, uint16_t _id); + }; + + class ReprogControlsV4 : public ReprogControlsV3 + { + public: + static const uint16_t ID = FeatureID::REPROG_CONTROLS_V4; + uint16_t getID() override { return ID; } + + bool supportsRawXY() override { return true; } + + explicit ReprogControlsV4(Device* dev); + protected: + ReprogControlsV4(Device* dev, uint16_t _id); + }; +}}} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H From cd14d8dd27a369ee5a2e652112a92b8d97fad97d Mon Sep 17 00:00:00 2001 From: pixl Date: Thu, 2 Jul 2020 17:37:28 -0400 Subject: [PATCH 44/81] Add thanks to README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 30c3a0e..5df1959 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,11 @@ I'm also looking for contributors to help in my project; feel free to submit a p ## Compatible Devices [For a list of tested devices, check TESTED.md](TESTED.md) + +## Special Thanks +Thanks to the following people for contributing to this repository. + +- [Clément Vuchener & contributors for creating the old HID++ library](https://github.com/cvuchener/hidpp) +- [Developers of Solaar for providing information on HID++](https://github.com/pwr-Solaar/Solaar) +- [Nestor Lopez Casado for providing Logitech documentation on the HID++ protocol](http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28) +- Everyone listed in the contributors page From 0b87d3c6647835614ca63c97c1df595c5d7d1b2f Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 3 Jul 2020 22:59:34 -0400 Subject: [PATCH 45/81] Fixed bug: odd devices would fail Root GetFeature Some devices throw an hidpp20::Error InvalidFeatureIndex when Root GetFeature is called with some features (e.g. 0x1b04) as the parameter. Since Root is a required feature in the HID++ 2.0 protocol, this error can safely be ignored and treated as an UnsupportedFeature. Fixes bug in #20 where the Logitech PRO headset would not work. --- src/logid/backend/hidpp/Device.cpp | 2 +- src/logid/backend/hidpp20/Device.cpp | 2 ++ .../backend/hidpp20/EssentialFeature.cpp | 14 +++++++++++-- src/logid/backend/hidpp20/Feature.cpp | 13 ++++++++++-- src/logid/backend/hidpp20/features/Root.cpp | 21 +++++++++++++++---- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 680ed3f..d6d5a64 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -174,7 +174,7 @@ Report Device::sendReport(Report& report) Report::Hidpp20Error hidpp20_error{}; if(response.isError20(&hidpp20_error)) - throw hidpp10::Error(hidpp20_error.error_code); + throw hidpp20::Error(hidpp20_error.error_code); return response; } diff --git a/src/logid/backend/hidpp20/Device.cpp b/src/logid/backend/hidpp20/Device.cpp index 2e71ae7..691cc34 100644 --- a/src/logid/backend/hidpp20/Device.cpp +++ b/src/logid/backend/hidpp20/Device.cpp @@ -45,6 +45,8 @@ std::vector Device::callFunction(uint8_t feature_index, type = hidpp::Report::Type::Short; else if(params.size() <= hidpp::LongParamLength) type = hidpp::Report::Type::Long; + else + throw hidpp::Report::InvalidReportID(); hidpp::Report request(type, deviceIndex(), feature_index, function, LOGID_HIDPP_SOFTWARE_ID); diff --git a/src/logid/backend/hidpp20/EssentialFeature.cpp b/src/logid/backend/hidpp20/EssentialFeature.cpp index bbca19f..11b9751 100644 --- a/src/logid/backend/hidpp20/EssentialFeature.cpp +++ b/src/logid/backend/hidpp20/EssentialFeature.cpp @@ -20,6 +20,7 @@ #include "EssentialFeature.h" #include "feature_defs.h" #include "features/Root.h" +#include "Error.h" using namespace logid::backend::hidpp20; @@ -33,6 +34,8 @@ std::vector EssentialFeature::callFunction(uint8_t function_id, type = hidpp::Report::Type::Short; else if(params.size() <= hidpp::LongParamLength) type = hidpp::Report::Type::Long; + else + throw hidpp::Report::InvalidReportID(); hidpp::Report request(type, _device->deviceIndex(), _index, function_id, LOGID_HIDPP_SOFTWARE_ID); @@ -52,8 +55,15 @@ EssentialFeature::EssentialFeature(hidpp::Device* dev, uint16_t _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]; + try { + auto getFunc_resp = this->callFunction(Root::GetFeature, + getFunc_req); + _index = getFunc_resp[0]; + } catch(Error& e) { + if(e.code() == Error::InvalidFeatureIndex) + throw UnsupportedFeature(_id); + throw e; + } // 0 if not found if(!_index) diff --git a/src/logid/backend/hidpp20/Feature.cpp b/src/logid/backend/hidpp20/Feature.cpp index 5f32bbb..6f64db5 100644 --- a/src/logid/backend/hidpp20/Feature.cpp +++ b/src/logid/backend/hidpp20/Feature.cpp @@ -16,6 +16,7 @@ * */ +#include "Error.h" #include "Feature.h" #include "feature_defs.h" #include "features/Root.h" @@ -46,8 +47,16 @@ Feature::Feature(Device* dev, uint16_t _id) : _device (dev) 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]; + + try { + auto getFunc_resp = this->callFunction(Root::GetFeature, + getFunc_req); + _index = getFunc_resp[0]; + } catch(Error& e) { + if(e.code() == Error::InvalidFeatureIndex) + throw UnsupportedFeature(_id); + throw e; + } // 0 if not found if(!_index) diff --git a/src/logid/backend/hidpp20/features/Root.cpp b/src/logid/backend/hidpp20/features/Root.cpp index ba5e91a..f1aa724 100644 --- a/src/logid/backend/hidpp20/features/Root.cpp +++ b/src/logid/backend/hidpp20/features/Root.cpp @@ -17,6 +17,7 @@ */ #include "Root.h" +#include "../Error.h" using namespace logid::backend::hidpp20; @@ -51,8 +52,14 @@ feature_info _genGetFeatureInfo(uint16_t feature_id, 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); + try { + auto response = this->callFunction(Root::Function::GetFeature, params); + return _genGetFeatureInfo(feature_id, response); + } catch(Error& e) { + if(e.code() == Error::InvalidFeatureIndex) + throw UnsupportedFeature(feature_id); + throw e; + } } std::tuple Root::getVersion() @@ -70,8 +77,14 @@ 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); + try { + auto response = this->callFunction(Root::Function::GetFeature, params); + return _genGetFeatureInfo(feature_id, response); + } catch(Error& e) { + if(e.code() == Error::InvalidFeatureIndex) + throw UnsupportedFeature(feature_id); + throw e; + } } std::tuple EssentialRoot::getVersion() From 5bf5dc75b5234dd1ca27125b1f02d8e1a1b8afb7 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 5 Jul 2020 02:55:46 -0400 Subject: [PATCH 46/81] Implement RemapButton feature Many changes were made here, too many to list here. --- src/logid/CMakeLists.txt | 4 + src/logid/Configuration.cpp | 2 +- src/logid/Configuration.h | 2 +- src/logid/Device.cpp | 11 +- .../{EvdevDevice.cpp => InputDevice.cpp} | 82 ++++++-- src/logid/InputDevice.h | 66 +++++++ src/logid/actions/Action.cpp | 58 ++++++ src/logid/actions/Action.h | 82 ++++++++ src/logid/actions/KeypressAction.cpp | 87 +++++++++ .../KeypressAction.h} | 43 +++-- src/logid/backend/hidpp/Device.cpp | 28 ++- src/logid/backend/hidpp/Device.h | 6 +- src/logid/backend/hidpp/defs.h | 2 + src/logid/backend/hidpp20/Feature.cpp | 5 + src/logid/backend/hidpp20/Feature.h | 2 +- .../hidpp20/features/ReprogControls.cpp | 78 ++++++-- .../backend/hidpp20/features/ReprogControls.h | 58 +++--- src/logid/backend/raw/RawDevice.cpp | 1 + src/logid/features/RemapButton.cpp | 180 ++++++++++++++++++ src/logid/features/RemapButton.h | 55 ++++++ src/logid/logid.cpp | 17 +- 21 files changed, 765 insertions(+), 104 deletions(-) rename src/logid/{EvdevDevice.cpp => InputDevice.cpp} (54%) create mode 100644 src/logid/InputDevice.h create mode 100644 src/logid/actions/Action.cpp create mode 100644 src/logid/actions/Action.h create mode 100644 src/logid/actions/KeypressAction.cpp rename src/logid/{EvdevDevice.h => actions/KeypressAction.h} (50%) create mode 100644 src/logid/features/RemapButton.cpp create mode 100644 src/logid/features/RemapButton.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index a1904c3..4032e9f 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -11,12 +11,16 @@ find_package(PkgConfig REQUIRED) add_executable(logid logid.cpp util/log.cpp + InputDevice.cpp DeviceManager.cpp Device.cpp Receiver.cpp Configuration.cpp features/DPI.cpp features/SmartShift.cpp + features/RemapButton.cpp + actions/Action.cpp + actions/KeypressAction.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index 9846951..7a16266 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -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(); } diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index b9d5200..2d2166a 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -37,7 +37,7 @@ namespace logid { public: explicit DeviceNotFound(std::string name); - virtual const char* what(); + const char* what() const noexcept override; private: std::string _name; }; diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 9e9212c..7aa3e11 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -20,6 +20,7 @@ #include "features/DPI.h" #include "Device.h" #include "features/SmartShift.h" +#include "features/RemapButton.h" using namespace logid; using namespace logid::backend; @@ -46,9 +47,14 @@ void Device::_init() _addFeature("dpi"); _addFeature("smartshift"); + _addFeature("remapbutton"); - for(auto& feature: _features) + for(auto& feature: _features) { feature.second->configure(); + feature.second->listen(); + } + + _hidpp20.listen(); } std::string Device::name() @@ -69,6 +75,9 @@ void Device::sleep() void Device::wakeup() { logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + for(auto& feature: _features) + feature.second->configure(); } DeviceConfig& Device::config() diff --git a/src/logid/EvdevDevice.cpp b/src/logid/InputDevice.cpp similarity index 54% rename from src/logid/EvdevDevice.cpp rename to src/logid/InputDevice.cpp index 05eb3dc..972d127 100644 --- a/src/logid/EvdevDevice.cpp +++ b/src/logid/InputDevice.cpp @@ -16,19 +16,35 @@ * */ +#include +#include + +#include "InputDevice.h" + +extern "C" +{ #include #include -#include - -#include "EvdevDevice.h" +}; using namespace logid; -EvdevDevice::EvdevDevice(const char* name) +InputDevice::InvalidEventCode::InvalidEventCode(std::string name) +{ + _what = "Invalid event code " + name; +} + +const char* InputDevice::InvalidEventCode::what() const noexcept +{ + return _what.c_str(); +} + +InputDevice::InputDevice(const char* name) { device = libevdev_new(); libevdev_set_name(device, name); + ///TODO: Is it really a good idea to enable all events? libevdev_enable_event_type(device, EV_KEY); for(int i = 0; i < KEY_CNT; i++) libevdev_enable_event_code(device, EV_KEY, i, nullptr); @@ -36,26 +52,56 @@ EvdevDevice::EvdevDevice(const char* name) for(int i = 0; i < REL_CNT; i++) libevdev_enable_event_code(device, EV_REL, i, nullptr); - int err = libevdev_uinput_create_from_device(device, LIBEVDEV_UINPUT_OPEN_MANAGED, &ui_device); + int err = libevdev_uinput_create_from_device(device, + LIBEVDEV_UINPUT_OPEN_MANAGED, &ui_device); if(err != 0) throw std::system_error(-err, std::generic_category()); } -void EvdevDevice::moveAxis(unsigned int axis, int movement) -{ - libevdev_uinput_write_event(ui_device, EV_REL, axis, movement); - libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0); -} - -void EvdevDevice::sendEvent(unsigned int type, unsigned int code, int value) -{ - libevdev_uinput_write_event(ui_device, type, code, value); - libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0); -} - -EvdevDevice::~EvdevDevice() +InputDevice::~InputDevice() { libevdev_uinput_destroy(ui_device); libevdev_free(device); } + +void InputDevice::moveAxis(uint axis, int movement) +{ + _sendEvent(EV_REL, axis, movement); +} + +void InputDevice::pressKey(uint code) +{ + _sendEvent(EV_KEY, code, 1); +} + +void InputDevice::releaseKey(uint code) +{ + _sendEvent(EV_KEY, code, 0); +} + +uint InputDevice::toKeyCode(std::string name) +{ + return _toEventCode(EV_KEY, std::move(name)); +} + +uint InputDevice::toAxisCode(std::string name) +{ + return _toEventCode(EV_KEY, std::move(name)); +} + +uint InputDevice::_toEventCode(uint type, const std::string& name) +{ + int code = libevdev_event_code_from_name(type, name.c_str()); + + if(code == -1) + throw InvalidEventCode(name); + + return code; +} + +void InputDevice::_sendEvent(uint type, uint code, int value) +{ + libevdev_uinput_write_event(ui_device, type, code, value); + libevdev_uinput_write_event(ui_device, EV_SYN, SYN_REPORT, 0); +} \ No newline at end of file diff --git a/src/logid/InputDevice.h b/src/logid/InputDevice.h new file mode 100644 index 0000000..0072271 --- /dev/null +++ b/src/logid/InputDevice.h @@ -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 . + * + */ + +#ifndef LOGID_INPUTDEVICE_H +#define LOGID_INPUTDEVICE_H + +#include + +extern "C" +{ +#include +#include +}; + +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 virtual_input; +} + +#endif //LOGID_INPUTDEVICE_H \ No newline at end of file diff --git a/src/logid/actions/Action.cpp b/src/logid/actions/Action.cpp new file mode 100644 index 0000000..44e9dd1 --- /dev/null +++ b/src/logid/actions/Action.cpp @@ -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 . + * + */ + +#include +#include "Action.h" +#include "../util/log.h" +#include "KeypressAction.h" + +using namespace logid; +using namespace logid::actions; + +std::shared_ptr 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(device, setting); + else + throw InvalidAction(type); + + } catch(libconfig::SettingNotFoundException& e) { + logPrintf(WARN, "Line %d: Action type is missing, ignoring.", + setting.getSourceLine()); + throw InvalidAction(); + } +} \ No newline at end of file diff --git a/src/logid/actions/Action.h b/src/logid/actions/Action.h new file mode 100644 index 0000000..6bdb87c --- /dev/null +++ b/src/logid/actions/Action.h @@ -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 . + * + */ +#ifndef LOGID_ACTION_H +#define LOGID_ACTION_H + +#include +#include +#include + +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 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 _pressed; + }; +}} + +#endif //LOGID_ACTION_H diff --git a/src/logid/actions/KeypressAction.cpp b/src/logid/actions/KeypressAction.cpp new file mode 100644 index 0000000..f5a3540 --- /dev/null +++ b/src/logid/actions/KeypressAction.cpp @@ -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 . + * + */ +#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& KeypressAction::Config::keys() +{ + return _keys; +} \ No newline at end of file diff --git a/src/logid/EvdevDevice.h b/src/logid/actions/KeypressAction.h similarity index 50% rename from src/logid/EvdevDevice.h rename to src/logid/actions/KeypressAction.h index a37584c..4385faf 100644 --- a/src/logid/EvdevDevice.h +++ b/src/logid/actions/KeypressAction.h @@ -15,31 +15,36 @@ * along with this program. If not, see . * */ +#ifndef LOGID_ACTION_KEYPRESS_H +#define LOGID_ACTION_KEYPRESS_H -#ifndef LOGID_EVDEVDEVICE_H -#define LOGID_EVDEVDEVICE_H +#include +#include +#include "Action.h" -#include -#include - -namespace logid -{ - class EvdevDevice +namespace logid { +namespace actions { + class KeypressAction : public Action { public: - EvdevDevice(const char *name); + KeypressAction(Device* dev, libconfig::Setting& config); - ~EvdevDevice(); + virtual void press(); + virtual void release(); - void moveAxis(unsigned int axis, int movement); + virtual uint8_t reprogFlags() const; - void sendEvent(unsigned int type, unsigned int code, int value); - - libevdev *device; - libevdev_uinput *ui_device; + class Config : public Action::Config + { + public: + explicit Config(Device* device, libconfig::Setting& root); + std::vector& keys(); + protected: + std::vector _keys; + }; + protected: + Config _config; }; +}} - extern EvdevDevice* global_evdev; -} - -#endif //LOGID_EVDEVDEVICE_H \ No newline at end of file +#endif //LOGID_ACTION_KEYPRESS_H diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index d6d5a64..ae3f061 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -92,6 +92,7 @@ std::tuple Device::version() const void Device::_init() { + _listening = false; _supported_reports = getSupportedReports(_raw_device->reportDescriptor()); if(!_supported_reports) throw InvalidDevice(InvalidDevice::NoHIDPPReport); @@ -127,7 +128,8 @@ void Device::_init() Device::~Device() { - _raw_device->removeEventHandler("DEV_" + std::to_string(_index)); + if(_listening) + _raw_device->removeEventHandler("DEV_" + std::to_string(_index)); } void Device::addEventHandler(const std::string& nickname, @@ -144,6 +146,12 @@ void Device::removeEventHandler(const std::string& nickname) _event_handlers.erase(nickname); } +const std::map>& + Device::eventHandlers() +{ + return _event_handlers; +} + void Device::handleEvent(Report& report) { for(auto& handler : _event_handlers) @@ -192,31 +200,33 @@ uint16_t Device::pid() const void Device::listen() { if(!_raw_device->isListening()) - ///TODO: Kill RawDevice? thread::spawn({[raw=this->_raw_device]() { raw->listen(); }}); // Pass all HID++ events with device index to this device. - std::shared_ptr handler; - handler->condition = [this](std::vector& report)->bool - { + auto handler = std::make_shared(); + handler->condition = [index=this->_index](std::vector& report) + ->bool { return (report[Offset::Type] == Report::Type::Short || report[Offset::Type] == Report::Type::Long) && - (report[Offset::DeviceIndex] == this->_index); + (report[Offset::DeviceIndex] == index); }; - handler->callback = [this](std::vector& report)->void - { + handler->callback = [this](std::vector& report)->void { Report _report(report); this->handleEvent(_report); }; _raw_device->addEventHandler("DEV_" + std::to_string(_index), handler); + _listening = true; } void Device::stopListening() { - _raw_device->removeEventHandler("DEV_" + std::to_string(_index)); + if(_listening) + _raw_device->removeEventHandler("DEV_" + std::to_string(_index)); + + _listening = false; if(!_raw_device->eventHandlers().empty()) _raw_device->stopListener(); diff --git a/src/logid/backend/hidpp/Device.h b/src/logid/backend/hidpp/Device.h index cc6cade..0ea2062 100644 --- a/src/logid/backend/hidpp/Device.h +++ b/src/logid/backend/hidpp/Device.h @@ -55,7 +55,7 @@ namespace hidpp Asleep }; InvalidDevice(Reason reason) : _reason (reason) {} - virtual const char *what() const noexcept; + virtual const char* what() const noexcept; virtual Reason code() const noexcept; private: Reason _reason; @@ -81,6 +81,8 @@ namespace hidpp void addEventHandler(const std::string& nickname, const std::shared_ptr& handler); void removeEventHandler(const std::string& nickname); + const std::map>& + eventHandlers(); Report sendReport(Report& report); @@ -98,6 +100,8 @@ namespace hidpp uint16_t _pid; std::string _name; + std::atomic _listening; + std::map> _event_handlers; }; } } } diff --git a/src/logid/backend/hidpp/defs.h b/src/logid/backend/hidpp/defs.h index a884e65..1590e06 100644 --- a/src/logid/backend/hidpp/defs.h +++ b/src/logid/backend/hidpp/defs.h @@ -21,6 +21,8 @@ #define LOGID_HIDPP_SOFTWARE_ID 0 +#include + namespace logid { namespace backend { namespace hidpp diff --git a/src/logid/backend/hidpp20/Feature.cpp b/src/logid/backend/hidpp20/Feature.cpp index 6f64db5..18c30b1 100644 --- a/src/logid/backend/hidpp20/Feature.cpp +++ b/src/logid/backend/hidpp20/Feature.cpp @@ -63,3 +63,8 @@ Feature::Feature(Device* dev, uint16_t _id) : _device (dev) throw UnsupportedFeature(_id); } } + +uint8_t Feature::featureIndex() +{ + return _index; +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Feature.h b/src/logid/backend/hidpp20/Feature.h index f68cfa0..cc4732b 100644 --- a/src/logid/backend/hidpp20/Feature.h +++ b/src/logid/backend/hidpp20/Feature.h @@ -40,7 +40,7 @@ namespace hidpp20 { public: static const uint16_t ID; virtual uint16_t getID() = 0; - + uint8_t featureIndex(); protected: explicit Feature(Device* dev, uint16_t _id); std::vector callFunction(uint8_t function_id, diff --git a/src/logid/backend/hidpp20/features/ReprogControls.cpp b/src/logid/backend/hidpp20/features/ReprogControls.cpp index d9835bd..4200385 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.cpp +++ b/src/logid/backend/hidpp20/features/ReprogControls.cpp @@ -16,6 +16,7 @@ * */ #include +#include "../Error.h" #include "ReprogControls.h" using namespace logid::backend::hidpp20; @@ -30,7 +31,7 @@ x::x(Device* dev, uint16_t _id) : base(dev, _id) \ #define MAKE_REPROG(x, dev) \ try { \ - return x(dev); \ + return std::make_shared(dev); \ } catch(UnsupportedFeature &e) {\ } @@ -41,7 +42,7 @@ DEFINE_REPROG(ReprogControlsV2_2, ReprogControlsV2); DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2); DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3); -ReprogControls ReprogControls::autoVersion(Device *dev) +std::shared_ptr ReprogControls::autoVersion(Device *dev) { MAKE_REPROG(ReprogControlsV4, dev); MAKE_REPROG(ReprogControlsV3, dev); @@ -49,7 +50,7 @@ ReprogControls ReprogControls::autoVersion(Device *dev) MAKE_REPROG(ReprogControlsV2, dev); // If base version cannot be made, throw error - return ReprogControls(dev); + return std::make_shared(dev); } uint8_t ReprogControls::getControlCount() @@ -78,29 +79,43 @@ ReprogControls::ControlInfo ReprogControls::getControlInfo(uint8_t index) return info; } +ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid) +{ + if(_cids.empty()) { + for(uint8_t i = 0; i < getControlCount(); i++) { + auto info = getControlInfo(i); + _cids.emplace(info.controlID, info); + } + } + + auto it = _cids.find(cid); + if(it == _cids.end()) + throw Error(Error::InvalidArgument); + else + return it->second; +} + ReprogControls::ControlInfo ReprogControls::getControlReporting(uint16_t cid) { - std::vector params(2); - ControlInfo info{}; - params[0] = (cid >> 8) & 0xff; - params[1] = cid & 0xff; - auto response = callFunction(GetControlReporting, params); + // Emulate this function, only Reprog controls v4 supports this + auto info = getControlIdInfo(cid); - info.controlID = response[1]; - info.controlID |= response[0] << 8; - info.flags = response[2]; - return info; + ControlInfo report{}; + report.controlID = cid; + report.flags = 0; + if(info.flags & TemporaryDivertable) + report.flags |= TemporaryDiverted; + if(info.flags & PersisentlyDivertable) + report.flags |= PersistentlyDiverted; + if(info.additionalFlags & RawXY) + report.flags |= RawXYDiverted; + + return report; } void ReprogControls::setControlReporting(uint8_t cid, ControlInfo info) { - std::vector params(5); - params[0] = (cid >> 8) & 0xff; - params[1] = cid & 0xff; - params[2] = info.flags; - params[3] = (info.controlID >> 8) & 0xff; - params[4] = info.controlID & 0xff; - callFunction(SetControlReporting, params); + // This function does not exist pre-v4 and cannot be emulated, ignore. } std::set ReprogControls::divertedButtonEvent( @@ -131,3 +146,28 @@ ReprogControls::Move ReprogControls::divertedRawXYEvent(const hidpp::Report move.y |= report.paramBegin()[2] << 8; return move; } + +ReprogControls::ControlInfo ReprogControlsV4::getControlReporting(uint16_t cid) +{ + std::vector 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 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); +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/ReprogControls.h b/src/logid/backend/hidpp20/features/ReprogControls.h index 3197040..143dafd 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.h +++ b/src/logid/backend/hidpp20/features/ReprogControls.h @@ -18,6 +18,8 @@ #ifndef LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H #define LOGID_BACKEND_HIDPP20_FEATURE_REPROGCONTROLS_H +#include + #include "../feature_defs.h" #include "../Feature.h" @@ -28,27 +30,17 @@ namespace hidpp20 class ReprogControls : public Feature { public: - static const uint16_t ID = FeatureID::REPROG_CONTROLS; - virtual uint16_t getID() { return ID; } - - virtual bool supportsRawXY() { return false; } - enum Function { GetControlCount = 0, GetControlInfo = 1, GetControlReporting = 2, SetControlReporting = 3 }; - enum Event { DivertedButtonEvent = 0, DivertedRawXYEvent = 1 }; - explicit ReprogControls(Device* dev); - - virtual uint8_t getControlCount(); - struct ControlInfo { uint16_t controlID; @@ -68,24 +60,41 @@ namespace hidpp20 FnToggle = 1<<3, ReprogHint = 1<<4, TemporaryDivertable = 1<<5, - PerisentlyDiverable = 1<<6, + PersisentlyDivertable = 1<<6, Virtual = 1<<7 }; enum ControlInfoAdditionalFlags: uint8_t { RawXY = 1<<0 }; - virtual ControlInfo getControlInfo(uint8_t index); - enum ControlReportingFlags: uint8_t { TemporaryDiverted = 1<<0, ChangeTemporaryDivert = 1<<1, - PersistentDiverted = 1<<2, + PersistentlyDiverted = 1<<2, ChangePersistentDivert = 1<<3, RawXYDiverted = 1<<4, ChangeRawXYDivert = 1<<5 }; + struct Move + { + int16_t x; + int16_t y; + }; + + static const uint16_t ID = FeatureID::REPROG_CONTROLS; + virtual uint16_t getID() { return ID; } + + virtual bool supportsRawXY() { return false; } + + explicit ReprogControls(Device* dev); + + virtual uint8_t getControlCount(); + + virtual ControlInfo getControlInfo(uint8_t cid); + + virtual ControlInfo getControlIdInfo(uint16_t cid); + // Onlu controlId and flags will be set virtual ControlInfo getControlReporting(uint16_t cid); @@ -95,24 +104,19 @@ namespace hidpp20 static std::set divertedButtonEvent(const hidpp::Report& report); - struct Move - { - int16_t x; - int16_t y; - }; - static Move divertedRawXYEvent(const hidpp::Report& report); - static ReprogControls autoVersion(Device *dev); + static std::shared_ptr autoVersion(Device *dev); protected: ReprogControls(Device* dev, uint16_t _id); + std::map _cids; }; class ReprogControlsV2 : public ReprogControls { public: static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2; - uint16_t getID() override { return ID; } + virtual uint16_t getID() { return ID; } explicit ReprogControlsV2(Device* dev); protected: @@ -123,7 +127,7 @@ namespace hidpp20 { public: static const uint16_t ID = FeatureID::REPROG_CONTROLS_V2_2; - uint16_t getID() override { return ID; } + virtual uint16_t getID() { return ID; } explicit ReprogControlsV2_2(Device* dev); protected: @@ -134,7 +138,7 @@ namespace hidpp20 { public: static const uint16_t ID = FeatureID::REPROG_CONTROLS_V3; - uint16_t getID() override { return ID; } + virtual uint16_t getID() { return ID; } explicit ReprogControlsV3(Device* dev); protected: @@ -145,10 +149,14 @@ namespace hidpp20 { public: static const uint16_t ID = FeatureID::REPROG_CONTROLS_V4; - uint16_t getID() override { return ID; } + virtual uint16_t getID() { return ID; } bool supportsRawXY() override { return true; } + ControlInfo getControlReporting(uint16_t cid) override; + + void setControlReporting(uint8_t cid, ControlInfo info) override; + explicit ReprogControlsV4(Device* dev); protected: ReprogControlsV4(Device* dev, uint16_t _id); diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 9719f49..680bdd2 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -354,6 +354,7 @@ void RawDevice::addEventHandler(const std::string& nickname, { auto it = _event_handlers.find(nickname); assert(it == _event_handlers.end()); + assert(handler); _event_handlers.emplace(nickname, handler); } diff --git a/src/logid/features/RemapButton.cpp b/src/logid/features/RemapButton.cpp new file mode 100644 index 0000000..58aa786 --- /dev/null +++ b/src/logid/features/RemapButton.cpp @@ -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 . + * + */ +#include +#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(); + 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 new_state) +{ + // Ensure I/O doesn't occur while updating button state + std::lock_guard 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>& RemapButton::Config::buttons() +{ + return _buttons; +} \ No newline at end of file diff --git a/src/logid/features/RemapButton.h b/src/logid/features/RemapButton.h new file mode 100644 index 0000000..a45e767 --- /dev/null +++ b/src/logid/features/RemapButton.h @@ -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 . + * + */ +#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>& + buttons(); + protected: + void _parseButton(libconfig::Setting& setting); + std::map> _buttons; + }; + private: + void _buttonEvent(std::set new_state); + Config _config; + std::shared_ptr _reprog_controls; + std::set _pressed_buttons; + std::mutex _button_lock; + }; +}} + +#endif //LOGID_FEATURE_REMAPBUTTON_H diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index ac6a446..6fc1197 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -24,8 +24,9 @@ #include "util/log.h" #include "DeviceManager.h" #include "logid.h" +#include "InputDevice.h" -#define evdev_name "logid" +#define LOGID_VIRTUAL_INPUT_NAME "LogiOps Virtual Input" #define DEFAULT_CONFIG_FILE "/etc/logid.cfg" #ifndef LOGIOPS_VERSION @@ -40,6 +41,7 @@ std::string config_file = DEFAULT_CONFIG_FILE; LogLevel logid::global_loglevel = INFO; std::shared_ptr logid::global_config; std::unique_ptr logid::device_manager; +std::unique_ptr logid::virtual_input; bool logid::kill_logid = false; std::mutex logid::device_manager_reload; @@ -163,17 +165,14 @@ int main(int argc, char** argv) global_config = std::make_shared(); } - /* - //Create an evdev device called 'logid' - try { global_evdev = new EvdevDevice(evdev_name); } - catch(std::system_error& e) - { - log_printf(ERROR, "Could not create evdev device: %s", e.what()); + //Create a virtual input device + try { + virtual_input = std::make_unique(LOGID_VIRTUAL_INPUT_NAME); + } catch(std::system_error& e) { + logPrintf(ERROR, "Could not create input device: %s", e.what()); return EXIT_FAILURE; } - */ - // Scan devices, create listeners, handlers, etc. device_manager = std::make_unique(); From 71b0ddd2793064c8dc75549323e7fe8a5f8a688a Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 5 Jul 2020 16:16:38 -0400 Subject: [PATCH 47/81] Add reset mechanism to logid::Device --- src/logid/Device.cpp | 27 +++++++++++++++++++++++++++ src/logid/Device.h | 5 +++++ 2 files changed, 32 insertions(+) diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 7aa3e11..7867f0b 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -21,6 +21,7 @@ #include "Device.h" #include "features/SmartShift.h" #include "features/RemapButton.h" +#include "backend/hidpp20/features/Reset.h" using namespace logid; using namespace logid::backend; @@ -49,6 +50,9 @@ void Device::_init() _addFeature("smartshift"); _addFeature("remapbutton"); + _makeResetMechanism(); + reset(); + for(auto& feature: _features) { feature.second->configure(); feature.second->listen(); @@ -76,10 +80,22 @@ void Device::wakeup() { logPrintf(INFO, "%s:%d woke up.", _path.c_str(), _index); std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + reset(); + for(auto& feature: _features) feature.second->configure(); } +void Device::reset() +{ + if(_reset_mechanism) + (*_reset_mechanism)(); + else + logPrintf(DEBUG, "%s:%d tried to reset, but no reset mechanism was " + "available.", _path.c_str(), _index); +} + DeviceConfig& Device::config() { return _config; @@ -90,6 +106,17 @@ hidpp20::Device& Device::hidpp20() return _hidpp20; } +void Device::_makeResetMechanism() +{ + try { + hidpp20::Reset reset(&_hidpp20); + _reset_mechanism = std::make_unique>( + [dev=&this->_hidpp20]{ hidpp20::Reset(dev).reset(); }); + } catch(hidpp20::UnsupportedFeature& e) { + // Reset unsupported, ignore. + } +} + DeviceConfig::DeviceConfig(const std::shared_ptr& config, Device* device) : _device (device), _config (config) { diff --git a/src/logid/Device.h b/src/logid/Device.h index ad047f8..c1e7dbc 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -62,6 +62,8 @@ namespace logid void wakeup(); void sleep(); + void reset(); + template std::shared_ptr getFeature(std::string name) { auto it = _features.find(name); @@ -96,6 +98,9 @@ namespace logid std::map> _features; DeviceConfig _config; + + void _makeResetMechanism(); + std::unique_ptr> _reset_mechanism; }; } From d6f5c359830646efc2628360909f551ca133e492 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 5 Jul 2020 16:46:52 -0400 Subject: [PATCH 48/81] Add HiresScroll hidpp20 feature --- src/logid/CMakeLists.txt | 1 + .../backend/hidpp20/features/HiresScroll.cpp | 76 +++++++++++++++ .../backend/hidpp20/features/HiresScroll.h | 94 +++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 src/logid/backend/hidpp20/features/HiresScroll.cpp create mode 100644 src/logid/backend/hidpp20/features/HiresScroll.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 4032e9f..dddf7cd 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -42,6 +42,7 @@ add_executable(logid backend/hidpp20/features/AdjustableDPI.cpp backend/hidpp20/features/SmartShift.cpp backend/hidpp20/features/ReprogControls.cpp + backend/hidpp20/features/HiresScroll.cpp backend/dj/Report.cpp util/mutex_queue.h util/thread.cpp diff --git a/src/logid/backend/hidpp20/features/HiresScroll.cpp b/src/logid/backend/hidpp20/features/HiresScroll.cpp new file mode 100644 index 0000000..62bc597 --- /dev/null +++ b/src/logid/backend/hidpp20/features/HiresScroll.cpp @@ -0,0 +1,76 @@ +/* + * 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 . + * + */ +#include +#include "HiresScroll.h" + +using namespace logid::backend::hidpp20; + +HiresScroll::HiresScroll(Device *device) : Feature(device, ID) +{ +} + +HiresScroll::Capabilities HiresScroll::getCapabilities() +{ + std::vector params(0); + auto response = callFunction(GetCapabilities, params); + + Capabilities capabilities{}; + capabilities.multiplier = response[0]; + capabilities.flags = response[1]; + return capabilities; +} + +uint8_t HiresScroll::getMode() +{ + std::vector params(0); + auto response = callFunction(GetMode, params); + return response[0]; +} + +void HiresScroll::setMode(uint8_t mode) +{ + std::vector params(1); + params[0] = mode; + callFunction(SetMode, params); +} + +bool HiresScroll::getRatchetState() +{ + std::vector params(0); + auto response = callFunction(GetRatchetState, params); + return params[0]; +} + +HiresScroll::WheelStatus HiresScroll::wheelMovementEvent(const hidpp::Report + &report) +{ + assert(report.function() == WheelMovement); + WheelStatus status{}; + status.hiRes = report.paramBegin()[0] & 1<<4; + status.periods = report.paramBegin()[0] & 0x0F; + status.deltaV = report.paramBegin()[1] << 8 | report.paramBegin()[2]; + return status; +} + +HiresScroll::RatchetState HiresScroll::ratchetSwitchEvent(const hidpp::Report + &report) +{ + assert(report.function() == WheelMovement); + // Possible bad cast + return static_cast(report.paramBegin()[0]); +} \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/HiresScroll.h b/src/logid/backend/hidpp20/features/HiresScroll.h new file mode 100644 index 0000000..f008c64 --- /dev/null +++ b/src/logid/backend/hidpp20/features/HiresScroll.h @@ -0,0 +1,94 @@ +/* + * 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 . + * + */ +#ifndef LOGID_BACKEND_HIDPP20_FEATURE_HIRESSCROLL_H +#define LOGID_BACKEND_HIDPP20_FEATURE_HIRESSCROLL_H + +#include "../Feature.h" +#include "../feature_defs.h" + +namespace logid { +namespace backend { +namespace hidpp20 +{ + class HiresScroll : public Feature + { + public: + ///TODO: Hires scroll V1? + static const uint16_t ID = FeatureID::HIRES_SCROLLING_V2; + virtual uint16_t getID() { return ID; } + + enum Function : uint8_t + { + GetCapabilities = 0, + GetMode = 1, + SetMode = 2, + GetRatchetState = 3 + }; + + enum Event : uint8_t + { + WheelMovement = 0, + RatchetSwitch = 1, + }; + + enum Capability : uint8_t + { + Invertable = 1<<3, + HasRatchet = 1<<2 + }; + + enum Mode : uint8_t + { + Inverted = 1<<2, + HiRes = 1<<1, + Target = 1 + }; + + enum RatchetState : uint8_t + { + FreeWheel = 0, + Ratchet = 1 + }; + + struct Capabilities + { + uint8_t multiplier; + uint8_t flags; + }; + + struct WheelStatus + { + bool hiRes; + uint8_t periods; + uint16_t deltaV; + }; + + explicit HiresScroll(Device* device); + + Capabilities getCapabilities(); + uint8_t getMode(); + void setMode(uint8_t mode); + bool getRatchetState(); + + ///TODO: Event handlers + static WheelStatus wheelMovementEvent(const hidpp::Report& report); + static RatchetState ratchetSwitchEvent(const hidpp::Report& report); + }; +}}} + +#endif //LOGID_BACKEND_HIDPP20_FEATURE_HIRESSCROLL_H From 055d136b09d680d75bfda97bc018c2ec31c934b0 Mon Sep 17 00:00:00 2001 From: pixl Date: Mon, 6 Jul 2020 14:18:44 -0400 Subject: [PATCH 49/81] Add HiresScroll device feature --- src/logid/CMakeLists.txt | 1 + src/logid/Device.cpp | 2 + src/logid/features/HiresScroll.cpp | 101 +++++++++++++++++++++++++++++ src/logid/features/HiresScroll.h | 50 ++++++++++++++ src/logid/features/SmartShift.cpp | 2 +- 5 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 src/logid/features/HiresScroll.cpp create mode 100644 src/logid/features/HiresScroll.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index dddf7cd..248cce9 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(logid Configuration.cpp features/DPI.cpp features/SmartShift.cpp + features/HiresScroll.cpp features/RemapButton.cpp actions/Action.cpp actions/KeypressAction.cpp diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 7867f0b..7f59980 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -22,6 +22,7 @@ #include "features/SmartShift.h" #include "features/RemapButton.h" #include "backend/hidpp20/features/Reset.h" +#include "features/HiresScroll.h" using namespace logid; using namespace logid::backend; @@ -48,6 +49,7 @@ void Device::_init() _addFeature("dpi"); _addFeature("smartshift"); + _addFeature("hiresscroll"); _addFeature("remapbutton"); _makeResetMechanism(); diff --git a/src/logid/features/HiresScroll.cpp b/src/logid/features/HiresScroll.cpp new file mode 100644 index 0000000..7accd6d --- /dev/null +++ b/src/logid/features/HiresScroll.cpp @@ -0,0 +1,101 @@ +/* + * 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 . + * + */ +#include "HiresScroll.h" +#include "../Device.h" + +using namespace logid::features; +using namespace logid::backend; + +HiresScroll::HiresScroll(Device *dev) : DeviceFeature(dev), + _hires_scroll(&dev->hidpp20()), _config(dev) +{ +} + +void HiresScroll::configure() +{ + auto mode = _hires_scroll.getMode(); + mode &= ~_config.getMask(); + mode |= (_config.getMode() & _config.getMask()); + _hires_scroll.setMode(mode); +} + +void HiresScroll::listen() +{ + ///TODO: Map hires scroll events +} + +HiresScroll::Config::Config(Device *dev) : DeviceFeature::Config(dev) +{ + try { + auto& config_root = dev->config().getSetting("hiresscroll"); + if(!config_root.isGroup()) { + logPrintf(WARN, "Line %d: hiresscroll must be a group", + config_root.getSourceLine()); + return; + } + _mode = 0; + _mask = 0; + try { + auto& hires = config_root.lookup("hires"); + if(hires.getType() == libconfig::Setting::TypeBoolean) { + _mask |= hidpp20::HiresScroll::Mode::HiRes; + if(hires) + _mode |= hidpp20::HiresScroll::Mode::HiRes; + } else { + logPrintf(WARN, "Line %d: hires must be a boolean", + hires.getSourceLine()); + } + } catch(libconfig::SettingNotFoundException& e) { } + + try { + auto& invert = config_root.lookup("invert"); + if(invert.getType() == libconfig::Setting::TypeBoolean) { + _mask |= hidpp20::HiresScroll::Mode::Inverted; + if(invert) + _mode |= hidpp20::HiresScroll::Mode::Inverted; + } else { + logPrintf(WARN, "Line %d: invert must be a boolean, ignoring.", + invert.getSourceLine()); + } + } catch(libconfig::SettingNotFoundException& e) { } + + try { + auto& target = config_root.lookup("target"); + if(target.getType() == libconfig::Setting::TypeBoolean) { + _mask |= hidpp20::HiresScroll::Mode::Target; + if(target) + _mode |= hidpp20::HiresScroll::Mode::Target; + } else { + logPrintf(WARN, "Line %d: target must be a boolean, ignoring.", + target.getSourceLine()); + } + } catch(libconfig::SettingNotFoundException& e) { } + } catch(libconfig::SettingNotFoundException& e) { + // HiresScroll not configured, use default + } +} + +uint8_t HiresScroll::Config::getMode() const +{ + return _mode; +} + +uint8_t HiresScroll::Config::getMask() const +{ + return _mask; +} \ No newline at end of file diff --git a/src/logid/features/HiresScroll.h b/src/logid/features/HiresScroll.h new file mode 100644 index 0000000..6eaf356 --- /dev/null +++ b/src/logid/features/HiresScroll.h @@ -0,0 +1,50 @@ +/* + * 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 . + * + */ +#ifndef LOGID_FEATURE_HIRESSCROLL_H +#define LOGID_FEATURE_HIRESSCROLL_H + +#include "../backend/hidpp20/features/HiresScroll.h" +#include "DeviceFeature.h" + +namespace logid { +namespace features +{ + class HiresScroll : public DeviceFeature + { + public: + explicit HiresScroll(Device* dev); + virtual void configure(); + virtual void listen(); + + class Config : public DeviceFeature::Config + { + public: + explicit Config(Device* dev); + uint8_t getMode() const; + uint8_t getMask() const; + protected: + uint8_t _mode; + uint8_t _mask; + }; + private: + backend::hidpp20::HiresScroll _hires_scroll; + Config _config; + }; +}} + +#endif //LOGID_FEATURE_HIRESSCROLL_H diff --git a/src/logid/features/SmartShift.cpp b/src/logid/features/SmartShift.cpp index 5a0bba8..212b920 100644 --- a/src/logid/features/SmartShift.cpp +++ b/src/logid/features/SmartShift.cpp @@ -55,7 +55,7 @@ SmartShift::Config::Config(Device *dev) : DeviceFeature::Config(dev), _status() if(_status.setDefaultAutoDisengage) _status.defaultAutoDisengage = tmp; } catch(libconfig::SettingNotFoundException& e) { - // DPI not configured, use default + // SmartShift not configured, use default } } From bc8f1a983a7baa6c99ed38d92d42a923ec4cfe45 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 8 Jul 2020 03:41:05 -0400 Subject: [PATCH 50/81] Add configurable I/O timeout --- src/logid/Configuration.cpp | 23 +++++++++++++++++++++++ src/logid/Configuration.h | 6 ++++++ src/logid/backend/raw/RawDevice.cpp | 14 +++++++++++--- src/logid/backend/raw/RawDevice.h | 2 -- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index 7a16266..a94265a 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -25,6 +25,7 @@ using namespace logid; using namespace libconfig; +using namespace std::chrono; Configuration::Configuration(const std::string& config_file) { @@ -49,6 +50,23 @@ Configuration::Configuration(const std::string& config_file) return; } + _io_timeout = LOGID_DEFAULT_RAWDEVICE_TIMEOUT; + try { + auto& timeout = root["io_timeout"]; + if(timeout.isNumber()) { + auto t = timeout.getType(); + if(timeout.getType() == libconfig::Setting::TypeFloat) + _io_timeout = duration_cast( + duration(timeout)); + else + _io_timeout = milliseconds((int)timeout); + } else + logPrintf(WARN, "Line %d: io_timeout must be a number.", + timeout.getSourceLine()); + } catch(const SettingNotFoundException& e) { + // Ignore + } + for(int i = 0; i < devices->getLength(); i++) { const Setting &device = (*devices)[i]; std::string name; @@ -90,3 +108,8 @@ const char * Configuration::DeviceNotFound::what() const noexcept { return _name.c_str(); } + +std::chrono::milliseconds Configuration::ioTimeout() const +{ + return _io_timeout; +} diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index 2d2166a..d083ef9 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -22,6 +22,9 @@ #include #include #include +#include + +#define LOGID_DEFAULT_RAWDEVICE_TIMEOUT std::chrono::seconds(2) namespace logid { @@ -41,8 +44,11 @@ namespace logid private: std::string _name; }; + + std::chrono::milliseconds ioTimeout() const; private: std::map _device_paths; + std::chrono::milliseconds _io_timeout; libconfig::Config _config; }; diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 680bdd2..2905c38 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -22,6 +22,7 @@ #include "../dj/defs.h" #include "../../util/log.h" #include "../hidpp/Report.h" +#include "../../Configuration.h" #include #include @@ -258,8 +259,15 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng int ret; report.resize(maxDataLength); - timeval timeout = { duration_cast(HIDPP_IO_TIMEOUT).count(), - duration_cast(HIDPP_IO_TIMEOUT).count() }; + timeval timeout{}; + timeout.tv_sec = duration_cast(global_config->ioTimeout()) + .count(); + timeout.tv_usec = duration_cast( + global_config->ioTimeout()).count() % + duration_cast(seconds(1)).count(); + + auto timeout_ms = duration_cast( + global_config->ioTimeout()).count(); fd_set fds; do { @@ -269,7 +277,7 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng ret = select(std::max(_fd, _pipe[0]) + 1, &fds, nullptr, nullptr, - (HIDPP_IO_TIMEOUT.count() > 0 ? nullptr : &timeout)); + (timeout_ms > 0 ? nullptr : &timeout)); } while(ret == -1 && errno == EINTR); if(ret == -1) diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index d12020a..3cb1823 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -30,8 +30,6 @@ #include "defs.h" #include "../../util/mutex_queue.h" -#define HIDPP_IO_TIMEOUT std::chrono::seconds(2) - namespace logid { namespace backend { namespace raw From 77f4240ec7564fc157b6ae11f119c0639c20216b Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 8 Jul 2020 16:29:20 -0400 Subject: [PATCH 51/81] Add ToggleSmartShift action --- src/logid/CMakeLists.txt | 1 + src/logid/Device.h | 3 +- src/logid/actions/Action.cpp | 3 ++ src/logid/actions/KeypressAction.cpp | 2 + src/logid/actions/ToggleSmartShift.cpp | 64 ++++++++++++++++++++++++++ src/logid/actions/ToggleSmartShift.h | 48 +++++++++++++++++++ src/logid/features/SmartShift.cpp | 11 +++++ src/logid/features/SmartShift.h | 3 ++ 8 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 src/logid/actions/ToggleSmartShift.cpp create mode 100644 src/logid/actions/ToggleSmartShift.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 248cce9..a8f76ad 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable(logid features/RemapButton.cpp actions/Action.cpp actions/KeypressAction.cpp + actions/ToggleSmartShift.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/Device.h b/src/logid/Device.h index c1e7dbc..66147b3 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -70,8 +70,7 @@ namespace logid if(it == _features.end()) return nullptr; try { - return std::dynamic_pointer_cast> - (it->second); + return std::dynamic_pointer_cast(it->second); } catch(std::bad_cast& e) { logPrintf(ERROR, "bad_cast while getting device feature %s: " "%s", name.c_str(), e.what()); diff --git a/src/logid/actions/Action.cpp b/src/logid/actions/Action.cpp index 44e9dd1..b60a37d 100644 --- a/src/logid/actions/Action.cpp +++ b/src/logid/actions/Action.cpp @@ -20,6 +20,7 @@ #include "Action.h" #include "../util/log.h" #include "KeypressAction.h" +#include "ToggleSmartShift.h" using namespace logid; using namespace logid::actions; @@ -47,6 +48,8 @@ std::shared_ptr Action::makeAction(Device *device, libconfig::Setting if(type == "keypress") return std::make_shared(device, setting); + else if(type == "togglesmartshift") + return std::make_shared(device, setting); else throw InvalidAction(type); diff --git a/src/logid/actions/KeypressAction.cpp b/src/logid/actions/KeypressAction.cpp index f5a3540..da097cc 100644 --- a/src/logid/actions/KeypressAction.cpp +++ b/src/logid/actions/KeypressAction.cpp @@ -30,12 +30,14 @@ KeypressAction::KeypressAction(Device *device, libconfig::Setting& config) : void KeypressAction::press() { + _pressed = true; for(auto& key : _config.keys()) virtual_input->pressKey(key); } void KeypressAction::release() { + _pressed = false; for(auto& key : _config.keys()) virtual_input->releaseKey(key); } diff --git a/src/logid/actions/ToggleSmartShift.cpp b/src/logid/actions/ToggleSmartShift.cpp new file mode 100644 index 0000000..9713c16 --- /dev/null +++ b/src/logid/actions/ToggleSmartShift.cpp @@ -0,0 +1,64 @@ +/* + * 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 . + * + */ +#include "ToggleSmartShift.h" +#include "../Device.h" +#include "../backend/hidpp20/features/ReprogControls.h" +#include "../util/thread.h" + +using namespace logid::actions; +using namespace logid::backend; + +ToggleSmartShift::ToggleSmartShift(Device *dev, libconfig::Setting &config) : + Action (dev), _config (dev, config) +{ + _smartshift = _device->getFeature("smartshift"); + if(!_smartshift) + logPrintf(WARN, "%s:%d: SmartShift feature not found, cannot use " + "ToggleSmartShift action.", + _device->hidpp20().devicePath().c_str(), + _device->hidpp20().devicePath().c_str()); +} + +void ToggleSmartShift::press() +{ + _pressed = true; + if(_smartshift) + { + thread::spawn([ss=this->_smartshift](){ + auto status = ss->getStatus(); + status.setActive = true; + status.active = !status.active; + ss->setStatus(status); + }); + } +} + +void ToggleSmartShift::release() +{ + _pressed = false; +} + +uint8_t ToggleSmartShift::reprogFlags() const +{ + return hidpp20::ReprogControls::TemporaryDiverted; +} + +ToggleSmartShift::Config::Config(Device *device, libconfig::Setting &root) : + Action::Config(device) +{ +} \ No newline at end of file diff --git a/src/logid/actions/ToggleSmartShift.h b/src/logid/actions/ToggleSmartShift.h new file mode 100644 index 0000000..3cd8f95 --- /dev/null +++ b/src/logid/actions/ToggleSmartShift.h @@ -0,0 +1,48 @@ +/* + * 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 . + * + */ +#ifndef LOGID_ACTION_TOGGLESMARTSHIFT_H +#define LOGID_ACTION_TOGGLESMARTSHIFT_H + +#include +#include "Action.h" +#include "../features/SmartShift.h" + +namespace logid { +namespace actions { + class ToggleSmartShift : public Action + { + public: + ToggleSmartShift(Device* dev, libconfig::Setting& config); + + virtual void press(); + virtual void release(); + + virtual uint8_t reprogFlags() const; + + class Config : public Action::Config + { + public: + explicit Config(Device* device, libconfig::Setting& root); + }; + protected: + Config _config; + std::shared_ptr _smartshift; + }; +}} + +#endif //LOGID_ACTION_TOGGLESMARTSHIFT_H \ No newline at end of file diff --git a/src/logid/features/SmartShift.cpp b/src/logid/features/SmartShift.cpp index 212b920..fd357f0 100644 --- a/src/logid/features/SmartShift.cpp +++ b/src/logid/features/SmartShift.cpp @@ -36,6 +36,17 @@ void SmartShift::listen() { } +hidpp20::SmartShift::SmartshiftStatus SmartShift::getStatus() +{ + return _smartshift.getStatus(); +} + +void SmartShift::setStatus(backend::hidpp20::SmartShift::SmartshiftStatus + status) +{ + _smartshift.setStatus(status); +} + SmartShift::Config::Config(Device *dev) : DeviceFeature::Config(dev), _status() { try { diff --git a/src/logid/features/SmartShift.h b/src/logid/features/SmartShift.h index 98f1811..06bb280 100644 --- a/src/logid/features/SmartShift.h +++ b/src/logid/features/SmartShift.h @@ -31,6 +31,9 @@ namespace features virtual void configure(); virtual void listen(); + backend::hidpp20::SmartShift::SmartshiftStatus getStatus(); + void setStatus(backend::hidpp20::SmartShift::SmartshiftStatus status); + class Config : public DeviceFeature::Config { public: From 02d361b54136120c6e1110e8c636a702d495e356 Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 8 Jul 2020 17:12:37 -0400 Subject: [PATCH 52/81] Add ToggleHiresScroll action --- src/logid/CMakeLists.txt | 1 + src/logid/actions/Action.cpp | 3 ++ src/logid/actions/ToggleHiresScroll.cpp | 64 +++++++++++++++++++++++++ src/logid/actions/ToggleHiresScroll.h | 48 +++++++++++++++++++ src/logid/features/HiresScroll.cpp | 10 ++++ src/logid/features/HiresScroll.h | 3 ++ 6 files changed, 129 insertions(+) create mode 100644 src/logid/actions/ToggleHiresScroll.cpp create mode 100644 src/logid/actions/ToggleHiresScroll.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index a8f76ad..052ba5c 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable(logid features/RemapButton.cpp actions/Action.cpp actions/KeypressAction.cpp + actions/ToggleHiresScroll.cpp actions/ToggleSmartShift.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp diff --git a/src/logid/actions/Action.cpp b/src/logid/actions/Action.cpp index b60a37d..5f52987 100644 --- a/src/logid/actions/Action.cpp +++ b/src/logid/actions/Action.cpp @@ -21,6 +21,7 @@ #include "../util/log.h" #include "KeypressAction.h" #include "ToggleSmartShift.h" +#include "ToggleHiresScroll.h" using namespace logid; using namespace logid::actions; @@ -50,6 +51,8 @@ std::shared_ptr Action::makeAction(Device *device, libconfig::Setting return std::make_shared(device, setting); else if(type == "togglesmartshift") return std::make_shared(device, setting); + else if(type == "togglehiresscroll") + return std::make_shared(device, setting); else throw InvalidAction(type); diff --git a/src/logid/actions/ToggleHiresScroll.cpp b/src/logid/actions/ToggleHiresScroll.cpp new file mode 100644 index 0000000..6dc9703 --- /dev/null +++ b/src/logid/actions/ToggleHiresScroll.cpp @@ -0,0 +1,64 @@ +/* + * 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 . + * + */ +#include "ToggleHiresScroll.h" +#include "../Device.h" +#include "../util/log.h" +#include "../util/thread.h" +#include "../backend/hidpp20/features/ReprogControls.h" + +using namespace logid::actions; +using namespace logid::backend; + +ToggleHiresScroll::ToggleHiresScroll(Device *dev, libconfig::Setting &config) : + Action (dev), _config (dev, config) +{ + _hires_scroll = _device->getFeature("hiresscroll"); + if(!_hires_scroll) + logPrintf(WARN, "%s:%d: HiresScroll feature not found, cannot use " + "ToggleHiresScroll action.", + _device->hidpp20().devicePath().c_str(), + _device->hidpp20().devicePath().c_str()); +} + +void ToggleHiresScroll::press() +{ + _pressed = true; + if(_hires_scroll) + { + thread::spawn([hires=this->_hires_scroll](){ + auto mode = hires->getMode(); + mode ^= backend::hidpp20::HiresScroll::HiRes; + hires->setMode(mode); + }); + } +} + +void ToggleHiresScroll::release() +{ + _pressed = false; +} + +uint8_t ToggleHiresScroll::reprogFlags() const +{ + return hidpp20::ReprogControls::TemporaryDiverted; +} + +ToggleHiresScroll::Config::Config(Device *device, libconfig::Setting &root) : + Action::Config(device) +{ +} \ No newline at end of file diff --git a/src/logid/actions/ToggleHiresScroll.h b/src/logid/actions/ToggleHiresScroll.h new file mode 100644 index 0000000..39377f5 --- /dev/null +++ b/src/logid/actions/ToggleHiresScroll.h @@ -0,0 +1,48 @@ +/* + * 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 . + * + */ +#ifndef LOGID_ACTION_TOGGLEHIRESSCROLL_H +#define LOGID_ACTION_TOGGLEHIRESSCROLL_H + +#include "Action.h" +#include "../features/HiresScroll.h" + +namespace logid { +namespace actions +{ + class ToggleHiresScroll : public Action + { + public: + ToggleHiresScroll(Device* dev, libconfig::Setting& config); + + virtual void press(); + virtual void release(); + + virtual uint8_t reprogFlags() const; + + class Config : public Action::Config + { + public: + explicit Config(Device* device, libconfig::Setting& root); + }; + protected: + Config _config; + std::shared_ptr _hires_scroll; + }; +}} + +#endif //LOGID_ACTION_TOGGLEHIRESSCROLL_H diff --git a/src/logid/features/HiresScroll.cpp b/src/logid/features/HiresScroll.cpp index 7accd6d..e21a8b0 100644 --- a/src/logid/features/HiresScroll.cpp +++ b/src/logid/features/HiresScroll.cpp @@ -39,6 +39,16 @@ void HiresScroll::listen() ///TODO: Map hires scroll events } +uint8_t HiresScroll::getMode() +{ + return _hires_scroll.getMode(); +} + +void HiresScroll::setMode(uint8_t mode) +{ + _hires_scroll.setMode(mode); +} + HiresScroll::Config::Config(Device *dev) : DeviceFeature::Config(dev) { try { diff --git a/src/logid/features/HiresScroll.h b/src/logid/features/HiresScroll.h index 6eaf356..7e81c97 100644 --- a/src/logid/features/HiresScroll.h +++ b/src/logid/features/HiresScroll.h @@ -31,6 +31,9 @@ namespace features virtual void configure(); virtual void listen(); + uint8_t getMode(); + void setMode(uint8_t mode); + class Config : public DeviceFeature::Config { public: From 1f3fa5372195c16cb043825bb22214615dc4cf49 Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 10 Jul 2020 03:16:51 -0400 Subject: [PATCH 53/81] Add workqueue system --- src/logid/CMakeLists.txt | 3 + src/logid/Configuration.cpp | 23 +++- src/logid/Configuration.h | 3 + src/logid/actions/ToggleHiresScroll.cpp | 4 +- src/logid/actions/ToggleSmartShift.cpp | 4 +- src/logid/backend/dj/ReceiverMonitor.cpp | 4 +- src/logid/backend/raw/DeviceMonitor.cpp | 8 +- src/logid/logid.cpp | 6 + src/logid/util/ExceptionHandler.cpp | 8 +- src/logid/util/task.cpp | 60 +++++++++ src/logid/util/task.h | 67 ++++++++++ src/logid/util/worker_thread.cpp | 88 +++++++++++++ src/logid/util/worker_thread.h | 56 +++++++++ src/logid/util/workqueue.cpp | 151 +++++++++++++++++++++++ src/logid/util/workqueue.h | 59 +++++++++ 15 files changed, 529 insertions(+), 15 deletions(-) create mode 100644 src/logid/util/task.cpp create mode 100644 src/logid/util/task.h create mode 100644 src/logid/util/worker_thread.cpp create mode 100644 src/logid/util/worker_thread.h create mode 100644 src/logid/util/workqueue.cpp create mode 100644 src/logid/util/workqueue.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 052ba5c..6d93d7b 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -48,6 +48,9 @@ add_executable(logid backend/hidpp20/features/HiresScroll.cpp backend/dj/Report.cpp util/mutex_queue.h + util/workqueue.cpp + util/worker_thread.cpp + util/task.cpp util/thread.cpp util/ExceptionHandler.cpp) diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index a94265a..0826c0f 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -50,12 +50,28 @@ Configuration::Configuration(const std::string& config_file) return; } + _worker_threads = LOGID_DEFAULT_WORKER_COUNT; + try { + auto& worker_count = root["workers"]; + if(worker_count.getType() == Setting::TypeInt) { + _worker_threads = worker_count; + if(_worker_threads < 0) + logPrintf(WARN, "Line %d: workers cannot be negative.", + worker_count.getSourceLine()); + } else { + logPrintf(WARN, "Line %d: workers must be an integer.", + worker_count.getSourceLine()); + } + } catch(const SettingNotFoundException& e) { + // Ignore + } + _io_timeout = LOGID_DEFAULT_RAWDEVICE_TIMEOUT; try { auto& timeout = root["io_timeout"]; if(timeout.isNumber()) { auto t = timeout.getType(); - if(timeout.getType() == libconfig::Setting::TypeFloat) + if(timeout.getType() == Setting::TypeFloat) _io_timeout = duration_cast( duration(timeout)); else @@ -109,6 +125,11 @@ const char * Configuration::DeviceNotFound::what() const noexcept return _name.c_str(); } +int Configuration::workerCount() const +{ + return _worker_threads; +} + std::chrono::milliseconds Configuration::ioTimeout() const { return _io_timeout; diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index d083ef9..ed10893 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -25,6 +25,7 @@ #include #define LOGID_DEFAULT_RAWDEVICE_TIMEOUT std::chrono::seconds(2) +#define LOGID_DEFAULT_WORKER_COUNT 2 namespace logid { @@ -46,9 +47,11 @@ namespace logid }; std::chrono::milliseconds ioTimeout() const; + int workerCount() const; private: std::map _device_paths; std::chrono::milliseconds _io_timeout; + int _worker_threads; libconfig::Config _config; }; diff --git a/src/logid/actions/ToggleHiresScroll.cpp b/src/logid/actions/ToggleHiresScroll.cpp index 6dc9703..7fbf5ac 100644 --- a/src/logid/actions/ToggleHiresScroll.cpp +++ b/src/logid/actions/ToggleHiresScroll.cpp @@ -18,7 +18,7 @@ #include "ToggleHiresScroll.h" #include "../Device.h" #include "../util/log.h" -#include "../util/thread.h" +#include "../util/task.h" #include "../backend/hidpp20/features/ReprogControls.h" using namespace logid::actions; @@ -40,7 +40,7 @@ void ToggleHiresScroll::press() _pressed = true; if(_hires_scroll) { - thread::spawn([hires=this->_hires_scroll](){ + task::spawn([hires=this->_hires_scroll](){ auto mode = hires->getMode(); mode ^= backend::hidpp20::HiresScroll::HiRes; hires->setMode(mode); diff --git a/src/logid/actions/ToggleSmartShift.cpp b/src/logid/actions/ToggleSmartShift.cpp index 9713c16..dfe86a9 100644 --- a/src/logid/actions/ToggleSmartShift.cpp +++ b/src/logid/actions/ToggleSmartShift.cpp @@ -18,7 +18,7 @@ #include "ToggleSmartShift.h" #include "../Device.h" #include "../backend/hidpp20/features/ReprogControls.h" -#include "../util/thread.h" +#include "../util/task.h" using namespace logid::actions; using namespace logid::backend; @@ -39,7 +39,7 @@ void ToggleSmartShift::press() _pressed = true; if(_smartshift) { - thread::spawn([ss=this->_smartshift](){ + task::spawn([ss=this->_smartshift](){ auto status = ss->getStatus(); status.setActive = true; status.active = !status.active; diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp index a983171..d483475 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/dj/ReceiverMonitor.cpp @@ -17,7 +17,7 @@ */ #include "ReceiverMonitor.h" -#include "../../util/thread.h" +#include "../../util/task.h" #include "../../util/log.h" #include @@ -57,7 +57,7 @@ void ReceiverMonitor::run() /* Running in a new thread prevents deadlocks since the * receiver may be enumerating. */ - thread::spawn({[this, report]() { + task::spawn({[this, report]() { if (report.subId() == Receiver::DeviceConnection) this->addDevice(this->_receiver->deviceConnectionEvent (report)); diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp index fcbc6db..278f4ca 100644 --- a/src/logid/backend/raw/DeviceMonitor.cpp +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -17,7 +17,7 @@ */ #include "DeviceMonitor.h" -#include "../../util/thread.h" +#include "../../util/task.h" #include "../../util/log.h" #include @@ -98,14 +98,14 @@ void DeviceMonitor::run() std::string devnode = udev_device_get_devnode(device); if (action == "add") - thread::spawn([this, name=devnode]() { + task::spawn([this, name=devnode]() { this->addDevice(name); }, [name=devnode](std::exception& e){ logPrintf(WARN, "Error adding device %s: %s", name.c_str(), e.what()); }); else if (action == "remove") - thread::spawn([this, name=devnode]() { + task::spawn([this, name=devnode]() { this->removeDevice(name); }, [name=devnode](std::exception& e){ logPrintf(WARN, "Error removing device %s: %s", @@ -157,7 +157,7 @@ void DeviceMonitor::enumerate() std::string devnode = udev_device_get_devnode(device); udev_device_unref(device); - thread::spawn([this, name=devnode]() { + task::spawn([this, name=devnode]() { this->addDevice(name); }, [name=devnode](std::exception& e){ logPrintf(ERROR, "Error adding device %s: %s", diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 6fc1197..0d3bc34 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -25,6 +25,7 @@ #include "DeviceManager.h" #include "logid.h" #include "InputDevice.h" +#include "util/workqueue.h" #define LOGID_VIRTUAL_INPUT_NAME "LogiOps Virtual Input" #define DEFAULT_CONFIG_FILE "/etc/logid.cfg" @@ -42,6 +43,7 @@ LogLevel logid::global_loglevel = INFO; std::shared_ptr logid::global_config; std::unique_ptr logid::device_manager; std::unique_ptr logid::virtual_input; +std::unique_ptr logid::global_workqueue; bool logid::kill_logid = false; std::mutex logid::device_manager_reload; @@ -155,6 +157,8 @@ Possible options are: int main(int argc, char** argv) { + global_workqueue = std::make_unique(LOGID_DEFAULT_WORKER_COUNT); + readCliOptions(argc, argv); // Read config @@ -165,6 +169,8 @@ int main(int argc, char** argv) global_config = std::make_shared(); } + global_workqueue->setThreadCount(global_config->workerCount()); + //Create a virtual input device try { virtual_input = std::make_unique(LOGID_VIRTUAL_INPUT_NAME); diff --git a/src/logid/util/ExceptionHandler.cpp b/src/logid/util/ExceptionHandler.cpp index 859783f..48c723f 100644 --- a/src/logid/util/ExceptionHandler.cpp +++ b/src/logid/util/ExceptionHandler.cpp @@ -28,16 +28,16 @@ void ExceptionHandler::Default(std::exception& error) try { throw error; } catch(backend::hidpp10::Error& e) { - logPrintf(WARN, "HID++ 1.0 error ignored on detached thread: %s", + logPrintf(WARN, "HID++ 1.0 error ignored on detached thread/task: %s", error.what()); } catch(backend::hidpp20::Error& e) { - logPrintf(WARN, "HID++ 2.0 error ignored on detached thread: %s", + logPrintf(WARN, "HID++ 2.0 error ignored on detached thread/task: %s", error.what()); } catch(std::system_error& e) { - logPrintf(WARN, "System error ignored on detached thread: %s", + logPrintf(WARN, "System error ignored on detached thread/task: %s", error.what()); } catch(std::exception& e) { - logPrintf(WARN, "Error ignored on detached thread: %s", + logPrintf(WARN, "Error ignored on detached thread/task: %s", error.what()); } } \ No newline at end of file diff --git a/src/logid/util/task.cpp b/src/logid/util/task.cpp new file mode 100644 index 0000000..021e0fb --- /dev/null +++ b/src/logid/util/task.cpp @@ -0,0 +1,60 @@ +/* + * 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 . + * + */ +#include "task.h" +#include "workqueue.h" + +using namespace logid; + +task::task(const std::function& function, + const std::function& exception_handler) : + _function (std::make_shared>(function)), + _exception_handler (std::make_shared> + (exception_handler)), _status (Waiting), + _task_pkg ([this](){ + try { + (*_function)(); + } catch(std::exception& e) { + (*_exception_handler)(e); + } + }) +{ +} + +void task::run() +{ + _status = Running; + _task_pkg(); + _status = Completed; +} + +task::Status task::getStatus() +{ + return _status; +} + +void task::wait() +{ + _task_pkg.get_future().wait(); +} + +void task::spawn(const std::function& function, + const std::function& exception_handler) +{ + auto t = std::make_shared(function, exception_handler); + global_workqueue->queue(t); +} \ No newline at end of file diff --git a/src/logid/util/task.h b/src/logid/util/task.h new file mode 100644 index 0000000..100a938 --- /dev/null +++ b/src/logid/util/task.h @@ -0,0 +1,67 @@ +/* + * 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 . + * + */ +#ifndef LOGID_TASK_H +#define LOGID_TASK_H + +#include +#include +#include +#include "ExceptionHandler.h" + +namespace logid +{ + class task + { + public: + enum Status + { + Waiting, + Running, + Completed + }; + + explicit task(const std::function& function, + const std::function& + exception_handler={[](std::exception& e) + {ExceptionHandler::Default(e);}}); + + Status getStatus(); + + void run(); // Runs synchronously + void wait(); + + /* This function spawns a new task into the least used worker queue + * and forgets about it. + */ + static void spawn(const std::function& function, + const std::function& + exception_handler={[](std::exception& e) + {ExceptionHandler::Default(e);}}); + + static void autoQueue(std::shared_ptr); + + private: + std::shared_ptr> _function; + std::shared_ptr> + _exception_handler; + std::atomic _status; + std::packaged_task _task_pkg; + }; +} + +#endif //LOGID_TASK_H diff --git a/src/logid/util/worker_thread.cpp b/src/logid/util/worker_thread.cpp new file mode 100644 index 0000000..116e991 --- /dev/null +++ b/src/logid/util/worker_thread.cpp @@ -0,0 +1,88 @@ +/* + * 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 . + * + */ +#include +#include "worker_thread.h" +#include "log.h" +#include "workqueue.h" + +using namespace logid; + +worker_thread::worker_thread(workqueue* parent, std::size_t worker_number) : +_parent (parent), _worker_number (worker_number), _continue_run (false), +_thread (std::make_unique ([this](){ + _run(); }, [this](std::exception& e){ _exception_handler(e); })) +{ + _thread->run(); +} + +worker_thread::~worker_thread() +{ + _continue_run = false; + _queue_cv.notify_all(); + // Block until task is complete + std::unique_lock lock(_busy); + + while(!_queue.empty()) { + _parent->queue(_queue.front()); + _queue.pop(); + } +} + +void worker_thread::queue(std::shared_ptr t) +{ + _queue.push(t); + _queue_cv.notify_all(); +} + +bool worker_thread::busy() +{ + bool not_busy = _busy.try_lock(); + + if(not_busy) + _busy.unlock(); + + return !not_busy; +} + +void worker_thread::_run() +{ + std::unique_lock lock(_run_lock); + _continue_run = true; + while(_continue_run) { + _parent->busyUpdate(); + _queue_cv.wait(lock, [this]{ return !_queue.empty() || + !_continue_run; }); + if(!_continue_run) + return; + std::unique_lock busy_lock(_busy); + while(!_queue.empty()) { + _queue.front()->run(); + _queue.pop(); + } + } +} + +void worker_thread::_exception_handler(std::exception &e) +{ + logPrintf(WARN, "Exception caught on worker thread %d, restarting: %s", + _worker_number, e.what()); + // This action destroys the logid::thread, std::thread should detach safely. + _thread = std::make_unique([this](){ _run(); }, + [this](std::exception& e) { _exception_handler(e); }); + _thread->run(); +} \ No newline at end of file diff --git a/src/logid/util/worker_thread.h b/src/logid/util/worker_thread.h new file mode 100644 index 0000000..c4305e5 --- /dev/null +++ b/src/logid/util/worker_thread.h @@ -0,0 +1,56 @@ +/* + * 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 . + * + */ +#ifndef LOGID_WORKER_THREAD_H +#define LOGID_WORKER_THREAD_H + +#include "mutex_queue.h" +#include "task.h" +#include "thread.h" + +namespace logid +{ + class workqueue; + + class worker_thread + { + public: + worker_thread(workqueue* parent, std::size_t worker_number); + ~worker_thread(); + + void queue(std::shared_ptr t); + + bool busy(); + private: + void _run(); + void _exception_handler(std::exception& e); + + workqueue* _parent; + std::size_t _worker_number; + + std::unique_ptr _thread; + std::mutex _busy; + + std::mutex _run_lock; + std::atomic _continue_run; + std::condition_variable _queue_cv; + + mutex_queue> _queue; + }; +} + +#endif //LOGID_WORKER_THREAD_H diff --git a/src/logid/util/workqueue.cpp b/src/logid/util/workqueue.cpp new file mode 100644 index 0000000..14c1f79 --- /dev/null +++ b/src/logid/util/workqueue.cpp @@ -0,0 +1,151 @@ +/* + * 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 . + * + */ +#include "workqueue.h" +#include "log.h" + +using namespace logid; + +workqueue::workqueue(std::size_t thread_count) : _manager_thread ( + std::make_unique( + [this](){ _run(); } + , [this](std::exception& e){ _exception_handler(e); } + )), _continue_run (false), _worker_count (thread_count) +{ + _workers.reserve(_worker_count); + for(std::size_t i = 0; i < thread_count; i++) + _workers.push_back(std::make_unique(this, i)); + _manager_thread->run(); +} + +workqueue::~workqueue() +{ + stop(); + + while(_workers.empty()) + _workers.pop_back(); + + // Queue should have been empty before, but just confirm here. + while(!_queue.empty()) { + thread::spawn([t=_queue.front()](){ t->run(); }); + _queue.pop(); + } +} + +void workqueue::queue(std::shared_ptr t) +{ + _queue.push(t); + _queue_cv.notify_all(); +} + +void workqueue::busyUpdate() +{ + _busy_cv.notify_all(); +} + +void workqueue::stop() +{ + _continue_run = false; + std::unique_lock lock(_run_lock); +} + +void workqueue::setThreadCount(std::size_t count) +{ + while(_workers.size() < count) + _workers.push_back(std::make_unique(this, + _workers.size())); + + if(_workers.size() > count) { + // Restart manager thread + stop(); + while (_workers.size() > count) + _workers.pop_back(); + _manager_thread = std::make_unique( + [this](){ _run(); } + , [this](std::exception& e){ _exception_handler(e); } + ); + _manager_thread->run(); + } + + _worker_count = count; +} + +std::size_t workqueue::threadCount() const +{ + return _workers.size(); +} + +void workqueue::_run() +{ + using namespace std::chrono_literals; + + std::unique_lock lock(_run_lock); + _continue_run = true; + while(_continue_run) { + _queue_cv.wait(lock, [this]{ return !(_queue.empty()); }); + while(!_queue.empty()) { + + if(_workers.empty()) { + if(_worker_count) + logPrintf(DEBUG, "No workers were found, running task in" + " a new thread."); + thread::spawn([t=_queue.front()](){ t->run(); }); + _queue.pop(); + continue; + } + + auto worker = _workers.begin(); + for(; worker != _workers.end(); worker++) { + if(!(*worker)->busy()) + break; + } + if(worker != _workers.end()) + (*worker)->queue(_queue.front()); + else { + _busy_cv.wait_for(lock, 500ms, [this, &worker]{ + for(worker = _workers.begin(); worker != _workers.end(); + worker++) { + if (!(*worker)->busy()) { + return true; + } + } + return false; + }); + + if(worker != _workers.end()) + (*worker)->queue(_queue.front()); + else{ + // Workers busy, launch in new thread + logPrintf(DEBUG, "All workers were busy for 500ms, " + "running task in new thread."); + thread::spawn([t = _queue.front()]() { t->run(); }); + } + } + _queue.pop(); + } + } +} + +void workqueue::_exception_handler(std::exception &e) +{ + logPrintf(WARN, "Exception caught on workqueue manager thread, " + "restarting: %s" , e.what()); + // This action destroys the logid::thread, std::thread should detach safely. + _manager_thread = std::make_unique([this](){ _run(); }, + [this](std::exception& e) { _exception_handler(e); }); + _manager_thread->run(); +} \ No newline at end of file diff --git a/src/logid/util/workqueue.h b/src/logid/util/workqueue.h new file mode 100644 index 0000000..2234cf3 --- /dev/null +++ b/src/logid/util/workqueue.h @@ -0,0 +1,59 @@ +/* + * 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 . + * + */ +#ifndef LOGID_WORKQUEUE_H +#define LOGID_WORKQUEUE_H + +#include "worker_thread.h" +#include "thread.h" + +namespace logid +{ + class workqueue + { + public: + explicit workqueue(std::size_t thread_count); + ~workqueue(); + + void queue(std::shared_ptr t); + + void busyUpdate(); + + void stop(); + + void setThreadCount(std::size_t count); + std::size_t threadCount() const; + private: + void _run(); + + void _exception_handler(std::exception& e); + std::unique_ptr _manager_thread; + + mutex_queue> _queue; + std::condition_variable _queue_cv; + std::condition_variable _busy_cv; + std::mutex _run_lock; + std::atomic _continue_run; + + std::vector> _workers; + std::size_t _worker_count; + }; + + extern std::unique_ptr global_workqueue; +} + +#endif //LOGID_WORKQUEUE_H From d478ef33094fde27cb70109d016706a6e2dc07af Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 10 Jul 2020 03:20:25 -0400 Subject: [PATCH 54/81] Add error check for bad action type --- src/logid/features/RemapButton.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/logid/features/RemapButton.cpp b/src/logid/features/RemapButton.cpp index 58aa786..b28d091 100644 --- a/src/logid/features/RemapButton.cpp +++ b/src/logid/features/RemapButton.cpp @@ -171,6 +171,9 @@ void RemapButton::Config::_parseButton(libconfig::Setting &setting) } catch(libconfig::SettingNotFoundException& e) { logPrintf(WARN, "Line %d: action is required, ignoring.", setting.getSourceLine()); + } catch(InvalidAction& e) { + logPrintf(WARN, "Line %d: %s is not a valid action, ignoring.", + setting["action"].getSourceLine(), e.what()); } } From 41049deb35be6853788aad406ee14a0fc72cc81d Mon Sep 17 00:00:00 2001 From: pixl Date: Fri, 10 Jul 2020 04:20:15 -0400 Subject: [PATCH 55/81] Fix compiler warnings --- src/logid/Configuration.cpp | 1 - src/logid/actions/Action.cpp | 4 ++-- src/logid/actions/Action.h | 2 ++ src/logid/actions/ToggleHiresScroll.cpp | 9 +-------- src/logid/actions/ToggleHiresScroll.h | 9 +-------- src/logid/actions/ToggleSmartShift.cpp | 8 +------- src/logid/actions/ToggleSmartShift.h | 9 +-------- src/logid/backend/hidpp20/features/ReprogControls.cpp | 1 + src/logid/util/worker_thread.h | 6 +++--- 9 files changed, 12 insertions(+), 37 deletions(-) diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index 0826c0f..5f9a7a9 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -70,7 +70,6 @@ Configuration::Configuration(const std::string& config_file) try { auto& timeout = root["io_timeout"]; if(timeout.isNumber()) { - auto t = timeout.getType(); if(timeout.getType() == Setting::TypeFloat) _io_timeout = duration_cast( duration(timeout)); diff --git a/src/logid/actions/Action.cpp b/src/logid/actions/Action.cpp index 5f52987..6de115d 100644 --- a/src/logid/actions/Action.cpp +++ b/src/logid/actions/Action.cpp @@ -50,9 +50,9 @@ std::shared_ptr Action::makeAction(Device *device, libconfig::Setting if(type == "keypress") return std::make_shared(device, setting); else if(type == "togglesmartshift") - return std::make_shared(device, setting); + return std::make_shared(device); else if(type == "togglehiresscroll") - return std::make_shared(device, setting); + return std::make_shared(device); else throw InvalidAction(type); diff --git a/src/logid/actions/Action.h b/src/logid/actions/Action.h index 6bdb87c..14ff244 100644 --- a/src/logid/actions/Action.h +++ b/src/logid/actions/Action.h @@ -52,6 +52,8 @@ namespace actions { virtual void release() = 0; virtual void move(int16_t x, int16_t y) { + // Suppress unused warning + (void)x; (void)y; } virtual bool pressed() diff --git a/src/logid/actions/ToggleHiresScroll.cpp b/src/logid/actions/ToggleHiresScroll.cpp index 7fbf5ac..0c57d54 100644 --- a/src/logid/actions/ToggleHiresScroll.cpp +++ b/src/logid/actions/ToggleHiresScroll.cpp @@ -17,15 +17,13 @@ */ #include "ToggleHiresScroll.h" #include "../Device.h" -#include "../util/log.h" #include "../util/task.h" #include "../backend/hidpp20/features/ReprogControls.h" using namespace logid::actions; using namespace logid::backend; -ToggleHiresScroll::ToggleHiresScroll(Device *dev, libconfig::Setting &config) : - Action (dev), _config (dev, config) +ToggleHiresScroll::ToggleHiresScroll(Device *dev) : Action (dev) { _hires_scroll = _device->getFeature("hiresscroll"); if(!_hires_scroll) @@ -56,9 +54,4 @@ void ToggleHiresScroll::release() uint8_t ToggleHiresScroll::reprogFlags() const { return hidpp20::ReprogControls::TemporaryDiverted; -} - -ToggleHiresScroll::Config::Config(Device *device, libconfig::Setting &root) : - Action::Config(device) -{ } \ No newline at end of file diff --git a/src/logid/actions/ToggleHiresScroll.h b/src/logid/actions/ToggleHiresScroll.h index 39377f5..bd60f71 100644 --- a/src/logid/actions/ToggleHiresScroll.h +++ b/src/logid/actions/ToggleHiresScroll.h @@ -27,20 +27,13 @@ namespace actions class ToggleHiresScroll : public Action { public: - ToggleHiresScroll(Device* dev, libconfig::Setting& config); + explicit ToggleHiresScroll(Device* dev); virtual void press(); virtual void release(); virtual uint8_t reprogFlags() const; - - class Config : public Action::Config - { - public: - explicit Config(Device* device, libconfig::Setting& root); - }; protected: - Config _config; std::shared_ptr _hires_scroll; }; }} diff --git a/src/logid/actions/ToggleSmartShift.cpp b/src/logid/actions/ToggleSmartShift.cpp index dfe86a9..28885ae 100644 --- a/src/logid/actions/ToggleSmartShift.cpp +++ b/src/logid/actions/ToggleSmartShift.cpp @@ -23,8 +23,7 @@ using namespace logid::actions; using namespace logid::backend; -ToggleSmartShift::ToggleSmartShift(Device *dev, libconfig::Setting &config) : - Action (dev), _config (dev, config) +ToggleSmartShift::ToggleSmartShift(Device *dev) : Action (dev) { _smartshift = _device->getFeature("smartshift"); if(!_smartshift) @@ -56,9 +55,4 @@ void ToggleSmartShift::release() uint8_t ToggleSmartShift::reprogFlags() const { return hidpp20::ReprogControls::TemporaryDiverted; -} - -ToggleSmartShift::Config::Config(Device *device, libconfig::Setting &root) : - Action::Config(device) -{ } \ No newline at end of file diff --git a/src/logid/actions/ToggleSmartShift.h b/src/logid/actions/ToggleSmartShift.h index 3cd8f95..7884f48 100644 --- a/src/logid/actions/ToggleSmartShift.h +++ b/src/logid/actions/ToggleSmartShift.h @@ -27,20 +27,13 @@ namespace actions { class ToggleSmartShift : public Action { public: - ToggleSmartShift(Device* dev, libconfig::Setting& config); + explicit ToggleSmartShift(Device* dev); virtual void press(); virtual void release(); virtual uint8_t reprogFlags() const; - - class Config : public Action::Config - { - public: - explicit Config(Device* device, libconfig::Setting& root); - }; protected: - Config _config; std::shared_ptr _smartshift; }; }} diff --git a/src/logid/backend/hidpp20/features/ReprogControls.cpp b/src/logid/backend/hidpp20/features/ReprogControls.cpp index 4200385..444096f 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.cpp +++ b/src/logid/backend/hidpp20/features/ReprogControls.cpp @@ -116,6 +116,7 @@ ReprogControls::ControlInfo ReprogControls::getControlReporting(uint16_t cid) void ReprogControls::setControlReporting(uint8_t cid, ControlInfo info) { // This function does not exist pre-v4 and cannot be emulated, ignore. + (void)cid; (void)info; // Suppress unused warnings } std::set ReprogControls::divertedButtonEvent( diff --git a/src/logid/util/worker_thread.h b/src/logid/util/worker_thread.h index c4305e5..0c252e1 100644 --- a/src/logid/util/worker_thread.h +++ b/src/logid/util/worker_thread.h @@ -42,13 +42,13 @@ namespace logid workqueue* _parent; std::size_t _worker_number; - std::unique_ptr _thread; - std::mutex _busy; - std::mutex _run_lock; std::atomic _continue_run; std::condition_variable _queue_cv; + std::unique_ptr _thread; + std::mutex _busy; + mutex_queue> _queue; }; } From 0fbeb1e3c90b4e22cacedf81ab3725f774d6b7c2 Mon Sep 17 00:00:00 2001 From: pixl Date: Sat, 11 Jul 2020 16:31:08 -0400 Subject: [PATCH 56/81] Add Gesture support Only supports OnRelease right now. Also, some bugs were spotted while writing this: - Sometimes deadlocks on startup (cause unknown) - Sometimes valid CIDs will be unknown (bug may have been fixed?) --- src/logid/CMakeLists.txt | 3 + src/logid/Configuration.h | 2 +- src/logid/actions/Action.cpp | 3 + src/logid/actions/Action.h | 3 +- src/logid/actions/GestureAction.cpp | 269 ++++++++++++++++++ src/logid/actions/GestureAction.h | 67 +++++ src/logid/actions/gesture/Gesture.cpp | 109 +++++++ src/logid/actions/gesture/Gesture.h | 73 +++++ src/logid/actions/gesture/ReleaseGesture.cpp | 49 ++++ src/logid/actions/gesture/ReleaseGesture.h | 43 +++ .../hidpp20/features/ReprogControls.cpp | 24 +- .../backend/hidpp20/features/ReprogControls.h | 2 + src/logid/features/RemapButton.cpp | 2 +- 13 files changed, 636 insertions(+), 13 deletions(-) create mode 100644 src/logid/actions/GestureAction.cpp create mode 100644 src/logid/actions/GestureAction.h create mode 100644 src/logid/actions/gesture/Gesture.cpp create mode 100644 src/logid/actions/gesture/Gesture.h create mode 100644 src/logid/actions/gesture/ReleaseGesture.cpp create mode 100644 src/logid/actions/gesture/ReleaseGesture.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 6d93d7b..c279ff8 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -24,6 +24,9 @@ add_executable(logid actions/KeypressAction.cpp actions/ToggleHiresScroll.cpp actions/ToggleSmartShift.cpp + actions/GestureAction.cpp + actions/gesture/Gesture.cpp + actions/gesture/ReleaseGesture.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index ed10893..b05da63 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -50,7 +50,7 @@ namespace logid int workerCount() const; private: std::map _device_paths; - std::chrono::milliseconds _io_timeout; + std::chrono::milliseconds _io_timeout = LOGID_DEFAULT_RAWDEVICE_TIMEOUT; int _worker_threads; libconfig::Config _config; }; diff --git a/src/logid/actions/Action.cpp b/src/logid/actions/Action.cpp index 6de115d..1a845ee 100644 --- a/src/logid/actions/Action.cpp +++ b/src/logid/actions/Action.cpp @@ -22,6 +22,7 @@ #include "KeypressAction.h" #include "ToggleSmartShift.h" #include "ToggleHiresScroll.h" +#include "GestureAction.h" using namespace logid; using namespace logid::actions; @@ -53,6 +54,8 @@ std::shared_ptr Action::makeAction(Device *device, libconfig::Setting return std::make_shared(device); else if(type == "togglehiresscroll") return std::make_shared(device); + else if(type == "gestures") + return std::make_shared(device, setting); else throw InvalidAction(type); diff --git a/src/logid/actions/Action.h b/src/logid/actions/Action.h index 14ff244..846abd7 100644 --- a/src/logid/actions/Action.h +++ b/src/logid/actions/Action.h @@ -65,11 +65,10 @@ namespace actions { class Config { - public: + protected: explicit Config(Device* device) : _device (device) { } - protected: Device* _device; }; protected: diff --git a/src/logid/actions/GestureAction.cpp b/src/logid/actions/GestureAction.cpp new file mode 100644 index 0000000..a9b80f4 --- /dev/null +++ b/src/logid/actions/GestureAction.cpp @@ -0,0 +1,269 @@ +/* + * 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 . + * + */ +#include +#include "GestureAction.h" +#include "../Device.h" +#include "../backend/hidpp20/features/ReprogControls.h" + +using namespace logid::actions; +using namespace logid; +using namespace logid::backend; + +GestureAction::Direction GestureAction::toDirection(std::string direction) +{ + std::transform(direction.begin(), direction.end(), direction.begin(), + ::tolower); + if(direction == "up") + return Up; + else if(direction == "down") + return Down; + else if(direction == "left") + return Left; + else if(direction == "right") + return Right; + else if(direction == "none") + return None; + else + throw std::invalid_argument("direction"); +} + +GestureAction::Direction GestureAction::toDirection(int16_t x, int16_t y) +{ + if(x >= 0 && y >= 0) + return x >= y ? Right : Down; + else if(x < 0 && y >= 0) + return -x <= y ? Down : Left; + else if(x <= 0 && y < 0) + return x <= y ? Left : Up; + else + return x <= -y ? Up : Right; +} + +GestureAction::GestureAction(Device* dev, libconfig::Setting& config) : + Action (dev), _config (dev, config) +{ +} + +void GestureAction::press() +{ + _pressed = true; + _x = 0, _y = 0; + for(auto& gesture : _config.gestures()) + gesture.second->press(); +} + +void GestureAction::release() +{ + _pressed = false; + bool threshold_met = false; + + auto d = toDirection(_x, _y); + auto primary_gesture = _config.gestures().find(d); + if(primary_gesture != _config.gestures().end()) { + threshold_met = primary_gesture->second->metThreshold(); + primary_gesture->second->release(true); + } + + for(auto& gesture : _config.gestures()) { + if(gesture.first == d) + continue; + if(!threshold_met) { + if(gesture.second->metThreshold()) { + // If the primary gesture did not meet its threshold, use the + // secondary one. + threshold_met = true; + gesture.second->release(true); + break; + } + } else { + gesture.second->release(false); + } + } + + if(!threshold_met) { + if(_config.noneAction()) { + _config.noneAction()->press(); + _config.noneAction()->release(); + } + } +} + +void GestureAction::move(int16_t x, int16_t y) +{ + auto new_x = _x + x, new_y = _y + y; + + if(abs(x) > 0) { + if(_x < 0 && new_x >= 0) { // Left -> Origin/Right + auto left = _config.gestures().find(Left); + if(left != _config.gestures().end()) + left->second->move(_x); + if(new_x) { // Ignore to origin + auto right = _config.gestures().find(Right); + if(right != _config.gestures().end()) + right->second->move(new_x); + } + } else if(_x > 0 && new_x <= 0) { // Right -> Origin/Left + auto right = _config.gestures().find(Right); + if(right != _config.gestures().end()) + right->second->move(-_x); + if(new_x) { // Ignore to origin + auto left = _config.gestures().find(Left); + if(left != _config.gestures().end()) + left->second->move(-new_x); + } + } else if(new_x < 0) { // Origin/Left to Left + auto left = _config.gestures().find(Left); + if(left != _config.gestures().end()) + left->second->move(-x); + } else if(new_x > 0) { // Origin/Right to Right + auto right = _config.gestures().find(Right); + if(right != _config.gestures().end()) + right->second->move(x); + } + } + + if(abs(y) > 0) { + if(_y > 0 && new_y <= 0) { // Up -> Origin/Down + auto up = _config.gestures().find(Up); + if(up != _config.gestures().end()) + up->second->move(_y); + if(new_y) { // Ignore to origin + auto down = _config.gestures().find(Down); + if(down != _config.gestures().end()) + down->second->move(new_y); + } + } else if(_y < 0 && new_y >= 0) { // Down -> Origin/Up + auto down = _config.gestures().find(Down); + if(down != _config.gestures().end()) + down->second->move(-_y); + if(new_y) { // Ignore to origin + auto up = _config.gestures().find(Up); + if(up != _config.gestures().end()) + up->second->move(-new_y); + } + } else if(new_y < 0) { // Origin/Up to Up + auto up = _config.gestures().find(Up); + if(up != _config.gestures().end()) + up->second->move(-y); + } else if(new_y > 0) {// Origin/Down to Down + auto down = _config.gestures().find(Down); + if(down != _config.gestures().end()) + down->second->move(y); + } + } + + _x = new_x; _y = new_y; +} + +uint8_t GestureAction::reprogFlags() const +{ + return (hidpp20::ReprogControls::TemporaryDiverted | + hidpp20::ReprogControls::RawXYDiverted); +} + +GestureAction::Config::Config(Device* device, libconfig::Setting &root) : + Action::Config(device) +{ + try { + auto& gestures = root.lookup("gestures"); + + if(!gestures.isList()) { + logPrintf(WARN, "Line %d: gestures must be a list, ignoring.", + gestures.getSourceLine()); + return; + } + + int gesture_count = gestures.getLength(); + + for(int i = 0; i < gesture_count; i++) { + if(!gestures[i].isGroup()) { + logPrintf(WARN, "Line %d: gesture must be a group, skipping.", + gestures[i].getSourceLine()); + continue; + } + + Direction d; + try { + auto& direction = gestures[i].lookup("direction"); + if(direction.getType() != libconfig::Setting::TypeString) { + logPrintf(WARN, "Line %d: direction must be a string, " + "skipping.", direction.getSourceLine()); + continue; + } + + try { + d = toDirection(direction); + } catch(std::invalid_argument& e) { + logPrintf(WARN, "Line %d: Invalid direction %s", + direction.getSourceLine(), (const char*)direction); + continue; + } + } catch(libconfig::SettingNotFoundException& e) { + logPrintf(WARN, "Line %d: direction is a required field, " + "skipping.", gestures[i].getSourceLine()); + continue; + } + + if(_gestures.find(d) != _gestures.end() || (d == None && _none_action)) { + logPrintf(WARN, "Line %d: Gesture is already defined for " + "this direction, duplicate ignored.", + gestures[i].getSourceLine()); + continue; + } + + if(d == None) { + try { + _none_action = Action::makeAction(_device, + gestures[i].lookup("action")); + } catch (InvalidAction& e) { + logPrintf(WARN, "Line %d: %s is not a valid action, " + "skipping.", gestures[i].lookup("action") + .getSourceLine(), e.what()); + } catch (libconfig::SettingNotFoundException& e) { + logPrintf(WARN, "Line %d: action is a required field, " + "skipping.", gestures[i].getSourceLine(), + e.what()); + } + continue; + } + + try { + _gestures.emplace(d, Gesture::makeGesture(_device, + gestures[i])); + } catch(InvalidGesture& e) { + logPrintf(WARN, "Line %d: Invalid gesture: %s", + gestures[i].getSourceLine(), e.what()); + } + } + + } catch(libconfig::SettingNotFoundException& e) { + logPrintf(WARN, "Line %d: gestures is a required field, ignoring.", + root.getSourceLine()); + } +} + +std::map>& + GestureAction::Config::gestures() +{ + return _gestures; +} + +std::shared_ptr GestureAction::Config::noneAction() +{ + return _none_action; +} \ No newline at end of file diff --git a/src/logid/actions/GestureAction.h b/src/logid/actions/GestureAction.h new file mode 100644 index 0000000..3928f75 --- /dev/null +++ b/src/logid/actions/GestureAction.h @@ -0,0 +1,67 @@ +/* + * 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 . + * + */ +#ifndef LOGID_ACTION_GESTUREACTION_H +#define LOGID_ACTION_GESTUREACTION_H + +#include +#include +#include "Action.h" +#include "gesture/Gesture.h" + +namespace logid { +namespace actions { + class GestureAction : public Action + { + public: + enum Direction + { + None, + Up, + Down, + Left, + Right + }; + static Direction toDirection(std::string direction); + static Direction toDirection(int16_t x, int16_t y); + + GestureAction(Device* dev, libconfig::Setting& config); + + virtual void press(); + virtual void release(); + virtual void move(int16_t x, int16_t y); + + virtual uint8_t reprogFlags() const; + + class Config : public Action::Config + { + public: + Config(Device* device, libconfig::Setting& root); + std::map>& gestures(); + std::shared_ptr noneAction(); + protected: + std::map> _gestures; + std::shared_ptr _none_action; + }; + + protected: + int16_t _x, _y; + Config _config; + }; +}} + +#endif //LOGID_ACTION_GESTUREACTION_H diff --git a/src/logid/actions/gesture/Gesture.cpp b/src/logid/actions/gesture/Gesture.cpp new file mode 100644 index 0000000..4691d4e --- /dev/null +++ b/src/logid/actions/gesture/Gesture.cpp @@ -0,0 +1,109 @@ +/* + * 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 . + * + */ + +#include +#include "Gesture.h" +#include "../../util/log.h" +#include "ReleaseGesture.h" +#include "../../backend/hidpp20/features/ReprogControls.h" + +using namespace logid::actions; + +Gesture::Gesture(Device *device) : _device (device) +{ +} + +Gesture::Config::Config(Device* device, libconfig::Setting& root, + bool action_required) : _device (device) +{ + if(action_required) { + try { + _action = Action::makeAction(_device, + root.lookup("action")); + } catch (libconfig::SettingNotFoundException &e) { + throw InvalidGesture("action is missing"); + } + + if(_action->reprogFlags() & backend::hidpp20::ReprogControls::RawXYDiverted) + throw InvalidGesture("gesture cannot require RawXY"); + } + + _threshold = LOGID_GESTURE_DEFAULT_THRESHOLD; + try { + auto& threshold = root.lookup("threshold"); + if(threshold.getType() == libconfig::Setting::TypeInt) { + _threshold = (int)threshold; + if(_threshold <= 0) { + _threshold = LOGID_GESTURE_DEFAULT_THRESHOLD; + logPrintf(WARN, "Line %d: threshold must be positive, setting" + "to default (%d)", threshold.getSourceLine(), + _threshold); + } + } else + logPrintf(WARN, "Line %d: threshold must be an integer, setting " + "to default (%d).", threshold.getSourceLine()); + } catch(libconfig::SettingNotFoundException& e) { + // Ignore + } +} + +std::shared_ptr Gesture::makeGesture(Device *device, + libconfig::Setting &setting) +{ + if(!setting.isGroup()) { + logPrintf(WARN, "Line %d: Gesture is not a group, ignoring.", + setting.getSourceLine()); + throw InvalidGesture(); + } + + try { + auto& gesture_mode = setting.lookup("mode"); + + if(gesture_mode.getType() != libconfig::Setting::TypeString) { + logPrintf(WARN, "Line %d: Gesture mode must be a string," + "defaulting to OnRelease.", + gesture_mode.getSourceLine()); + return std::make_shared(device, setting); + } + + std::string type = gesture_mode; + std::transform(type.begin(), type.end(), type.begin(), ::tolower); + + if(type == "onrelease") + return std::make_shared(device, setting); + else { + logPrintf(WARN, "Line %d: Unknown gesture mode %s, defaulting to " + "OnRelease.", gesture_mode.getSourceLine(), + (const char*)gesture_mode); + return std::make_shared(device, setting); + } + + } catch(libconfig::SettingNotFoundException& e) { + return std::make_shared(device, setting); + } +} + +int16_t Gesture::Config::threshold() const +{ + return _threshold; +} + +std::shared_ptr Gesture::Config::action() +{ + return _action; +} \ No newline at end of file diff --git a/src/logid/actions/gesture/Gesture.h b/src/logid/actions/gesture/Gesture.h new file mode 100644 index 0000000..731c50f --- /dev/null +++ b/src/logid/actions/gesture/Gesture.h @@ -0,0 +1,73 @@ +/* + * 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 . + * + */ +#ifndef LOGID_ACTION_GESTURE_H +#define LOGID_ACTION_GESTURE_H + +#include "../Action.h" + +#define LOGID_GESTURE_DEFAULT_THRESHOLD 50 + +namespace logid { +namespace actions +{ + class InvalidGesture : public std::exception + { + public: + explicit InvalidGesture(std::string what="") : _what (what) + { + } + virtual const char* what() + { + return _what.c_str(); + } + private: + std::string _what; + }; + + class Gesture + { + public: + virtual void press() = 0; + virtual void release(bool primary=false) = 0; + virtual void move(int16_t axis) = 0; + + virtual bool metThreshold() const = 0; + + class Config + { + public: + Config(Device* device, libconfig::Setting& root, + bool action_required=true); + virtual int16_t threshold() const; + virtual std::shared_ptr action(); + protected: + Device* _device; + std::shared_ptr _action; + int16_t _threshold; + }; + + static std::shared_ptr makeGesture(Device* device, + libconfig::Setting& setting); + + protected: + explicit Gesture(Device* device); + Device* _device; + }; +}} + +#endif //LOGID_ACTION_GESTURE_H diff --git a/src/logid/actions/gesture/ReleaseGesture.cpp b/src/logid/actions/gesture/ReleaseGesture.cpp new file mode 100644 index 0000000..1e14891 --- /dev/null +++ b/src/logid/actions/gesture/ReleaseGesture.cpp @@ -0,0 +1,49 @@ +/* + * 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 . + * + */ +#include "ReleaseGesture.h" +#include "../../util/log.h" + +using namespace logid::actions; + +ReleaseGesture::ReleaseGesture(Device *device, libconfig::Setting &root) : + Gesture (device), _config (device, root) +{ +} + +void ReleaseGesture::press() +{ + _axis = 0; +} + +void ReleaseGesture::release(bool primary) +{ + if(metThreshold() && primary) { + _config.action()->press(); + _config.action()->release(); + } +} + +void ReleaseGesture::move(int16_t axis) +{ + _axis += axis; +} + +bool ReleaseGesture::metThreshold() const +{ + return _axis >= _config.threshold(); +} \ No newline at end of file diff --git a/src/logid/actions/gesture/ReleaseGesture.h b/src/logid/actions/gesture/ReleaseGesture.h new file mode 100644 index 0000000..b35fda2 --- /dev/null +++ b/src/logid/actions/gesture/ReleaseGesture.h @@ -0,0 +1,43 @@ +/* + * 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 . + * + */ +#ifndef LOGID_ACTION_RELEASEGESTURE_H +#define LOGID_ACTION_RELEASEGESTURE_H + +#include "Gesture.h" + +namespace logid { +namespace actions +{ + class ReleaseGesture : public Gesture + { + public: + ReleaseGesture(Device* device, libconfig::Setting& root); + + virtual void press(); + virtual void release(bool primary=false); + virtual void move(int16_t axis); + + virtual bool metThreshold() const; + + protected: + int16_t _axis; + Gesture::Config _config; + }; +}} + +#endif //LOGID_ACTION_RELEASEGESTURE_H diff --git a/src/logid/backend/hidpp20/features/ReprogControls.cpp b/src/logid/backend/hidpp20/features/ReprogControls.cpp index 444096f..82af1d4 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.cpp +++ b/src/logid/backend/hidpp20/features/ReprogControls.cpp @@ -36,11 +36,11 @@ try { \ } // Define all of the ReprogControls versions -DEFINE_REPROG(ReprogControls, Feature); -DEFINE_REPROG(ReprogControlsV2, ReprogControls); -DEFINE_REPROG(ReprogControlsV2_2, ReprogControlsV2); -DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2); -DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3); +DEFINE_REPROG(ReprogControls, Feature) +DEFINE_REPROG(ReprogControlsV2, ReprogControls) +DEFINE_REPROG(ReprogControlsV2_2, ReprogControlsV2) +DEFINE_REPROG(ReprogControlsV3, ReprogControlsV2_2) +DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3) std::shared_ptr ReprogControls::autoVersion(Device *dev) { @@ -81,10 +81,16 @@ ReprogControls::ControlInfo ReprogControls::getControlInfo(uint8_t index) 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); + if(!_cids_initialized) { + std::unique_lock lock(_cids_populating); + if(!_cids_initialized) { + uint8_t controls = getControlCount(); + for(uint8_t i = 0; i < controls; i++) { + auto info = getControlInfo(i); + _cids.emplace(info.controlID, info); + } + _cids_populating.unlock(); + _cids_initialized = true; } } diff --git a/src/logid/backend/hidpp20/features/ReprogControls.h b/src/logid/backend/hidpp20/features/ReprogControls.h index 143dafd..07567f8 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.h +++ b/src/logid/backend/hidpp20/features/ReprogControls.h @@ -110,6 +110,8 @@ namespace hidpp20 protected: ReprogControls(Device* dev, uint16_t _id); std::map _cids; + bool _cids_initialized = false; + std::mutex _cids_populating; }; class ReprogControlsV2 : public ReprogControls diff --git a/src/logid/features/RemapButton.cpp b/src/logid/features/RemapButton.cpp index b28d091..32fdd6e 100644 --- a/src/logid/features/RemapButton.cpp +++ b/src/logid/features/RemapButton.cpp @@ -56,7 +56,7 @@ void RemapButton::configure() } if((i.second->reprogFlags() & hidpp20::ReprogControls::RawXYDiverted) && - (!_reprog_controls->supportsRawXY() || (info.additionalFlags & + (!_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); From de8e453bd304eb2d5ee50370f4303a35549efc10 Mon Sep 17 00:00:00 2001 From: pixl Date: Sat, 11 Jul 2020 19:04:19 -0400 Subject: [PATCH 57/81] Fix deadlock issue RawDevice would deadlock in some situations because I/O occured while the listener was turning on. This also seems to have fixed logid sometimes not detecting devices on receivers. --- src/logid/backend/dj/Receiver.cpp | 4 +--- src/logid/backend/hidpp/Device.cpp | 4 +--- src/logid/backend/raw/RawDevice.cpp | 15 ++++++++++++++- src/logid/backend/raw/RawDevice.h | 2 ++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp index e32a1dd..64118f5 100644 --- a/src/logid/backend/dj/Receiver.cpp +++ b/src/logid/backend/dj/Receiver.cpp @@ -325,9 +325,7 @@ void Receiver::listen() { if(!_raw_device->isListening()) ///TODO: Kill RawDevice? - thread::spawn({[raw=this->_raw_device]() { - raw->listen(); - }}); + _raw_device->listenAsync(); if(_raw_device->eventHandlers().find("RECV_HIDPP") == _raw_device->eventHandlers().end()) { diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index ae3f061..5e62a66 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -200,9 +200,7 @@ uint16_t Device::pid() const void Device::listen() { if(!_raw_device->isListening()) - thread::spawn({[raw=this->_raw_device]() { - raw->listen(); - }}); + _raw_device->listenAsync(); // Pass all HID++ events with device index to this device. auto handler = std::make_shared(); diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 2905c38..8019aa5 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -23,6 +23,7 @@ #include "../../util/log.h" #include "../hidpp/Report.h" #include "../../Configuration.h" +#include "../../util/thread.h" #include #include @@ -327,8 +328,8 @@ void RawDevice::interruptRead() void RawDevice::listen() { std::lock_guard lock(_listening); - _continue_listen = true; + _listen_condition.notify_all(); while(_continue_listen) { while(!_io_queue.empty()) { auto task = _io_queue.front(); @@ -351,6 +352,18 @@ void RawDevice::listen() _continue_listen = false; } +void RawDevice::listenAsync() +{ + std::mutex listen_check; + std::unique_lock check_lock(listen_check); + thread::spawn({[this]() { listen(); }}); + + // Block until RawDevice is listening + _listen_condition.wait(check_lock, [this](){ + return (bool)_continue_listen; + }); +} + void RawDevice::stopListener() { _continue_listen = false; diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index 3cb1823..4c7dec9 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -54,6 +54,7 @@ namespace raw void interruptRead(); void listen(); + void listenAsync(); void stopListener(); bool isListening(); @@ -74,6 +75,7 @@ namespace raw std::vector rdesc; std::atomic _continue_listen; + std::condition_variable _listen_condition; std::map> _event_handlers; From ecfd03fd216a61338d2b52546225631c3599485e Mon Sep 17 00:00:00 2001 From: pixl Date: Sat, 11 Jul 2020 20:28:00 -0400 Subject: [PATCH 58/81] Add OnInterval/OnFewPixels gesture --- src/logid/CMakeLists.txt | 1 + src/logid/actions/gesture/Gesture.cpp | 3 + src/logid/actions/gesture/IntervalGesture.cpp | 91 +++++++++++++++++++ src/logid/actions/gesture/IntervalGesture.h | 53 +++++++++++ src/logid/actions/gesture/ReleaseGesture.cpp | 1 - 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 src/logid/actions/gesture/IntervalGesture.cpp create mode 100644 src/logid/actions/gesture/IntervalGesture.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index c279ff8..c50a83f 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -27,6 +27,7 @@ add_executable(logid actions/GestureAction.cpp actions/gesture/Gesture.cpp actions/gesture/ReleaseGesture.cpp + actions/gesture/IntervalGesture.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/actions/gesture/Gesture.cpp b/src/logid/actions/gesture/Gesture.cpp index 4691d4e..bebba9b 100644 --- a/src/logid/actions/gesture/Gesture.cpp +++ b/src/logid/actions/gesture/Gesture.cpp @@ -21,6 +21,7 @@ #include "../../util/log.h" #include "ReleaseGesture.h" #include "../../backend/hidpp20/features/ReprogControls.h" +#include "IntervalGesture.h" using namespace logid::actions; @@ -86,6 +87,8 @@ std::shared_ptr Gesture::makeGesture(Device *device, if(type == "onrelease") return std::make_shared(device, setting); + if(type == "oninterval" || type == "onfewpixels") + return std::make_shared(device, setting); else { logPrintf(WARN, "Line %d: Unknown gesture mode %s, defaulting to " "OnRelease.", gesture_mode.getSourceLine(), diff --git a/src/logid/actions/gesture/IntervalGesture.cpp b/src/logid/actions/gesture/IntervalGesture.cpp new file mode 100644 index 0000000..355c230 --- /dev/null +++ b/src/logid/actions/gesture/IntervalGesture.cpp @@ -0,0 +1,91 @@ +/* + * 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 . + * + */ +#include "IntervalGesture.h" +#include "../../util/log.h" + +using namespace logid::actions; + +IntervalGesture::IntervalGesture(Device *device, libconfig::Setting &root) : + Gesture (device), _config (device, root) +{ +} + +void IntervalGesture::press() +{ + _axis = 0; + _interval_pass_count = 0; +} + +void IntervalGesture::release(bool primary) +{ + // Do nothing + (void)primary; // Suppress unused warning +} + +void IntervalGesture::move(int16_t axis) +{ + _axis += axis; + if(_axis < _config.threshold()) + return; + + int16_t new_interval_count = (_axis - _config.threshold())/ + _config.interval(); + if(new_interval_count > _interval_pass_count) { + _config.action()->press(); + _config.action()->release(); + } + _interval_pass_count = new_interval_count; +} + +bool IntervalGesture::metThreshold() const +{ + return _axis >= _config.threshold(); +} + +IntervalGesture::Config::Config(Device *device, libconfig::Setting &setting) : + Gesture::Config(device, setting) +{ + try { + auto& interval = setting.lookup("interval"); + if(interval.getType() != libconfig::Setting::TypeInt) { + logPrintf(WARN, "Line %d: interval must be an integer, skipping.", + interval.getSourceLine()); + throw InvalidGesture(); + } + _interval = (int)interval; + } catch(libconfig::SettingNotFoundException& e) { + try { + // pixels is an alias for interval + auto& interval = setting.lookup("pixels"); + if(interval.getType() != libconfig::Setting::TypeInt) { + logPrintf(WARN, "Line %d: pixels must be an integer, skipping.", + interval.getSourceLine()); + throw InvalidGesture(); + } + _interval = (int)interval; + } catch(libconfig::SettingNotFoundException& e) { + logPrintf(WARN, "Line %d: interval is a required field, skipping.", + setting.getSourceLine()); + } + } +} + +int16_t IntervalGesture::Config::interval() const +{ + return _interval; +} \ No newline at end of file diff --git a/src/logid/actions/gesture/IntervalGesture.h b/src/logid/actions/gesture/IntervalGesture.h new file mode 100644 index 0000000..29dcb47 --- /dev/null +++ b/src/logid/actions/gesture/IntervalGesture.h @@ -0,0 +1,53 @@ +/* + * 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 . + * + */ +#ifndef LOGID_ACTION_INTERVALGESTURE_H +#define LOGID_ACTION_INTERVALGESTURE_H + +#include "Gesture.h" + +namespace logid { +namespace actions +{ + class IntervalGesture : public Gesture + { + public: + IntervalGesture(Device* device, libconfig::Setting& root); + + virtual void press(); + virtual void release(bool primary=false); + virtual void move(int16_t axis); + + virtual bool metThreshold() const; + + class Config : public Gesture::Config + { + public: + Config(Device* device, libconfig::Setting& setting); + int16_t interval() const; + private: + int16_t _interval; + }; + + protected: + int16_t _axis; + int16_t _interval_pass_count; + Config _config; + }; +}} + +#endif //LOGID_ACTION_INTERVALGESTURE_H diff --git a/src/logid/actions/gesture/ReleaseGesture.cpp b/src/logid/actions/gesture/ReleaseGesture.cpp index 1e14891..52b3ea1 100644 --- a/src/logid/actions/gesture/ReleaseGesture.cpp +++ b/src/logid/actions/gesture/ReleaseGesture.cpp @@ -16,7 +16,6 @@ * */ #include "ReleaseGesture.h" -#include "../../util/log.h" using namespace logid::actions; From 10bb10e5c54919d7231a3ed79f967ad6a31c275e Mon Sep 17 00:00:00 2001 From: pixl Date: Sat, 11 Jul 2020 23:14:32 -0400 Subject: [PATCH 59/81] Add Axis gesture --- src/logid/CMakeLists.txt | 1 + src/logid/InputDevice.cpp | 2 +- src/logid/actions/gesture/AxisGesture.cpp | 126 ++++++++++++++++++++++ src/logid/actions/gesture/AxisGesture.h | 55 ++++++++++ src/logid/actions/gesture/Gesture.cpp | 7 +- 5 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 src/logid/actions/gesture/AxisGesture.cpp create mode 100644 src/logid/actions/gesture/AxisGesture.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index c50a83f..55f8f74 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(logid actions/gesture/Gesture.cpp actions/gesture/ReleaseGesture.cpp actions/gesture/IntervalGesture.cpp + actions/gesture/AxisGesture.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/InputDevice.cpp b/src/logid/InputDevice.cpp index 972d127..041e3f4 100644 --- a/src/logid/InputDevice.cpp +++ b/src/logid/InputDevice.cpp @@ -87,7 +87,7 @@ uint InputDevice::toKeyCode(std::string name) uint InputDevice::toAxisCode(std::string name) { - return _toEventCode(EV_KEY, std::move(name)); + return _toEventCode(EV_REL, std::move(name)); } uint InputDevice::_toEventCode(uint type, const std::string& name) diff --git a/src/logid/actions/gesture/AxisGesture.cpp b/src/logid/actions/gesture/AxisGesture.cpp new file mode 100644 index 0000000..615c893 --- /dev/null +++ b/src/logid/actions/gesture/AxisGesture.cpp @@ -0,0 +1,126 @@ +/* + * 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 . + * + */ +#include +#include "AxisGesture.h" +#include "../../InputDevice.h" +#include "../../util/log.h" + +using namespace logid::actions; + +AxisGesture::AxisGesture(Device *device, libconfig::Setting &root) : + Gesture (device), _config (device, root) +{ +} + +void AxisGesture::press() +{ + _axis = 0; + _axis_remainder = 0; +} + +void AxisGesture::release(bool primary) +{ + // Do nothing + (void)primary; // Suppress unused warning +} + +void AxisGesture::move(int16_t axis) +{ + int16_t new_axis = _axis + axis; + if(new_axis > _config.threshold()) { + double move = axis; + if(_axis < _config.threshold()) + move = new_axis - _config.threshold(); + bool negative_multiplier = _config.multiplier() < 0; + if(negative_multiplier) + move *= -_config.multiplier(); + else + move *= _config.multiplier(); + + double move_floor = floor(move); + _axis_remainder = move - move_floor; + if(_axis_remainder >= 1) { + double int_remainder = floor(_axis_remainder); + move_floor += int_remainder; + _axis_remainder -= int_remainder; + } + + if(negative_multiplier) + move_floor = -move_floor; + + virtual_input->moveAxis(_config.axis(), move_floor); + } + _axis = new_axis; +} + +bool AxisGesture::metThreshold() const +{ + return _axis >= _config.threshold(); +} + +AxisGesture::Config::Config(Device *device, libconfig::Setting &setting) : + Gesture::Config(device, setting, false) +{ + try { + auto& axis = setting.lookup("axis"); + if(axis.isNumber()) { + _axis = axis; + } else if(axis.getType() == libconfig::Setting::TypeString) { + try { + _axis = virtual_input->toAxisCode(axis); + } catch(InputDevice::InvalidEventCode& e) { + logPrintf(WARN, "Line %d: Invalid axis %s, skipping." + , axis.getSourceLine(), axis.c_str()); + } + } else { + logPrintf(WARN, "Line %d: axis must be string or int, skipping.", + axis.getSourceLine(), axis.c_str()); + throw InvalidGesture(); + } + } catch(libconfig::SettingNotFoundException& e) { + logPrintf(WARN, "Line %d: axis is a required field, skippimg.", + setting.getSourceLine()); + throw InvalidGesture(); + } + + try { + auto& multiplier = setting.lookup("axis_multiplier"); + if(multiplier.isNumber()) { + if(multiplier.getType() == libconfig::Setting::TypeFloat) + _multiplier = multiplier; + else + _multiplier = (int)multiplier; + } else { + logPrintf(WARN, "Line %d: axis_multiplier must be a number, " + "setting to default (1).", + multiplier.getSourceLine()); + } + } catch(libconfig::SettingNotFoundException& e) { + // Ignore + } +} + +unsigned int AxisGesture::Config::axis() const +{ + return _axis; +} + +double AxisGesture::Config::multiplier() const +{ + return _multiplier; +} \ No newline at end of file diff --git a/src/logid/actions/gesture/AxisGesture.h b/src/logid/actions/gesture/AxisGesture.h new file mode 100644 index 0000000..2b69d41 --- /dev/null +++ b/src/logid/actions/gesture/AxisGesture.h @@ -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 . + * + */ +#ifndef LOGID_ACTION_AXISGESTURE_H +#define LOGID_ACTION_AXISGESTURE_H + +#include "Gesture.h" + +namespace logid { + namespace actions + { + class AxisGesture : public Gesture + { + public: + AxisGesture(Device* device, libconfig::Setting& root); + + virtual void press(); + virtual void release(bool primary=false); + virtual void move(int16_t axis); + + virtual bool metThreshold() const; + + class Config : public Gesture::Config + { + public: + Config(Device* device, libconfig::Setting& setting); + unsigned int axis() const; + double multiplier() const; + private: + unsigned int _axis; + double _multiplier = 1; + }; + + protected: + int16_t _axis; + double _axis_remainder; + Config _config; + }; + }} + +#endif //LOGID_ACTION_AXISGESTURE_H diff --git a/src/logid/actions/gesture/Gesture.cpp b/src/logid/actions/gesture/Gesture.cpp index bebba9b..04b1460 100644 --- a/src/logid/actions/gesture/Gesture.cpp +++ b/src/logid/actions/gesture/Gesture.cpp @@ -22,6 +22,7 @@ #include "ReleaseGesture.h" #include "../../backend/hidpp20/features/ReprogControls.h" #include "IntervalGesture.h" +#include "AxisGesture.h" using namespace logid::actions; @@ -51,7 +52,7 @@ Gesture::Config::Config(Device* device, libconfig::Setting& root, _threshold = (int)threshold; if(_threshold <= 0) { _threshold = LOGID_GESTURE_DEFAULT_THRESHOLD; - logPrintf(WARN, "Line %d: threshold must be positive, setting" + logPrintf(WARN, "Line %d: threshold must be positive, setting " "to default (%d)", threshold.getSourceLine(), _threshold); } @@ -87,8 +88,10 @@ std::shared_ptr Gesture::makeGesture(Device *device, if(type == "onrelease") return std::make_shared(device, setting); - if(type == "oninterval" || type == "onfewpixels") + else if(type == "oninterval" || type == "onfewpixels") return std::make_shared(device, setting); + else if(type == "axis") + return std::make_shared(device, setting); else { logPrintf(WARN, "Line %d: Unknown gesture mode %s, defaulting to " "OnRelease.", gesture_mode.getSourceLine(), From 4117d71c9d58f93f610e1cf7173be39baa23dff1 Mon Sep 17 00:00:00 2001 From: pixl Date: Sat, 11 Jul 2020 23:21:15 -0400 Subject: [PATCH 60/81] Add NoPress gesture mode --- src/logid/CMakeLists.txt | 1 + src/logid/actions/gesture/Gesture.cpp | 3 ++ src/logid/actions/gesture/NullGesture.cpp | 46 +++++++++++++++++++++++ src/logid/actions/gesture/NullGesture.h | 42 +++++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 src/logid/actions/gesture/NullGesture.cpp create mode 100644 src/logid/actions/gesture/NullGesture.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 55f8f74..c1853a8 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(logid actions/gesture/ReleaseGesture.cpp actions/gesture/IntervalGesture.cpp actions/gesture/AxisGesture.cpp + actions/gesture/NullGesture.cpp backend/Error.cpp backend/raw/DeviceMonitor.cpp backend/raw/RawDevice.cpp diff --git a/src/logid/actions/gesture/Gesture.cpp b/src/logid/actions/gesture/Gesture.cpp index 04b1460..92a21c8 100644 --- a/src/logid/actions/gesture/Gesture.cpp +++ b/src/logid/actions/gesture/Gesture.cpp @@ -23,6 +23,7 @@ #include "../../backend/hidpp20/features/ReprogControls.h" #include "IntervalGesture.h" #include "AxisGesture.h" +#include "NullGesture.h" using namespace logid::actions; @@ -92,6 +93,8 @@ std::shared_ptr Gesture::makeGesture(Device *device, return std::make_shared(device, setting); else if(type == "axis") return std::make_shared(device, setting); + else if(type == "nopress") + return std::make_shared(device, setting); else { logPrintf(WARN, "Line %d: Unknown gesture mode %s, defaulting to " "OnRelease.", gesture_mode.getSourceLine(), diff --git a/src/logid/actions/gesture/NullGesture.cpp b/src/logid/actions/gesture/NullGesture.cpp new file mode 100644 index 0000000..c97b45d --- /dev/null +++ b/src/logid/actions/gesture/NullGesture.cpp @@ -0,0 +1,46 @@ +/* + * 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 . + * + */ +#include "NullGesture.h" + +using namespace logid::actions; + +NullGesture::NullGesture(Device *device, libconfig::Setting& setting) : + Gesture (device), _config (device, setting, false) +{ +} + +void NullGesture::press() +{ + _axis = 0; +} + +void NullGesture::release(bool primary) +{ + // Do nothing + (void)primary; // Suppress unused warning +} + +void NullGesture::move(int16_t axis) +{ + _axis += axis; +} + +bool NullGesture::metThreshold() const +{ + return _axis > _config.threshold(); +} \ No newline at end of file diff --git a/src/logid/actions/gesture/NullGesture.h b/src/logid/actions/gesture/NullGesture.h new file mode 100644 index 0000000..9ea63c5 --- /dev/null +++ b/src/logid/actions/gesture/NullGesture.h @@ -0,0 +1,42 @@ +/* + * 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 . + * + */ +#ifndef LOGID_ACTION_NULLGESTURE_H +#define LOGID_ACTION_NULLGESTURE_H + +#include "Gesture.h" + +namespace logid { +namespace actions +{ + class NullGesture : public Gesture + { + public: + NullGesture(Device* device, libconfig::Setting& setting); + + virtual void press(); + virtual void release(bool primary=false); + virtual void move(int16_t axis); + + virtual bool metThreshold() const; + protected: + int16_t _axis; + Gesture::Config _config; + }; +}} + +#endif //LOGID_ACTION_NULLGESTURE_H From 21b7455919551ba91f6b649657e1d155031665be Mon Sep 17 00:00:00 2001 From: pixl Date: Sat, 11 Jul 2020 23:29:07 -0400 Subject: [PATCH 61/81] Add NullAction support --- src/logid/CMakeLists.txt | 1 + src/logid/actions/Action.cpp | 3 +++ src/logid/actions/NullAction.cpp | 41 ++++++++++++++++++++++++++++++++ src/logid/actions/NullAction.h | 39 ++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 src/logid/actions/NullAction.cpp create mode 100644 src/logid/actions/NullAction.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index c1853a8..f544666 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable(logid features/HiresScroll.cpp features/RemapButton.cpp actions/Action.cpp + actions/NullAction.cpp actions/KeypressAction.cpp actions/ToggleHiresScroll.cpp actions/ToggleSmartShift.cpp diff --git a/src/logid/actions/Action.cpp b/src/logid/actions/Action.cpp index 1a845ee..b1cd85b 100644 --- a/src/logid/actions/Action.cpp +++ b/src/logid/actions/Action.cpp @@ -23,6 +23,7 @@ #include "ToggleSmartShift.h" #include "ToggleHiresScroll.h" #include "GestureAction.h" +#include "NullAction.h" using namespace logid; using namespace logid::actions; @@ -56,6 +57,8 @@ std::shared_ptr Action::makeAction(Device *device, libconfig::Setting return std::make_shared(device); else if(type == "gestures") return std::make_shared(device, setting); + else if(type == "none") + return std::make_shared(device); else throw InvalidAction(type); diff --git a/src/logid/actions/NullAction.cpp b/src/logid/actions/NullAction.cpp new file mode 100644 index 0000000..8cf875f --- /dev/null +++ b/src/logid/actions/NullAction.cpp @@ -0,0 +1,41 @@ +/* + * 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 . + * + */ +#include "NullAction.h" +#include "../Device.h" +#include "../backend/hidpp20/features/ReprogControls.h" + +using namespace logid::actions; + +NullAction::NullAction(Device* device) : Action(device) +{ +} + +void NullAction::press() +{ + _pressed = true; +} + +void NullAction::release() +{ + _pressed = false; +} + +uint8_t NullAction::reprogFlags() const +{ + return backend::hidpp20::ReprogControls::TemporaryDiverted; +} \ No newline at end of file diff --git a/src/logid/actions/NullAction.h b/src/logid/actions/NullAction.h new file mode 100644 index 0000000..2d2d0ef --- /dev/null +++ b/src/logid/actions/NullAction.h @@ -0,0 +1,39 @@ +/* + * 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 . + * + */ +#ifndef LOGID_ACTION_NULL_H +#define LOGID_ACTION_NULL_H + +#include "Action.h" + +namespace logid { +namespace actions +{ + class NullAction : public Action + { + public: + explicit NullAction(Device* device); + + virtual void press(); + virtual void release(); + + virtual uint8_t reprogFlags() const; + }; +}} + + +#endif //LOGID_ACTION_NULL_H From 949a1ee2839e2b8e4ad2a9071694ac1d3eab2107 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 12 Jul 2020 00:06:33 -0400 Subject: [PATCH 62/81] Add CycleDPI action --- src/logid/CMakeLists.txt | 1 + src/logid/actions/Action.cpp | 3 + src/logid/actions/CycleDPI.cpp | 133 +++++++++++++++++++++++++ src/logid/actions/CycleDPI.h | 56 +++++++++++ src/logid/actions/ToggleSmartShift.cpp | 5 +- src/logid/features/DPI.cpp | 10 ++ src/logid/features/DPI.h | 3 + 7 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 src/logid/actions/CycleDPI.cpp create mode 100644 src/logid/actions/CycleDPI.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index f544666..32746d4 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -25,6 +25,7 @@ add_executable(logid actions/KeypressAction.cpp actions/ToggleHiresScroll.cpp actions/ToggleSmartShift.cpp + actions/CycleDPI.cpp actions/GestureAction.cpp actions/gesture/Gesture.cpp actions/gesture/ReleaseGesture.cpp diff --git a/src/logid/actions/Action.cpp b/src/logid/actions/Action.cpp index b1cd85b..41cf325 100644 --- a/src/logid/actions/Action.cpp +++ b/src/logid/actions/Action.cpp @@ -24,6 +24,7 @@ #include "ToggleHiresScroll.h" #include "GestureAction.h" #include "NullAction.h" +#include "CycleDPI.h" using namespace logid; using namespace logid::actions; @@ -57,6 +58,8 @@ std::shared_ptr Action::makeAction(Device *device, libconfig::Setting return std::make_shared(device); else if(type == "gestures") return std::make_shared(device, setting); + else if(type == "cycledpi") + return std::make_shared(device, setting); else if(type == "none") return std::make_shared(device); else diff --git a/src/logid/actions/CycleDPI.cpp b/src/logid/actions/CycleDPI.cpp new file mode 100644 index 0000000..19b729c --- /dev/null +++ b/src/logid/actions/CycleDPI.cpp @@ -0,0 +1,133 @@ +/* + * 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 . + * + */ +#include "CycleDPI.h" +#include "../Device.h" +#include "../util/task.h" +#include "../util/log.h" +#include "../backend/hidpp20/Error.h" +#include "../backend/hidpp20/features/ReprogControls.h" + +using namespace logid::actions; +using namespace libconfig; + +CycleDPI::CycleDPI(Device* device, libconfig::Setting& setting) : + Action (device), _config (device, setting) +{ + _dpi = _device->getFeature("dpi"); + if(!_dpi) + logPrintf(WARN, "%s:%d: DPI feature not found, cannot use " + "CycleDPI action.", + _device->hidpp20().devicePath().c_str(), + _device->hidpp20().deviceIndex()); +} + +void CycleDPI::press() +{ + _pressed = true; + if(_dpi && !_config.empty()) { + task::spawn([this](){ + uint16_t dpi = _config.nextDPI(); + try { + _dpi->setDPI(dpi, _config.sensor()); + } catch (backend::hidpp20::Error& e) { + if(e.code() == backend::hidpp20::Error::InvalidArgument) + logPrintf(WARN, "%s:%d: Could not set DPI to %d for " + "sensor %d", _device->hidpp20().devicePath().c_str(), + _device->hidpp20().deviceIndex(), dpi, + _config.sensor()); + else + throw e; + } + }); + } +} + +void CycleDPI::release() +{ + _pressed = false; +} + +uint8_t CycleDPI::reprogFlags() const +{ + return backend::hidpp20::ReprogControls::TemporaryDiverted; +} + +CycleDPI::Config::Config(Device *device, libconfig::Setting &config) : + Action::Config(device), _current_index (0), _sensor (0) +{ + if(!config.isGroup()) { + logPrintf(WARN, "Line %d: action must be an object, skipping.", + config.getSourceLine()); + return; + } + + try { + auto& sensor = config.lookup("sensor"); + if(sensor.getType() != Setting::TypeInt) + logPrintf(WARN, "Line %d: sensor must be an integer", + sensor.getSourceLine()); + _sensor = (int)sensor; + } catch(libconfig::SettingNotFoundException& e) { + // Ignore + } + + try { + auto& dpis = config.lookup("dpis"); + if(!dpis.isList() && !dpis.isArray()) { + logPrintf(WARN, "Line %d: dpis must be a list or array, skipping.", + dpis.getSourceLine()); + return; + } + + int dpi_count = dpis.getLength(); + for(int i = 0; i < dpi_count; i++) { + if(dpis[i].getType() != Setting::TypeInt) { + logPrintf(WARN, "Line %d: dpis must be integers, skipping.", + dpis[i].getSourceLine()); + if(dpis.isList()) + continue; + else + break; + } + + _dpis.push_back((int)(dpis[i])); + } + + } catch (libconfig::SettingNotFoundException& e) { + logPrintf(WARN, "Line %d: dpis is a required field, skipping.", + config.getSourceLine()); + } +} + +uint16_t CycleDPI::Config::nextDPI() +{ + uint16_t dpi = _dpis[_current_index++]; + if(_current_index >= _dpis.size()) + _current_index = 0; + return dpi; +} + +bool CycleDPI::Config::empty() const +{ + return _dpis.empty(); +} + +uint8_t CycleDPI::Config::sensor() const +{ + return _sensor; +} \ No newline at end of file diff --git a/src/logid/actions/CycleDPI.h b/src/logid/actions/CycleDPI.h new file mode 100644 index 0000000..b6a5017 --- /dev/null +++ b/src/logid/actions/CycleDPI.h @@ -0,0 +1,56 @@ +/* + * 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 . + * + */ +#ifndef LOGID_ACTION_CYCLEDPI_H +#define LOGID_ACTION_CYCLEDPI_H + +#include +#include "Action.h" +#include "../features/DPI.h" + +namespace logid { +namespace actions { + class CycleDPI : public Action + { + public: + explicit CycleDPI(Device* device, libconfig::Setting& setting); + + virtual void press(); + virtual void release(); + + virtual uint8_t reprogFlags() const; + + class Config : public Action::Config + { + public: + Config(Device* device, libconfig::Setting& setting); + uint16_t nextDPI(); + bool empty() const; + uint8_t sensor() const; + private: + std::size_t _current_index; + std::vector _dpis; + uint8_t _sensor; + }; + + protected: + Config _config; + std::shared_ptr _dpi; + }; +}} + +#endif //LOGID_ACTION_CYCLEDPI_H \ No newline at end of file diff --git a/src/logid/actions/ToggleSmartShift.cpp b/src/logid/actions/ToggleSmartShift.cpp index 28885ae..5e4fd6e 100644 --- a/src/logid/actions/ToggleSmartShift.cpp +++ b/src/logid/actions/ToggleSmartShift.cpp @@ -30,14 +30,13 @@ ToggleSmartShift::ToggleSmartShift(Device *dev) : Action (dev) logPrintf(WARN, "%s:%d: SmartShift feature not found, cannot use " "ToggleSmartShift action.", _device->hidpp20().devicePath().c_str(), - _device->hidpp20().devicePath().c_str()); + _device->hidpp20().deviceIndex()); } void ToggleSmartShift::press() { _pressed = true; - if(_smartshift) - { + if(_smartshift) { task::spawn([ss=this->_smartshift](){ auto status = ss->getStatus(); status.setActive = true; diff --git a/src/logid/features/DPI.cpp b/src/logid/features/DPI.cpp index 5d1bfd2..ffc5482 100644 --- a/src/logid/features/DPI.cpp +++ b/src/logid/features/DPI.cpp @@ -80,6 +80,16 @@ void DPI::listen() { } +uint16_t DPI::getDPI(uint8_t sensor) +{ + return _adjustable_dpi.getSensorDPI(sensor); +} + +void DPI::setDPI(uint16_t dpi, uint8_t sensor) +{ + _adjustable_dpi.setSensorDPI(sensor, dpi); +} + /* Some devices have multiple sensors, but an older config format * only supports a single DPI. The dpi setting can be an array or * an integer. diff --git a/src/logid/features/DPI.h b/src/logid/features/DPI.h index b4c5ca6..78f102f 100644 --- a/src/logid/features/DPI.h +++ b/src/logid/features/DPI.h @@ -31,6 +31,9 @@ namespace features virtual void configure(); virtual void listen(); + uint16_t getDPI(uint8_t sensor=0); + void setDPI(uint16_t dpi, uint8_t sensor=0); + class Config : public DeviceFeature::Config { public: From b00b4645e4f3d7c56613b429cc91ebc8b7aa0bde Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 12 Jul 2020 00:34:23 -0400 Subject: [PATCH 63/81] Add ChangeDPI action --- src/logid/CMakeLists.txt | 1 + src/logid/actions/Action.cpp | 3 + src/logid/actions/ChangeDPI.cpp | 109 ++++++++++++++++++++++++++++++++ src/logid/actions/ChangeDPI.h | 54 ++++++++++++++++ src/logid/features/DPI.cpp | 30 +++++++-- src/logid/features/DPI.h | 1 + 6 files changed, 191 insertions(+), 7 deletions(-) create mode 100644 src/logid/actions/ChangeDPI.cpp create mode 100644 src/logid/actions/ChangeDPI.h diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index 32746d4..de51a75 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable(logid actions/ToggleHiresScroll.cpp actions/ToggleSmartShift.cpp actions/CycleDPI.cpp + actions/ChangeDPI.cpp actions/GestureAction.cpp actions/gesture/Gesture.cpp actions/gesture/ReleaseGesture.cpp diff --git a/src/logid/actions/Action.cpp b/src/logid/actions/Action.cpp index 41cf325..307f5df 100644 --- a/src/logid/actions/Action.cpp +++ b/src/logid/actions/Action.cpp @@ -25,6 +25,7 @@ #include "GestureAction.h" #include "NullAction.h" #include "CycleDPI.h" +#include "ChangeDPI.h" using namespace logid; using namespace logid::actions; @@ -60,6 +61,8 @@ std::shared_ptr Action::makeAction(Device *device, libconfig::Setting return std::make_shared(device, setting); else if(type == "cycledpi") return std::make_shared(device, setting); + else if(type == "changedpi") + return std::make_shared(device, setting); else if(type == "none") return std::make_shared(device); else diff --git a/src/logid/actions/ChangeDPI.cpp b/src/logid/actions/ChangeDPI.cpp new file mode 100644 index 0000000..faab6b9 --- /dev/null +++ b/src/logid/actions/ChangeDPI.cpp @@ -0,0 +1,109 @@ +/* + * 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 . + * + */ +#include "ChangeDPI.h" +#include "../Device.h" +#include "../util/task.h" +#include "../util/log.h" +#include "../backend/hidpp20/Error.h" +#include "../backend/hidpp20/features/ReprogControls.h" + +using namespace logid::actions; + +ChangeDPI::ChangeDPI(Device *device, libconfig::Setting &setting) : + Action(device), _config(device, setting) +{ + _dpi = _device->getFeature("dpi"); + if(!_dpi) + logPrintf(WARN, "%s:%d: DPI feature not found, cannot use " + "ChangeDPI action.", + _device->hidpp20().devicePath().c_str(), + _device->hidpp20().deviceIndex()); +} + +void ChangeDPI::press() +{ + _pressed = true; + if(_dpi) { + task::spawn([this]{ + try { + uint16_t last_dpi = _dpi->getDPI(_config.sensor()); + _dpi->setDPI(last_dpi + _config.interval(), _config.sensor()); + } catch (backend::hidpp20::Error& e) { + if(e.code() == backend::hidpp20::Error::InvalidArgument) + logPrintf(WARN, "%s:%d: Could not get/set DPI for sensor " + "%d", + _device->hidpp20().devicePath().c_str(), + _device->hidpp20().deviceIndex(), + _config.sensor()); + else + throw e; + } + }); + } +} + +void ChangeDPI::release() +{ + _pressed = false; +} + +uint8_t ChangeDPI::reprogFlags() const +{ + return backend::hidpp20::ReprogControls::TemporaryDiverted; +} + +ChangeDPI::Config::Config(Device *device, libconfig::Setting &config) : + Action::Config(device), _interval (0), _sensor (0) +{ + if(!config.isGroup()) { + logPrintf(WARN, "Line %d: action must be an object, skipping.", + config.getSourceLine()); + return; + } + + try { + auto& inc = config.lookup("inc"); + if(inc.getType() != libconfig::Setting::TypeInt) + logPrintf(WARN, "Line %d: inc must be an integer", + inc.getSourceLine()); + _interval = (int)inc; + } catch(libconfig::SettingNotFoundException& e) { + logPrintf(WARN, "Line %d: inc is a required field, skipping.", + config.getSourceLine()); + } + + try { + auto& sensor = config.lookup("sensor"); + if(sensor.getType() != libconfig::Setting::TypeInt) + logPrintf(WARN, "Line %d: sensor must be an integer", + sensor.getSourceLine()); + _sensor = (int)sensor; + } catch(libconfig::SettingNotFoundException& e) { + // Ignore + } +} + +uint16_t ChangeDPI::Config::interval() const +{ + return _interval; +} + +uint8_t ChangeDPI::Config::sensor() const +{ + return _sensor; +} \ No newline at end of file diff --git a/src/logid/actions/ChangeDPI.h b/src/logid/actions/ChangeDPI.h new file mode 100644 index 0000000..4d9e04f --- /dev/null +++ b/src/logid/actions/ChangeDPI.h @@ -0,0 +1,54 @@ +/* + * 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 . + * + */ +#ifndef LOGID_ACTION_CHANGEDPI_H +#define LOGID_ACTION_CHANGEDPI_H + +#include +#include "Action.h" +#include "../features/DPI.h" + +namespace logid { + namespace actions { + class ChangeDPI : public Action + { + public: + explicit ChangeDPI(Device* device, libconfig::Setting& setting); + + virtual void press(); + virtual void release(); + + virtual uint8_t reprogFlags() const; + + class Config : public Action::Config + { + public: + Config(Device* device, libconfig::Setting& setting); + uint16_t interval() const; + uint8_t sensor() const; + private: + uint16_t _interval; + uint8_t _sensor; + }; + + protected: + Config _config; + std::shared_ptr _dpi; + }; + }} + +#endif //LOGID_ACTION_CHANGEDPI_H \ No newline at end of file diff --git a/src/logid/features/DPI.cpp b/src/logid/features/DPI.cpp index ffc5482..435cdb8 100644 --- a/src/logid/features/DPI.cpp +++ b/src/logid/features/DPI.cpp @@ -66,12 +66,20 @@ DPI::DPI(Device* device) : DeviceFeature(device), _config (device), void DPI::configure() { const uint8_t sensors = _adjustable_dpi.getSensorCount(); - for(uint8_t i = 0; i < _config.getSensorCount() && i < sensors; i++) { - auto dpi = _config.getDPI(i); - if(dpi) { - auto dpi_list = _adjustable_dpi.getSensorDPIList(i); - _adjustable_dpi.setSensorDPI(i, getClosestDPI(dpi_list, - dpi)); + for(uint8_t i = 0; i < _config.getSensorCount(); i++) { + hidpp20::AdjustableDPI::SensorDPIList dpi_list; + if(_dpi_lists.size() <= i) { + dpi_list = _adjustable_dpi.getSensorDPIList(i); + _dpi_lists.push_back(dpi_list); + } else { + dpi_list = _dpi_lists[i]; + } + if(i < sensors) { + auto dpi = _config.getDPI(i); + if(dpi) { + _adjustable_dpi.setSensorDPI(i, getClosestDPI(dpi_list, + dpi)); + } } } } @@ -87,7 +95,15 @@ uint16_t DPI::getDPI(uint8_t sensor) void DPI::setDPI(uint16_t dpi, uint8_t sensor) { - _adjustable_dpi.setSensorDPI(sensor, dpi); + hidpp20::AdjustableDPI::SensorDPIList dpi_list; + if(_dpi_lists.size() <= sensor) { + dpi_list = _adjustable_dpi.getSensorDPIList(sensor); + for(std::size_t i = _dpi_lists.size()-1; i <= sensor; i++) { + _dpi_lists.push_back(_adjustable_dpi.getSensorDPIList(i)); + } + } + dpi_list = _dpi_lists[sensor]; + _adjustable_dpi.setSensorDPI(sensor, getClosestDPI(dpi_list, dpi)); } /* Some devices have multiple sensors, but an older config format diff --git a/src/logid/features/DPI.h b/src/logid/features/DPI.h index 78f102f..8bc2468 100644 --- a/src/logid/features/DPI.h +++ b/src/logid/features/DPI.h @@ -46,6 +46,7 @@ namespace features private: Config _config; backend::hidpp20::AdjustableDPI _adjustable_dpi; + std::vector _dpi_lists; }; }} From 214bbbecbcb7ab7a6e665e807797af6a08fe9b0e Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 12 Jul 2020 00:39:49 -0400 Subject: [PATCH 64/81] Remove unused files --- src/logid/Actions.cpp | 256 ------------------------------------------ src/logid/Actions.h | 164 --------------------------- src/logid/util.cpp | 123 -------------------- src/logid/util.h | 34 ------ 4 files changed, 577 deletions(-) delete mode 100644 src/logid/Actions.cpp delete mode 100644 src/logid/Actions.h delete mode 100644 src/logid/util.cpp delete mode 100644 src/logid/util.h diff --git a/src/logid/Actions.cpp b/src/logid/Actions.cpp deleted file mode 100644 index db4e1ed..0000000 --- a/src/logid/Actions.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* - * 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 . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Actions.h" -#include "util.h" -#include "EvdevDevice.h" - -using namespace logid; - -Gesture::Gesture(ButtonAction* ba, GestureMode m, void* aux) : action (ba), mode (m) -{ - switch(m) - { - case GestureMode::OnFewPixels: - per_pixel = *(int*)aux; - break; - case GestureMode::Axis: - axis = *(axis_info*)aux; - break; - default: - break; - } -} - -NoAction* NoAction::copy(Device *dev) -{ - auto action = new NoAction(); - action->device = dev; - return action; -} -KeyAction* KeyAction::copy(Device *dev) -{ - auto action = new KeyAction(keys); - action->device = dev; - return action; -} -GestureAction* GestureAction::copy(Device* dev) -{ - auto action = new GestureAction({}); - action->device = dev; - for(auto it : gestures) - action->gestures.insert({it.first, new Gesture(*it.second, dev)}); - return action; -} -SmartshiftAction* SmartshiftAction::copy(Device* dev) -{ - auto action = new SmartshiftAction(); - action->device = dev; - return action; -} -HiresScrollAction* HiresScrollAction::copy(Device* dev) -{ - auto action = new HiresScrollAction(); - action->device = dev; - return action; -} -CycleDPIAction* CycleDPIAction::copy(Device* dev) -{ - auto action = new CycleDPIAction(dpis); - action->device = dev; - return action; -} -ChangeDPIAction* ChangeDPIAction::copy(Device* dev) -{ - auto action = new ChangeDPIAction(dpi_inc); - action->device = dev; - return action; -} - -void KeyAction::press() -{ - //KeyPress event for each in keys - for(unsigned int i : keys) - global_evdev->sendEvent(EV_KEY, i, 1); -} - -void KeyAction::release() -{ - //KeyRelease event for each in keys - for(unsigned int i : keys) - global_evdev->sendEvent(EV_KEY, i, 0); -} - -void GestureAction::press() -{ - for(auto g : gestures) - g.second->per_pixel_mod = 0; - - held = true; - x = 0; - y = 0; -} - -void GestureAction::move(HIDPP20::IReprogControlsV4::Move m) -{ - x += m.x; - y += m.y; - if(m.y != 0 && abs(y) > 50) - { - auto g = gestures.find(m.y > 0 ? Direction::Down : Direction::Up); - if(g != gestures.end()) - { - if (g->second->mode == GestureMode::Axis) - global_evdev->moveAxis(g->second->axis.code, abs(m.y) * g->second->axis.multiplier); - if (g->second->mode == GestureMode::OnFewPixels) - { - g->second->per_pixel_mod += abs(m.y); - if(g->second->per_pixel_mod >= g->second->per_pixel) - { - g->second->per_pixel_mod -= g->second->per_pixel; - g->second->action->press(); - g->second->action->release(); - } - } - } - } - if(m.x != 0 && abs(x) > 50) - { - auto g = gestures.find(m.x > 0 ? Direction::Right : Direction::Left); - if(g != gestures.end()) - { - if (g->second->mode == GestureMode::Axis) - global_evdev->moveAxis(g->second->axis.code, abs(m.x) * g->second->axis.multiplier); - if (g->second->mode == GestureMode::OnFewPixels) - { - g->second->per_pixel_mod += abs(m.x); - if (g->second->per_pixel_mod >= g->second->per_pixel) - { - g->second->per_pixel_mod -= g->second->per_pixel; - g->second->action->press(); - g->second->action->release(); - } - } - } - } -} - -void GestureAction::release() -{ - held = false; - Direction direction; - if(abs(x) < 50 && abs(y) < 50) direction = Direction::None; - else direction = getDirection(x, y); - x = 0; - y = 0; - - auto g = gestures.find(direction); - - if(g != gestures.end() && g->second->mode == GestureMode::OnRelease) - { - g->second->action->press(); - g->second->action->release(); - } -} - -void SmartshiftAction::press() -{ - try - { - HIDPP20::ISmartShift iss(device->hidpp_dev); - auto s = iss.getStatus(); - iss.setStatus({new bool(!*s.Active)}); - } - catch(HIDPP20::Error &e) - { - log_printf(ERROR, "Error toggling smart shift, code %d: %s\n", e.errorCode(), e.what()); - } -} - -void HiresScrollAction::press() -{ - try - { - HIDPP20::IHiresScroll ihs(device->hidpp_dev); - auto mode = ihs.getMode(); - mode ^= HIDPP20::IHiresScroll::Mode::HiRes; - ihs.setMode(mode); - } - catch(HIDPP20::Error &e) - { - log_printf(ERROR, "Error toggling hires scroll, code %d: %s\n", e.errorCode(), e.what()); - return; - } -} - -void CycleDPIAction::press() -{ - HIDPP20::IAdjustableDPI iad(device->hidpp_dev); - - try - { - for (uint i = 0; i < iad.getSensorCount(); i++) - { - int current_dpi = std::get<0>(iad.getSensorDPI(i)); - bool found = false; - for (uint j = 0; j < dpis.size(); j++) - { - if (dpis[j] == current_dpi) - { - if (j == dpis.size() - 1) - iad.setSensorDPI(i, dpis[0]); - else - iad.setSensorDPI(i, dpis[j + 1]); - found = true; - break; - } - } - if (found) break; - if (dpis.empty()) iad.setSensorDPI(i, std::get<1>(iad.getSensorDPI(i))); - else iad.setSensorDPI(i, dpis[0]); - } - } - catch(HIDPP20::Error &e) { log_printf(ERROR, "Error while cycling DPI: %s", e.what()); } -} - -void ChangeDPIAction::press() -{ - HIDPP20::IAdjustableDPI iad(device->hidpp_dev); - - try - { - for(uint i = 0; i < iad.getSensorCount(); i++) - { - int current_dpi = std::get<0>(iad.getSensorDPI(i)); - iad.setSensorDPI(i, current_dpi + dpi_inc); - } - } - catch(HIDPP20::Error &e) - { - log_printf(ERROR, "Error while incrementing DPI: %s", e.what()); - } -} \ No newline at end of file diff --git a/src/logid/Actions.h b/src/logid/Actions.h deleted file mode 100644 index 93fdd44..0000000 --- a/src/logid/Actions.h +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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 . - * - */ - -#ifndef LOGID_ACTIONS_H -#define LOGID_ACTIONS_H -#include "Device.h" - -#include -#include -#include - -namespace logid -{ - enum class Action - { - None, - Keypress, - Gestures, - CycleDPI, - ChangeDPI, - ToggleSmartshift, - ToggleHiresScroll - }; - enum class Direction - { - None, - Up, - Down, - Left, - Right - }; - enum class GestureMode - { - NoPress, - OnRelease, - OnFewPixels, - Axis - }; - - class Device; - - class ButtonAction - { - public: - virtual ~ButtonAction() = default; - - Action type; - virtual ButtonAction* copy(Device* dev) = 0; - virtual void press() = 0; - virtual void release() = 0; - // ButtonAction(const ButtonAction &a, Device* d) : type (a.type), device (d) {} - // ButtonAction* create_instance(Device* d); - protected: - ButtonAction(Action a) : type (a) {}; - Device* device; - }; - class NoAction : public ButtonAction - { - public: - NoAction() : ButtonAction(Action::None) {} - virtual NoAction* copy(Device* dev); - virtual void press() {} - virtual void release() {} - }; - class KeyAction : public ButtonAction - { - public: - explicit KeyAction(std::vector k) : ButtonAction(Action::Keypress), keys (std::move(k)) {}; - virtual KeyAction* copy(Device* dev); - virtual void press(); - virtual void release(); - private: - std::vector keys; - }; - class Gesture - { - public: - struct axis_info { - uint code; - float multiplier; - }; - - Gesture(ButtonAction* ba, GestureMode m, void* aux=nullptr); - Gesture(const Gesture &g, Device* dev) - : action (g.action->copy(dev)), mode (g.mode), per_pixel (g.per_pixel), - axis (g.axis) - { - } - - ButtonAction* action; - GestureMode mode; - int per_pixel; - int per_pixel_mod; - axis_info axis; - }; - - class GestureAction : public ButtonAction - { - public: - GestureAction(std::map g) : ButtonAction(Action::Gestures), gestures (std::move(g)) {}; - std::map gestures; - virtual GestureAction* copy(Device* dev); - virtual void press(); - virtual void release(); - void move(HIDPP20::IReprogControlsV4::Move m); - private: - bool held; - int x = 0; - int y = 0; - }; - class SmartshiftAction : public ButtonAction - { - public: - SmartshiftAction() : ButtonAction(Action::ToggleSmartshift) {}; - virtual SmartshiftAction* copy(Device* dev); - virtual void press(); - virtual void release() {} - }; - class HiresScrollAction : public ButtonAction - { - public: - HiresScrollAction() : ButtonAction(Action::ToggleHiresScroll) {}; - virtual HiresScrollAction* copy(Device* dev); - virtual void press(); - virtual void release() {} - }; - class CycleDPIAction : public ButtonAction - { - public: - CycleDPIAction(std::vector d) : ButtonAction(Action::CycleDPI), dpis (d) {}; - virtual CycleDPIAction* copy(Device* dev); - virtual void press(); - virtual void release() {} - private: - const std::vector dpis; - }; - class ChangeDPIAction : public ButtonAction - { - public: - ChangeDPIAction(int i) : ButtonAction(Action::ChangeDPI), dpi_inc (i) {}; - virtual ChangeDPIAction* copy(Device* dev); - virtual void press(); - virtual void release() {} - private: - int dpi_inc; - }; -} - -#endif //LOGID_ACTIONS_H diff --git a/src/logid/util.cpp b/src/logid/util.cpp deleted file mode 100644 index 03383d4..0000000 --- a/src/logid/util.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 . - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "util.h" - -using namespace logid; - -/* -Direction logid::getDirection(int x, int y) -{ - if(x == 0 && y == 0) return Direction::None; - - double angle; - - if(x == 0 && y > 0) angle = 90; // Y+ - else if(x == 0 && y < 0) angle = 270; // Y- - else if(x > 0 && y == 0) angle = 0; // X+ - else if(x < 0 && y == 0) angle = 180; // X+ - else - { - angle = fabs(atan((double)y/(double)x) * 180.0 / M_PI); - - if(x < 0 && y > 0) angle = 180.0 - angle; //Q2 - else if(x < 0 && y < 0) angle += 180; // Q3 - else if(x > 0 && y < 0) angle = 360.0 - angle; // Q4 - } - - if(315 < angle || angle <= 45) return Direction::Right; - else if(45 < angle && angle <= 135) return Direction::Down; - else if(135 < angle && angle <= 225) return Direction::Left; - else if(225 < angle && angle <= 315) return Direction::Up; - - return Direction::None; -} - -Direction logid::stringToDirection(std::string s) -{ - const char* original_str = s.c_str(); - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - - if(s == "none") return Direction::None; - if(s == "up") return Direction::Up; - if(s == "down") return Direction::Down; - if(s == "left") return Direction::Left; - if(s == "right") return Direction::Right; - - s = original_str; - - throw std::invalid_argument(s + " is an invalid direction."); -} - -GestureMode logid::stringToGestureMode(std::string s) -{ - const char* original_str = s.c_str(); - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - - if(s == "nopress") return GestureMode::NoPress; - if(s == "onrelease") return GestureMode::OnRelease; - if(s == "onfewpixels") return GestureMode::OnFewPixels; - if(s == "axis") return GestureMode::Axis; - - s = original_str; - - log_printf(INFO, "%s is an invalid gesture mode. Defaulting to OnRelease", original_str); - - - return GestureMode::OnRelease; -} - -Action logid::stringToAction(std::string s) -{ - std::string original_str = s; - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - - if(s == "none") return Action::None; - if(s == "keypress") return Action::Keypress; - if(s == "gestures") return Action::Gestures; - if(s == "togglesmartshift") return Action::ToggleSmartshift; - if(s == "togglehiresscroll") return Action::ToggleHiresScroll; - if(s == "cycledpi") return Action::CycleDPI; - if(s == "changedpi") return Action::ChangeDPI; - - throw std::invalid_argument(original_str + " is an invalid action."); -} - - */ - -LogLevel logid::stringToLogLevel(std::string s) -{ - std::string original_str = s; - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - - if(s == "rawreport") return RAWREPORT; - if(s == "debug") return DEBUG; - if(s == "info") return INFO; - if(s == "warn" || s == "warning") return WARN; - if(s == "error") return ERROR; - - throw std::invalid_argument(original_str + " is an invalid log level."); -} \ No newline at end of file diff --git a/src/logid/util.h b/src/logid/util.h deleted file mode 100644 index 24ed02b..0000000 --- a/src/logid/util.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 . - * - */ - -#ifndef LOGID_UTIL_H -#define LOGID_UTIL_H - -#include - -namespace logid -{ - /* - Direction getDirection(int x, int y); - Direction stringToDirection(std::string s); - GestureMode stringToGestureMode(std::string s); - Action stringToAction(std::string s); - */ -} - -#endif //LOGID_UTIL_H \ No newline at end of file From 9ad8ce0feaeb366771b43370e69fbed25323c3c7 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 12 Jul 2020 00:40:10 -0400 Subject: [PATCH 65/81] Remove README.md warning --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 5df1959..da742d1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # logiops -#### This branch is a WIP and currently does not function. - This is an unofficial driver for Logitech mice and keyboard. This is currently only compatible with HID++ \>2.0 devices. From f7638b09059efe303335f094169fd8e9a9e05b6f Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 12 Jul 2020 02:51:56 -0400 Subject: [PATCH 66/81] Properly output TimeoutError Fixed issue where receiver devices aren't detected (hopefully). --- src/logid/Configuration.h | 2 +- src/logid/DeviceManager.cpp | 4 +++ src/logid/backend/raw/DeviceMonitor.cpp | 2 +- src/logid/backend/raw/RawDevice.cpp | 41 ++++++++++++++++++++++--- src/logid/backend/raw/RawDevice.h | 1 + src/logid/logid.cpp | 7 ++--- src/logid/util/task.cpp | 13 ++++++++ src/logid/util/task.h | 5 +-- src/logid/util/workqueue.cpp | 24 ++------------- src/logid/util/workqueue.h | 3 +- 10 files changed, 64 insertions(+), 38 deletions(-) diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index b05da63..3666fbc 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -25,7 +25,7 @@ #include #define LOGID_DEFAULT_RAWDEVICE_TIMEOUT std::chrono::seconds(2) -#define LOGID_DEFAULT_WORKER_COUNT 2 +#define LOGID_DEFAULT_WORKER_COUNT 4 namespace logid { diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index 95448b7..da05400 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -24,6 +24,7 @@ #include "util/log.h" #include "backend/hidpp10/Error.h" #include "backend/dj/Receiver.h" +#include "backend/Error.h" #define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded" @@ -46,6 +47,9 @@ void DeviceManager::addDevice(std::string path) logPrintf(WARN, "I/O error on %s: %s, skipping device.", path.c_str(), e.what()); return; + } catch (TimeoutError &e) { + logPrintf(WARN, "Device %s timed out.", path.c_str()); + defaultExists = false; } if(isReceiver) { diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp index 278f4ca..54dde67 100644 --- a/src/logid/backend/raw/DeviceMonitor.cpp +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -160,7 +160,7 @@ void DeviceMonitor::enumerate() task::spawn([this, name=devnode]() { this->addDevice(name); }, [name=devnode](std::exception& e){ - logPrintf(ERROR, "Error adding device %s: %s", + logPrintf(WARN, "Error adding device %s: %s", name.c_str(), e.what()); }); } diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 8019aa5..6859271 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -24,6 +24,8 @@ #include "../hidpp/Report.h" #include "../../Configuration.h" #include "../../util/thread.h" +#include "../../util/task.h" +#include "../../util/workqueue.h" #include #include @@ -63,7 +65,7 @@ bool RawDevice::supportedReport(uint8_t id, uint8_t length) } RawDevice::RawDevice(std::string path) : _path (std::move(path)), - _continue_listen (false) + _continue_listen (false), _continue_respond (false) { int ret; @@ -151,15 +153,41 @@ std::vector RawDevice::sendReport(const std::vector& report) /* If the listener will stop, handle I/O manually. * Otherwise, push to queue and wait for result. */ if(_continue_listen) { - std::packaged_task()> task( [this, report]() { + std::mutex send_report; + std::unique_lock lock(send_report); + std::condition_variable cv; + bool top_of_queue = false; + std::packaged_task()> task( [this, report, &cv, + &top_of_queue] () { + top_of_queue = true; + cv.notify_all(); return this->_respondToReport(report); }); auto f = task.get_future(); _io_queue.push(&task); + cv.wait(lock, [&top_of_queue]{ return top_of_queue; }); + auto status = f.wait_for(global_config->ioTimeout()); + if(status == std::future_status::timeout) { + _continue_respond = false; + throw TimeoutError(); + } return f.get(); } - else - return _respondToReport(report); + else { + std::vector response; + std::shared_ptr t = std::make_shared( + [this, report, &response]() { + response = _respondToReport(report); + }); + global_workqueue->queue(t); + t->waitStart(); + auto status = t->waitFor(global_config->ioTimeout()); + if(status == std::future_status::timeout) { + _continue_respond = false; + throw TimeoutError(); + } else + return response; + } } // DJ commands are not systematically acknowledged, do not expect a result. @@ -184,7 +212,8 @@ std::vector RawDevice::_respondToReport (const std::vector& request) { _sendReport(request); - while(true) { + _continue_respond = true; + while(_continue_respond) { std::vector response; _readReport(response, MAX_DATA_LENGTH); @@ -227,6 +256,8 @@ std::vector RawDevice::_respondToReport if(_continue_listen) this->_handleEvent(response); } + + return {}; } int RawDevice::_sendReport(const std::vector& report) diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index 4c7dec9..dca622e 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -75,6 +75,7 @@ namespace raw std::vector rdesc; std::atomic _continue_listen; + std::atomic _continue_respond; std::condition_variable _listen_condition; std::map> diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 0d3bc34..42c75e4 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -43,7 +43,7 @@ LogLevel logid::global_loglevel = INFO; std::shared_ptr logid::global_config; std::unique_ptr logid::device_manager; std::unique_ptr logid::virtual_input; -std::unique_ptr logid::global_workqueue; +std::shared_ptr logid::global_workqueue; bool logid::kill_logid = false; std::mutex logid::device_manager_reload; @@ -157,8 +157,6 @@ Possible options are: int main(int argc, char** argv) { - global_workqueue = std::make_unique(LOGID_DEFAULT_WORKER_COUNT); - readCliOptions(argc, argv); // Read config @@ -168,8 +166,7 @@ int main(int argc, char** argv) catch (std::exception &e) { global_config = std::make_shared(); } - - global_workqueue->setThreadCount(global_config->workerCount()); + global_workqueue = std::make_shared(global_config->workerCount()); //Create a virtual input device try { diff --git a/src/logid/util/task.cpp b/src/logid/util/task.cpp index 021e0fb..79432d5 100644 --- a/src/logid/util/task.cpp +++ b/src/logid/util/task.cpp @@ -38,6 +38,7 @@ task::task(const std::function& function, void task::run() { _status = Running; + _status_cv.notify_all(); _task_pkg(); _status = Completed; } @@ -52,6 +53,18 @@ void task::wait() _task_pkg.get_future().wait(); } +void task::waitStart() +{ + std::mutex wait_start; + std::unique_lock lock(wait_start); + _status_cv.wait(lock, [this](){ return _status != Waiting; }); +} + +std::future_status task::waitFor(std::chrono::milliseconds ms) +{ + return _task_pkg.get_future().wait_for(ms); +} + void task::spawn(const std::function& function, const std::function& exception_handler) { diff --git a/src/logid/util/task.h b/src/logid/util/task.h index 100a938..91157a6 100644 --- a/src/logid/util/task.h +++ b/src/logid/util/task.h @@ -44,6 +44,8 @@ namespace logid void run(); // Runs synchronously void wait(); + void waitStart(); + std::future_status waitFor(std::chrono::milliseconds ms); /* This function spawns a new task into the least used worker queue * and forgets about it. @@ -53,13 +55,12 @@ namespace logid exception_handler={[](std::exception& e) {ExceptionHandler::Default(e);}}); - static void autoQueue(std::shared_ptr); - private: std::shared_ptr> _function; std::shared_ptr> _exception_handler; std::atomic _status; + std::condition_variable _status_cv; std::packaged_task _task_pkg; }; } diff --git a/src/logid/util/workqueue.cpp b/src/logid/util/workqueue.cpp index 14c1f79..a7e04f7 100644 --- a/src/logid/util/workqueue.cpp +++ b/src/logid/util/workqueue.cpp @@ -15,6 +15,7 @@ * along with this program. If not, see . * */ +#include #include "workqueue.h" #include "log.h" @@ -48,6 +49,7 @@ workqueue::~workqueue() void workqueue::queue(std::shared_ptr t) { + assert(t != nullptr); _queue.push(t); _queue_cv.notify_all(); } @@ -63,27 +65,6 @@ void workqueue::stop() std::unique_lock lock(_run_lock); } -void workqueue::setThreadCount(std::size_t count) -{ - while(_workers.size() < count) - _workers.push_back(std::make_unique(this, - _workers.size())); - - if(_workers.size() > count) { - // Restart manager thread - stop(); - while (_workers.size() > count) - _workers.pop_back(); - _manager_thread = std::make_unique( - [this](){ _run(); } - , [this](std::exception& e){ _exception_handler(e); } - ); - _manager_thread->run(); - } - - _worker_count = count; -} - std::size_t workqueue::threadCount() const { return _workers.size(); @@ -98,7 +79,6 @@ void workqueue::_run() while(_continue_run) { _queue_cv.wait(lock, [this]{ return !(_queue.empty()); }); while(!_queue.empty()) { - if(_workers.empty()) { if(_worker_count) logPrintf(DEBUG, "No workers were found, running task in" diff --git a/src/logid/util/workqueue.h b/src/logid/util/workqueue.h index 2234cf3..301b60b 100644 --- a/src/logid/util/workqueue.h +++ b/src/logid/util/workqueue.h @@ -35,7 +35,6 @@ namespace logid void stop(); - void setThreadCount(std::size_t count); std::size_t threadCount() const; private: void _run(); @@ -53,7 +52,7 @@ namespace logid std::size_t _worker_count; }; - extern std::unique_ptr global_workqueue; + extern std::shared_ptr global_workqueue; } #endif //LOGID_WORKQUEUE_H From 1a056a1ecfecee82d1243fc6e56ad22caff3c782 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 12 Jul 2020 03:04:07 -0400 Subject: [PATCH 67/81] Add mutex for events on Receiver --- src/logid/Receiver.cpp | 2 ++ src/logid/Receiver.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index 1efa1ea..bdfa178 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -31,6 +31,7 @@ Receiver::Receiver(std::string path) : dj::ReceiverMonitor(path), _path (path) void Receiver::addDevice(hidpp::DeviceConnectionEvent event) { + std::unique_lock lock(_devices_change); try { auto dev = _devices.find(event.index); if(dev != _devices.end()) { @@ -71,5 +72,6 @@ void Receiver::addDevice(hidpp::DeviceConnectionEvent event) void Receiver::removeDevice(hidpp::DeviceIndex index) { + std::unique_lock lock(_devices_change); _devices.erase(index); } \ No newline at end of file diff --git a/src/logid/Receiver.h b/src/logid/Receiver.h index 029c80c..15c0f9b 100644 --- a/src/logid/Receiver.h +++ b/src/logid/Receiver.h @@ -34,6 +34,7 @@ namespace logid void addDevice(backend::hidpp::DeviceConnectionEvent event) override; void removeDevice(backend::hidpp::DeviceIndex index) override; private: + std::mutex _devices_change; std::map> _devices; std::string _path; }; From 4ce76f5927245f7ae10f1817bd0d316459898767 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 12 Jul 2020 04:42:44 -0400 Subject: [PATCH 68/81] Listen for events on receiver device on timeout Previously, if a receiver device didn't respond during the initial scan, logid would not recognize it until it sent a wakeup/connect event. This makes it so if the device times out, logid will listen for the next event from the device and try detecting it. (e.g. shaking the mouse will make it become detected) --- src/logid/Receiver.cpp | 6 +++++ src/logid/backend/dj/Receiver.cpp | 1 + src/logid/backend/dj/Receiver.h | 1 + src/logid/backend/dj/ReceiverMonitor.cpp | 31 ++++++++++++++++++++++++ src/logid/backend/dj/ReceiverMonitor.h | 2 ++ src/logid/backend/raw/RawDevice.cpp | 4 +++ src/logid/backend/raw/RawDevice.h | 1 + 7 files changed, 46 insertions(+) diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index bdfa178..a3f8adb 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -21,6 +21,7 @@ #include "util/log.h" #include "backend/hidpp10/Error.h" #include "backend/hidpp20/Error.h" +#include "backend/Error.h" using namespace logid; using namespace logid::backend; @@ -67,6 +68,11 @@ void Receiver::addDevice(hidpp::DeviceConnectionEvent event) } catch(hidpp20::Error &e) { logPrintf(ERROR, "Caught HID++ 2.0 error while trying to initialize " "%s:%d: %s", _path.c_str(), event.index, e.what()); + } catch(TimeoutError &e) { + if(!event.fromTimeoutCheck) + logPrintf(DEBUG, "%s:%d timed out, waiting for input from device to" + " initialize.", _path.c_str(), event.index); + waitForDevice(event.index); } } diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp index 64118f5..a79c5bf 100644 --- a/src/logid/backend/dj/Receiver.cpp +++ b/src/logid/backend/dj/Receiver.cpp @@ -238,6 +238,7 @@ hidpp::DeviceConnectionEvent Receiver::deviceConnectionEvent(const event.encrypted = report.paramBegin()[0] & (1<<5); event.linkEstablished = !(report.paramBegin()[0] & (1<<6)); event.withPayload = report.paramBegin()[0] & (1<<7); + event.fromTimeoutCheck = false; event.pid =(report.paramBegin()[2] << 8); event.pid |= report.paramBegin()[1]; diff --git a/src/logid/backend/dj/Receiver.h b/src/logid/backend/dj/Receiver.h index 86430b2..12d1e1b 100644 --- a/src/logid/backend/dj/Receiver.h +++ b/src/logid/backend/dj/Receiver.h @@ -189,6 +189,7 @@ namespace hidpp bool encrypted; bool linkEstablished; bool withPayload; + bool fromTimeoutCheck = false; // Fake field }; }}} diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp index d483475..d3f9fb2 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/dj/ReceiverMonitor.cpp @@ -95,6 +95,37 @@ void ReceiverMonitor::enumerate() _receiver->enumerateHidpp(); } +void ReceiverMonitor::waitForDevice(hidpp::DeviceIndex index) +{ + std::string nickname = "WAIT_DEV_" + std::to_string(index); + auto handler = std::make_shared(); + handler->condition = [index](std::vector& report)->bool { + return report[Offset::DeviceIndex] == index; + }; + + handler->callback = [this, index, nickname](std::vector& report) { + (void)report; // Suppress unused warning + + hidpp::DeviceConnectionEvent event{}; + event.withPayload = false; + event.linkEstablished = true; + event.index = index; + event.fromTimeoutCheck = true; + + task::spawn({[this, event, nickname]() { + _receiver->rawDevice()->removeEventHandler(nickname); + this->addDevice(event); + }}, {[path=_receiver->rawDevice()->hidrawPath(), event] + (std::exception& e) { + logPrintf(ERROR, "Failed to add device %d to receiver " + "on %s: %s", event.index, + path.c_str(), e.what()); + }}); + }; + + _receiver->rawDevice()->addEventHandler(nickname, handler); +} + std::shared_ptr ReceiverMonitor::receiver() const { return _receiver; diff --git a/src/logid/backend/dj/ReceiverMonitor.h b/src/logid/backend/dj/ReceiverMonitor.h index 73d7938..d5deb98 100644 --- a/src/logid/backend/dj/ReceiverMonitor.h +++ b/src/logid/backend/dj/ReceiverMonitor.h @@ -42,6 +42,8 @@ namespace dj virtual void addDevice(hidpp::DeviceConnectionEvent event) = 0; virtual void removeDevice(hidpp::DeviceIndex index) = 0; + void waitForDevice(hidpp::DeviceIndex index); + // Internal methods for derived class void _pair(uint8_t timeout = 0); void _stopPairing(); diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 6859271..882396c 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -404,6 +404,7 @@ void RawDevice::stopListener() void RawDevice::addEventHandler(const std::string& nickname, const std::shared_ptr& handler) { + std::unique_lock lock(_event_handler_lock); auto it = _event_handlers.find(nickname); assert(it == _event_handlers.end()); assert(handler); @@ -412,17 +413,20 @@ void RawDevice::addEventHandler(const std::string& nickname, void RawDevice::removeEventHandler(const std::string &nickname) { + std::unique_lock lock(_event_handler_lock); _event_handlers.erase(nickname); } const std::map>& RawDevice::eventHandlers() { + std::unique_lock lock(_event_handler_lock); return _event_handlers; } void RawDevice::_handleEvent(std::vector &report) { + std::unique_lock lock(_event_handler_lock); for(auto& handler : _event_handlers) if(handler.second->condition(report)) handler.second->callback(report); diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index dca622e..d675e34 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -80,6 +80,7 @@ namespace raw std::map> _event_handlers; + std::mutex _event_handler_lock; void _handleEvent(std::vector& report); /* These will only be used internally and processed with a queue */ From 8f073d66c322860d97b0375813d579f8867c8626 Mon Sep 17 00:00:00 2001 From: pixl Date: Sun, 12 Jul 2020 16:18:43 -0400 Subject: [PATCH 69/81] Remove unused dependencies --- src/logid/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/logid/CMakeLists.txt b/src/logid/CMakeLists.txt index de51a75..610c109 100644 --- a/src/logid/CMakeLists.txt +++ b/src/logid/CMakeLists.txt @@ -75,12 +75,10 @@ find_path(EVDEV_INCLUDE_DIR libevdev/libevdev.h find_library(EVDEV_LIBRARY NAMES evdev libevdev) -include_directories(${EVDEV_INCLUDE_DIR} ${DBUSCXX_INCLUDE_DIR} ${LIBUDEV_INCLUDE_DIRECTORIES}) +include_directories(${EVDEV_INCLUDE_DIR} ${LIBUDEV_INCLUDE_DIRECTORIES}) target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ - ${DBUSCXX_LIBRARIES} ${LIBUDEV_LIBRARIES}) - -target_link_libraries(logid ${CMAKE_THREAD_LIBS_INIT} ${EVDEV_LIBRARY} config++ ${HIDPP_LIBRARY}) + ${LIBUDEV_LIBRARIES}) install(TARGETS logid DESTINATION bin) From dde299322380aea0bbcb1e8821784fe5a15808c5 Mon Sep 17 00:00:00 2001 From: pixl Date: Mon, 13 Jul 2020 01:14:15 -0400 Subject: [PATCH 70/81] Fix crashes when logid starts as root If logid scans every device, it will either SIGSEGV or not work at all. This commit should fix bug #100. --- src/logid/backend/raw/DeviceMonitor.cpp | 22 ++++- src/logid/backend/raw/RawDevice.cpp | 113 +++++++++++++++++------- src/logid/backend/raw/RawDevice.h | 11 ++- src/logid/util/task.cpp | 13 ++- src/logid/util/task.h | 1 + 5 files changed, 119 insertions(+), 41 deletions(-) diff --git a/src/logid/backend/raw/DeviceMonitor.cpp b/src/logid/backend/raw/DeviceMonitor.cpp index 54dde67..34d0cb8 100644 --- a/src/logid/backend/raw/DeviceMonitor.cpp +++ b/src/logid/backend/raw/DeviceMonitor.cpp @@ -19,6 +19,8 @@ #include "DeviceMonitor.h" #include "../../util/task.h" #include "../../util/log.h" +#include "RawDevice.h" +#include "../hidpp/Device.h" #include #include @@ -99,10 +101,16 @@ void DeviceMonitor::run() if (action == "add") task::spawn([this, name=devnode]() { - this->addDevice(name); + auto supported_reports = backend::hidpp::getSupportedReports( + RawDevice::getReportDescriptor(name)); + if(supported_reports) + this->addDevice(name); + else + logPrintf(DEBUG, "Unsupported device %s ignored", + name.c_str()); }, [name=devnode](std::exception& e){ logPrintf(WARN, "Error adding device %s: %s", - name.c_str(), e.what()); + name.c_str(), e.what()); }); else if (action == "remove") task::spawn([this, name=devnode]() { @@ -158,10 +166,16 @@ void DeviceMonitor::enumerate() udev_device_unref(device); task::spawn([this, name=devnode]() { - this->addDevice(name); + auto supported_reports = backend::hidpp::getSupportedReports( + RawDevice::getReportDescriptor(name)); + if(supported_reports) + this->addDevice(name); + else + logPrintf(DEBUG, "Unsupported device %s ignored", + name.c_str()); }, [name=devnode](std::exception& e){ logPrintf(WARN, "Error adding device %s: %s", - name.c_str(), e.what()); + name.c_str(), e.what()); }); } diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index 882396c..f5a5ac2 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -94,20 +94,7 @@ RawDevice::RawDevice(std::string path) : _path (std::move(path)), } _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"); - } - if (-1 == ::ioctl(_fd, HIDIOCGRDESC, &_rdesc)) { - int err = errno; - ::close(_fd); - throw std::system_error(err, std::system_category(), - "RawDevice HIDIOCGRDESC failed"); - } - rdesc = std::vector(_rdesc.value, _rdesc.value + _rdesc.size); + _rdesc = getReportDescriptor(_fd); if (-1 == ::pipe(_pipe)) { int err = errno; @@ -148,6 +135,41 @@ uint16_t RawDevice::productId() const return _pid; } +std::vector RawDevice::getReportDescriptor(std::string path) +{ + int fd = ::open(path.c_str(), O_RDWR); + if (fd == -1) + throw std::system_error(errno, std::system_category(), + "open failed"); + + auto rdesc = getReportDescriptor(fd); + ::close(fd); + return rdesc; +} + +std::vector RawDevice::getReportDescriptor(int fd) +{ + 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"); + } + if (-1 == ::ioctl(fd, HIDIOCGRDESC, &rdesc)) { + int err = errno; + ::close(fd); + throw std::system_error(err, std::system_category(), + "RawDevice HIDIOCGRDESC failed"); + } + return std::vector(rdesc.value, rdesc.value + rdesc.size); +} + +std::vector RawDevice::reportDescriptor() const +{ + return _rdesc; +} + std::vector RawDevice::sendReport(const std::vector& report) { /* If the listener will stop, handle I/O manually. @@ -157,33 +179,47 @@ std::vector RawDevice::sendReport(const std::vector& report) std::unique_lock lock(send_report); std::condition_variable cv; bool top_of_queue = false; - std::packaged_task()> task( [this, report, &cv, - &top_of_queue] () { + auto task = std::make_shared()>> + ( [this, report, &cv, &top_of_queue] () { top_of_queue = true; cv.notify_all(); return this->_respondToReport(report); }); - auto f = task.get_future(); - _io_queue.push(&task); + auto f = task->get_future(); + _io_queue.push(task); cv.wait(lock, [&top_of_queue]{ return top_of_queue; }); auto status = f.wait_for(global_config->ioTimeout()); if(status == std::future_status::timeout) { _continue_respond = false; - throw TimeoutError(); + interruptRead(); + return f.get(); // Expecting an error, but it could work } return f.get(); } else { std::vector response; + std::exception_ptr _exception; std::shared_ptr t = std::make_shared( [this, report, &response]() { response = _respondToReport(report); - }); + }, [&_exception](std::exception& e) { + try { + throw e; + } catch(std::exception& e) { + _exception = std::make_exception_ptr(e); + } + }); global_workqueue->queue(t); t->waitStart(); auto status = t->waitFor(global_config->ioTimeout()); + if(_exception) + std::rethrow_exception(_exception); if(status == std::future_status::timeout) { _continue_respond = false; + interruptRead(); + t->wait(); + if(_exception) + std::rethrow_exception(_exception); throw TimeoutError(); } else return response; @@ -196,12 +232,13 @@ void RawDevice::sendReportNoResponse(const std::vector& report) /* If the listener will stop, handle I/O manually. * Otherwise, push to queue and wait for result. */ if(_continue_listen) { - std::packaged_task()> task([this, report]() { + auto task = std::make_shared()>> + ([this, report]() { this->_sendReport(report); return std::vector(); }); - auto f = task.get_future(); - _io_queue.push(&task); + auto f = task->get_future(); + _io_queue.push(task); f.get(); } else @@ -213,9 +250,17 @@ std::vector RawDevice::_respondToReport { _sendReport(request); _continue_respond = true; + + auto start_point = std::chrono::steady_clock::now(); + while(_continue_respond) { std::vector response; - _readReport(response, MAX_DATA_LENGTH); + auto current_point = std::chrono::steady_clock::now(); + auto timeout = global_config->ioTimeout() - std::chrono::duration_cast + (current_point - start_point); + if(timeout.count() <= 0) + throw TimeoutError(); + _readReport(response, MAX_DATA_LENGTH, timeout); // All reports have the device index at byte 2 if(response[1] != request[1]) { @@ -285,21 +330,27 @@ int RawDevice::_sendReport(const std::vector& report) return ret; } -int RawDevice::_readReport(std::vector& report, std::size_t maxDataLength) +int RawDevice::_readReport(std::vector &report, + std::size_t maxDataLength) +{ + return _readReport(report, maxDataLength, global_config->ioTimeout()); +} + +int RawDevice::_readReport(std::vector &report, + std::size_t maxDataLength, std::chrono::milliseconds timeout) { std::lock_guard lock(_dev_io); int ret; report.resize(maxDataLength); - timeval timeout{}; - timeout.tv_sec = duration_cast(global_config->ioTimeout()) + timeval timeout_tv{}; + timeout_tv.tv_sec = duration_cast(global_config->ioTimeout()) .count(); - timeout.tv_usec = duration_cast( + timeout_tv.tv_usec = duration_cast( global_config->ioTimeout()).count() % duration_cast(seconds(1)).count(); - auto timeout_ms = duration_cast( - global_config->ioTimeout()).count(); + auto timeout_ms = duration_cast(timeout).count(); fd_set fds; do { @@ -309,7 +360,7 @@ int RawDevice::_readReport(std::vector& report, std::size_t maxDataLeng ret = select(std::max(_fd, _pipe[0]) + 1, &fds, nullptr, nullptr, - (timeout_ms > 0 ? nullptr : &timeout)); + (timeout_ms > 0 ? nullptr : &timeout_tv)); } while(ret == -1 && errno == EINTR); if(ret == -1) diff --git a/src/logid/backend/raw/RawDevice.h b/src/logid/backend/raw/RawDevice.h index d675e34..e625944 100644 --- a/src/logid/backend/raw/RawDevice.h +++ b/src/logid/backend/raw/RawDevice.h @@ -47,7 +47,9 @@ namespace raw uint16_t vendorId() const; uint16_t productId() const; - std::vector reportDescriptor() const { return rdesc; } + static std::vector getReportDescriptor(std::string path); + static std::vector getReportDescriptor(int fd); + std::vector reportDescriptor() const; std::vector sendReport(const std::vector& report); void sendReportNoResponse(const std::vector& report); @@ -72,7 +74,7 @@ namespace raw uint16_t _vid; uint16_t _pid; std::string _name; - std::vector rdesc; + std::vector _rdesc; std::atomic _continue_listen; std::atomic _continue_respond; @@ -86,11 +88,14 @@ namespace raw /* These will only be used internally and processed with a queue */ int _sendReport(const std::vector& report); int _readReport(std::vector& report, std::size_t maxDataLength); + int _readReport(std::vector& report, std::size_t maxDataLength, + std::chrono::milliseconds timeout); std::vector _respondToReport(const std::vector& request); - mutex_queue()>*> _io_queue; + mutex_queue()>>> + _io_queue; }; }}} diff --git a/src/logid/util/task.cpp b/src/logid/util/task.cpp index 79432d5..29a4987 100644 --- a/src/logid/util/task.cpp +++ b/src/logid/util/task.cpp @@ -31,7 +31,7 @@ task::task(const std::function& function, } catch(std::exception& e) { (*_exception_handler)(e); } - }) + }), _future (_task_pkg.get_future()) { } @@ -41,6 +41,7 @@ void task::run() _status_cv.notify_all(); _task_pkg(); _status = Completed; + _status_cv.notify_all(); } task::Status task::getStatus() @@ -50,7 +51,13 @@ task::Status task::getStatus() void task::wait() { - _task_pkg.get_future().wait(); + if(_future.valid()) + _future.wait(); + else { + std::mutex wait_start; + std::unique_lock lock(wait_start); + _status_cv.wait(lock, [this](){ return _status == Completed; }); + } } void task::waitStart() @@ -62,7 +69,7 @@ void task::waitStart() std::future_status task::waitFor(std::chrono::milliseconds ms) { - return _task_pkg.get_future().wait_for(ms); + return _future.wait_for(ms); } void task::spawn(const std::function& function, diff --git a/src/logid/util/task.h b/src/logid/util/task.h index 91157a6..c988cc9 100644 --- a/src/logid/util/task.h +++ b/src/logid/util/task.h @@ -62,6 +62,7 @@ namespace logid std::atomic _status; std::condition_variable _status_cv; std::packaged_task _task_pkg; + std::future _future; }; } From 0f60c3896c03589eeffb2ca7aedbf0854e8366c9 Mon Sep 17 00:00:00 2001 From: pixl Date: Mon, 13 Jul 2020 15:12:28 -0400 Subject: [PATCH 71/81] Fix usbhid-dump SIGSEGV on #100 --- src/logid/backend/hidpp/Report.cpp | 5 +++-- src/logid/backend/raw/RawDevice.cpp | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/logid/backend/hidpp/Report.cpp b/src/logid/backend/hidpp/Report.cpp index 8cc5c9b..c5683a7 100644 --- a/src/logid/backend/hidpp/Report.cpp +++ b/src/logid/backend/hidpp/Report.cpp @@ -161,9 +161,10 @@ Report::Report(Report::Type type, DeviceIndex device_index, (sw_id & 0x0f); } -Report::Report(const std::vector& data) +Report::Report(const std::vector& data) : + _data (data) { - _data = data; + _data.resize(HeaderLength + LongParamLength); // Truncating data is entirely valid here. switch(_data[Offset::Type]) { diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index f5a5ac2..e4f919a 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -262,6 +262,9 @@ std::vector RawDevice::_respondToReport throw TimeoutError(); _readReport(response, MAX_DATA_LENGTH, timeout); + if(!_continue_respond) + throw TimeoutError(); + // All reports have the device index at byte 2 if(response[1] != request[1]) { if(_continue_listen) From a2718d9023691565b3d92142e1b77478ceb86062 Mon Sep 17 00:00:00 2001 From: pixl Date: Mon, 13 Jul 2020 15:29:40 -0400 Subject: [PATCH 72/81] Fix undetected CIDs --- .../hidpp20/features/ReprogControls.cpp | 27 ++++++++++--------- .../backend/hidpp20/features/ReprogControls.h | 2 ++ src/logid/features/RemapButton.cpp | 1 + 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/logid/backend/hidpp20/features/ReprogControls.cpp b/src/logid/backend/hidpp20/features/ReprogControls.cpp index 82af1d4..34334c4 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.cpp +++ b/src/logid/backend/hidpp20/features/ReprogControls.cpp @@ -79,20 +79,23 @@ ReprogControls::ControlInfo ReprogControls::getControlInfo(uint8_t index) return info; } +void ReprogControls::initCidMap() +{ + std::unique_lock lock(_cids_populating); + if(_cids_initialized) + return; + uint8_t controls = getControlCount(); + for(uint8_t i = 0; i < controls; i++) { + auto info = getControlInfo(i); + _cids.emplace(info.controlID, info); + } + _cids_initialized = true; +} + ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid) { - if(!_cids_initialized) { - std::unique_lock lock(_cids_populating); - if(!_cids_initialized) { - uint8_t controls = getControlCount(); - for(uint8_t i = 0; i < controls; i++) { - auto info = getControlInfo(i); - _cids.emplace(info.controlID, info); - } - _cids_populating.unlock(); - _cids_initialized = true; - } - } + if(!_cids_initialized) + initCidMap(); auto it = _cids.find(cid); if(it == _cids.end()) diff --git a/src/logid/backend/hidpp20/features/ReprogControls.h b/src/logid/backend/hidpp20/features/ReprogControls.h index 07567f8..a3d342c 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.h +++ b/src/logid/backend/hidpp20/features/ReprogControls.h @@ -95,6 +95,8 @@ namespace hidpp20 virtual ControlInfo getControlIdInfo(uint16_t cid); + virtual void initCidMap(); + // Onlu controlId and flags will be set virtual ControlInfo getControlReporting(uint16_t cid); diff --git a/src/logid/features/RemapButton.cpp b/src/logid/features/RemapButton.cpp index 32fdd6e..13204b0 100644 --- a/src/logid/features/RemapButton.cpp +++ b/src/logid/features/RemapButton.cpp @@ -32,6 +32,7 @@ using namespace logid::actions; RemapButton::RemapButton(Device *dev): DeviceFeature(dev), _config (dev), _reprog_controls (hidpp20::ReprogControls::autoVersion(&dev->hidpp20())) { + _reprog_controls->initCidMap(); } RemapButton::~RemapButton() From 3659ec25fb7f39f518ca4f57d9df098f8937755b Mon Sep 17 00:00:00 2001 From: pixl Date: Mon, 13 Jul 2020 22:04:50 -0400 Subject: [PATCH 73/81] Add additional DJ report descriptor Should fix invalid DJ reports in #100. --- src/logid/backend/dj/Report.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/logid/backend/dj/Report.cpp b/src/logid/backend/dj/Report.cpp index 49d2900..dff4040 100644 --- a/src/logid/backend/dj/Report.cpp +++ b/src/logid/backend/dj/Report.cpp @@ -44,10 +44,35 @@ static const std::array DJReportDesc = { 0xC0 // End Collection }; +static const std::array DJReportDesc2 = { + 0xA1, 0x01, // Collection (Application) + 0x85, 0x20, // Report ID (32) + 0x75, 0x08, // Report Size (8) + 0x95, 0x0E, // Report Count (14) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x41, // Usage (0x41) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x41, // Usage (0x41) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0x85, 0x21, // Report ID (33) + 0x95, 0x1F, // Report Count (31) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x42, // Usage (0x42) + 0x81, 0x00, // Input (Data, Array, Absolute) + 0x09, 0x42, // Usage (0x42) + 0x91, 0x00, // Output (Data, Array, Absolute) + 0xC0 // End Collection +}; + bool dj::supportsDjReports(std::vector&& rdesc) { auto it = std::search(rdesc.begin(), rdesc.end(), DJReportDesc.begin(), DJReportDesc.end()); + if(it == rdesc.end()) + it = std::search(rdesc.begin(), rdesc.end(), + DJReportDesc2.begin(), DJReportDesc2.end()); return it != rdesc.end(); } From 944a6419b4feb3ccc210819bbfe54f2a7c6aaa93 Mon Sep 17 00:00:00 2001 From: pixl Date: Mon, 13 Jul 2020 22:06:15 -0400 Subject: [PATCH 74/81] Remove unused DeviceFinder.cpp How'd that get in here anyways? --- src/logid/DeviceFinder.cpp | 212 ------------------------------------- 1 file changed, 212 deletions(-) delete mode 100644 src/logid/DeviceFinder.cpp diff --git a/src/logid/DeviceFinder.cpp b/src/logid/DeviceFinder.cpp deleted file mode 100644 index 53bd2a0..0000000 --- a/src/logid/DeviceFinder.cpp +++ /dev/null @@ -1,212 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "DeviceFinder.h" -#include "util.h" -#include "Device.h" - -#define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded" - -using namespace logid; - -void stopAndDeleteConnectedDevice (ConnectedDevice &connected_device) -{ - 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); -} - -DeviceFinder::~DeviceFinder() -{ - this->devices_mutex.lock(); - for (auto it = this->devices.begin(); it != this->devices.end(); it++) { - for (auto jt = it->second.begin(); jt != it->second.end(); jt++) { - stopAndDeleteConnectedDevice(jt->second); - } - } - this->devices_mutex.unlock(); -} - -///TODO: Unused return variable? -Device* DeviceFinder::insertNewDevice(const std::string &path, HIDPP::DeviceIndex index) -{ - auto device = new Device(path, index); - try - { - device->init(); - } - catch(BlacklistedDevice& e) { return nullptr; } - - this->devices_mutex.lock(); - log_printf(INFO, "%s detected: device %d on %s", device->name.c_str(), index, path.c_str()); - auto path_bucket = this->devices.emplace(path, std::map()).first; - path_bucket->second.emplace(index, ConnectedDevice{ - device, - std::thread([device]() { - device->start(); - }) - }); - 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->waitForReceiver(); - }) - }); - this->devices_mutex.unlock(); - - return device; -} - -void DeviceFinder::stopAndDeleteAllDevicesIn (const std::string &path) -{ - this->devices_mutex.lock(); - auto path_bucket = this->devices.find(path); - if (path_bucket != this->devices.end()) - { - for (auto& index_bucket : path_bucket->second) { - stopAndDeleteConnectedDevice(index_bucket.second); - } - this->devices.erase(path_bucket); - } - this->devices_mutex.unlock(); -} - -void DeviceFinder::stopAndDeleteDevice (const std::string &path, HIDPP::DeviceIndex index) -{ - this->devices_mutex.lock(); - auto path_bucket = this->devices.find(path); - if (path_bucket != this->devices.end()) - { - auto index_bucket = path_bucket->second.find(index); - if (index_bucket != path_bucket->second.end()) - { - stopAndDeleteConnectedDevice(index_bucket->second); - path_bucket->second.erase(index_bucket); - } - } - this->devices_mutex.unlock(); - - log_printf(WARN, "Attempted to disconnect not previously connected device %d on %s", index, path.c_str()); -} - -void DeviceFinder::addDevice(const char *path) -{ - using namespace std::chrono_literals; - - std::string string_path(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()); - for(HIDPP::DeviceIndex index: { HIDPP::DefaultDevice, HIDPP::CordedDevice }) - { - bool device_not_connected = true; - bool device_unknown = false; - int remaining_tries = MAX_CONNECTION_TRIES; - do { - try - { - HIDPP::Device d(&dispatcher, index); - auto version = d.protocolVersion(); - uint major, minor; - std::tie(major, minor) = version; - if(index == HIDPP::DefaultDevice && version == std::make_tuple(1, 0)) - { - 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); - } - device_not_connected = false; - } - catch(HIDPP10::Error &e) - { - if (e.errorCode() == HIDPP10::Error::ResourceError) - { - if(remaining_tries == 1) - { - log_printf(DEBUG, "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, %s device: %s", string_path.c_str(), NON_WIRELESS_DEV(index), e.what()); - } - else device_unknown = true; - } - catch(HIDPP20::Error &e) - { - if(e.errorCode() != HIDPP20::Error::UnknownDevice) - { - if(remaining_tries == 1) - 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(DEBUG, "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 %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(); -} - -void DeviceFinder::removeDevice(const char* path) -{ - this->stopAndDeleteAllDevicesIn(std::string(path)); -} From 825967b140562e20ad8c7eaba854480ac66d9226 Mon Sep 17 00:00:00 2001 From: pixl Date: Mon, 13 Jul 2020 22:42:23 -0400 Subject: [PATCH 75/81] TODO cleanup: Add PowerSwitchLocation enum --- src/logid/backend/dj/Receiver.cpp | 7 +++++-- src/logid/backend/dj/Receiver.h | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp index a79c5bf..0377778 100644 --- a/src/logid/backend/dj/Receiver.cpp +++ b/src/logid/backend/dj/Receiver.cpp @@ -191,7 +191,11 @@ struct Receiver::ExtendedPairingInfo for(uint8_t i = 0; i < 4; i++) info.reportTypes[i] = response[i + 4]; - info.powerSwitchLocation = response[8] & 0xf; + uint8_t psl = response[8] & 0xf; + if(psl > 0xc) + info.powerSwitchLocation = PowerSwitchLocation::Reserved; + else + info.powerSwitchLocation = static_cast(psl); return info; } @@ -325,7 +329,6 @@ Receiver::ConnectionStatusEvent Receiver::connectionStatusEvent(Report& report) void Receiver::listen() { if(!_raw_device->isListening()) - ///TODO: Kill RawDevice? _raw_device->listenAsync(); if(_raw_device->eventHandlers().find("RECV_HIDPP") == diff --git a/src/logid/backend/dj/Receiver.h b/src/logid/backend/dj/Receiver.h index 12d1e1b..96b74db 100644 --- a/src/logid/backend/dj/Receiver.h +++ b/src/logid/backend/dj/Receiver.h @@ -126,11 +126,28 @@ namespace dj DeviceType::DeviceType deviceType; }; + enum class PowerSwitchLocation : uint8_t + { + Reserved = 0x0, + Base = 0x1, + TopCase = 0x2, + TopRightEdge = 0x3, + Other = 0x4, + TopLeft = 0x5, + BottomLeft = 0x6, + TopRight = 0x7, + BottomRight = 0x8, + TopEdge = 0x9, + RightEdge = 0xa, + LeftEdge = 0xb, + BottomEdge = 0xc + }; + struct ExtendedPairingInfo { uint32_t serialNumber; uint8_t reportTypes[4]; - uint8_t powerSwitchLocation; ///TODO: Make enum + PowerSwitchLocation powerSwitchLocation; }; struct PairingInfo getPairingInfo(hidpp::DeviceIndex index); From 937225b6f28622e1e31be16e0c8b4e76cc79d903 Mon Sep 17 00:00:00 2001 From: pixl Date: Mon, 13 Jul 2020 22:44:22 -0400 Subject: [PATCH 76/81] TODO cleanup: Remove error check todo in DeviceManager --- src/logid/DeviceManager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index da05400..98a678a 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -58,8 +58,7 @@ void DeviceManager::addDevice(std::string path) receiver->run(); _receivers.emplace(path, receiver); } else { - /* TODO: Error check? - * TODO: Can non-receivers only contain 1 device? + /* TODO: Can non-receivers only contain 1 device? * If the device exists, it is guaranteed to be an HID++ 2.0 device */ if(defaultExists) { auto device = std::make_shared(path, hidpp::DefaultDevice); From d3d4a2755ad82a5fbc1e91b1bcad67cca5fbefcd Mon Sep 17 00:00:00 2001 From: pixl Date: Tue, 14 Jul 2020 00:12:40 -0400 Subject: [PATCH 77/81] Apply clang-tidy inspections --- src/logid/Configuration.cpp | 44 +++++++++---------- src/logid/Configuration.h | 10 ++--- src/logid/Device.cpp | 7 ++- src/logid/Device.h | 6 +-- src/logid/DeviceManager.cpp | 3 -- src/logid/InputDevice.cpp | 19 ++++---- src/logid/InputDevice.h | 14 +++--- src/logid/Receiver.cpp | 4 +- src/logid/Receiver.h | 2 +- src/logid/backend/dj/ReceiverMonitor.cpp | 5 +++ src/logid/backend/dj/ReceiverMonitor.h | 1 + src/logid/backend/hidpp/defs.h | 6 +-- src/logid/backend/hidpp20/Device.h | 6 +-- src/logid/backend/hidpp20/Error.cpp | 1 - src/logid/backend/hidpp20/EssentialFeature.h | 6 +-- src/logid/backend/hidpp20/Feature.h | 6 +-- .../backend/hidpp20/features/HiresScroll.h | 1 - .../hidpp20/features/ReprogControls.cpp | 8 ++-- src/logid/features/RemapButton.cpp | 10 ++--- src/logid/features/RemapButton.h | 2 +- src/logid/logid.cpp | 17 ++++--- src/logid/util/thread.cpp | 4 +- src/logid/util/workqueue.cpp | 2 +- 23 files changed, 94 insertions(+), 90 deletions(-) diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index 5f9a7a9..c287441 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -42,15 +42,7 @@ Configuration::Configuration(const std::string& config_file) } const Setting &root = _config.getRoot(); - Setting* devices; - try { devices = &root["devices"]; } - catch(const SettingNotFoundException &e) { - logPrintf(WARN, "No devices listed in config file."); - return; - } - - _worker_threads = LOGID_DEFAULT_WORKER_COUNT; try { auto& worker_count = root["workers"]; if(worker_count.getType() == Setting::TypeInt) { @@ -66,7 +58,6 @@ Configuration::Configuration(const std::string& config_file) // Ignore } - _io_timeout = LOGID_DEFAULT_RAWDEVICE_TIMEOUT; try { auto& timeout = root["io_timeout"]; if(timeout.isNumber()) { @@ -82,30 +73,37 @@ Configuration::Configuration(const std::string& config_file) // Ignore } - for(int i = 0; i < devices->getLength(); i++) { - const Setting &device = (*devices)[i]; - std::string name; - try { - if(!device.lookupValue("name", name)) { - logPrintf(WARN, "Line %d: 'name' must be a string, skipping " - "device.", device["name"].getSourceLine()); + try { + auto& devices = root["devices"]; + + for(int i = 0; i < devices.getLength(); i++) { + const Setting& device = devices[i]; + std::string name; + try { + if(!device.lookupValue("name", name)) { + logPrintf(WARN, "Line %d: 'name' must be a string, skipping" + " device.", device["name"].getSourceLine()); + continue; + } + } catch(SettingNotFoundException &e) { + logPrintf(WARN, "Line %d: Missing name field, skipping device." + , device.getSourceLine()); continue; } - } catch(SettingNotFoundException &e) { - logPrintf(WARN, "Line %d: Missing 'name' field, skipping device." - , device.getSourceLine()); - continue; + _device_paths.insert({name, device.getPath()}); } - _device_paths.insert({name, device.getPath()}); + } + catch(const SettingNotFoundException &e) { + logPrintf(WARN, "No devices listed in config file."); } } -libconfig::Setting& Configuration::getSetting(std::string path) +libconfig::Setting& Configuration::getSetting(const std::string& path) { return _config.lookup(path); } -std::string Configuration::getDevice(std::string name) +std::string Configuration::getDevice(const std::string& name) { auto it = _device_paths.find(name); if(it == _device_paths.end()) diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index 3666fbc..7a196ed 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -24,7 +24,7 @@ #include #include -#define LOGID_DEFAULT_RAWDEVICE_TIMEOUT std::chrono::seconds(2) +#define LOGID_DEFAULT_IO_TIMEOUT std::chrono::seconds(2) #define LOGID_DEFAULT_WORKER_COUNT 4 namespace logid @@ -34,8 +34,8 @@ namespace logid public: explicit Configuration(const std::string& config_file); Configuration() = default; - libconfig::Setting& getSetting(std::string path); - std::string getDevice(std::string name); + libconfig::Setting& getSetting(const std::string& path); + std::string getDevice(const std::string& name); class DeviceNotFound : public std::exception { @@ -50,8 +50,8 @@ namespace logid int workerCount() const; private: std::map _device_paths; - std::chrono::milliseconds _io_timeout = LOGID_DEFAULT_RAWDEVICE_TIMEOUT; - int _worker_threads; + std::chrono::milliseconds _io_timeout = LOGID_DEFAULT_IO_TIMEOUT; + int _worker_threads = LOGID_DEFAULT_WORKER_COUNT; libconfig::Config _config; }; diff --git a/src/logid/Device.cpp b/src/logid/Device.cpp index 7f59980..9d32588 100644 --- a/src/logid/Device.cpp +++ b/src/logid/Device.cpp @@ -113,7 +113,10 @@ void Device::_makeResetMechanism() try { hidpp20::Reset reset(&_hidpp20); _reset_mechanism = std::make_unique>( - [dev=&this->_hidpp20]{ hidpp20::Reset(dev).reset(); }); + [dev=&this->_hidpp20]{ + hidpp20::Reset reset(dev); + reset.reset(reset.getProfile()); + }); } catch(hidpp20::UnsupportedFeature& e) { // Reset unsupported, ignore. } @@ -130,7 +133,7 @@ DeviceConfig::DeviceConfig(const std::shared_ptr& config, Device* } } -libconfig::Setting& DeviceConfig::getSetting(std::string path) +libconfig::Setting& DeviceConfig::getSetting(const std::string& path) { return _config->getSetting(_root_setting + '/' + path); } diff --git a/src/logid/Device.h b/src/logid/Device.h index 66147b3..2a0cd42 100644 --- a/src/logid/Device.h +++ b/src/logid/Device.h @@ -35,7 +35,7 @@ namespace logid public: DeviceConfig(const std::shared_ptr& config, Device* device); - libconfig::Setting& getSetting(std::string path); + libconfig::Setting& getSetting(const std::string& path); private: Device* _device; std::string _root_setting; @@ -72,8 +72,8 @@ namespace logid try { return std::dynamic_pointer_cast(it->second); } catch(std::bad_cast& e) { - logPrintf(ERROR, "bad_cast while getting device feature %s: " - "%s", name.c_str(), e.what()); + logPrintf(ERROR, "bad_cast while getting device feature %s: %s", + name.c_str(), e.what()); return nullptr; } } diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index 98a678a..40185df 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -23,11 +23,8 @@ #include "Receiver.h" #include "util/log.h" #include "backend/hidpp10/Error.h" -#include "backend/dj/Receiver.h" #include "backend/Error.h" -#define NON_WIRELESS_DEV(index) (index) == HIDPP::DefaultDevice ? "default" : "corded" - using namespace logid; using namespace logid::backend; diff --git a/src/logid/InputDevice.cpp b/src/logid/InputDevice.cpp index 041e3f4..aecf548 100644 --- a/src/logid/InputDevice.cpp +++ b/src/logid/InputDevice.cpp @@ -17,7 +17,6 @@ */ #include -#include #include "InputDevice.h" @@ -25,13 +24,13 @@ extern "C" { #include #include -}; +} using namespace logid; -InputDevice::InvalidEventCode::InvalidEventCode(std::string name) +InputDevice::InvalidEventCode::InvalidEventCode(const std::string& name) : + _what ("Invalid event code " + name) { - _what = "Invalid event code " + name; } const char* InputDevice::InvalidEventCode::what() const noexcept @@ -46,10 +45,10 @@ InputDevice::InputDevice(const char* name) ///TODO: Is it really a good idea to enable all events? libevdev_enable_event_type(device, EV_KEY); - for(int i = 0; i < KEY_CNT; i++) + for(unsigned int i = 0; i < KEY_CNT; i++) libevdev_enable_event_code(device, EV_KEY, i, nullptr); libevdev_enable_event_type(device, EV_REL); - for(int i = 0; i < REL_CNT; i++) + for(unsigned int i = 0; i < REL_CNT; i++) libevdev_enable_event_code(device, EV_REL, i, nullptr); int err = libevdev_uinput_create_from_device(device, @@ -80,14 +79,14 @@ void InputDevice::releaseKey(uint code) _sendEvent(EV_KEY, code, 0); } -uint InputDevice::toKeyCode(std::string name) +uint InputDevice::toKeyCode(const std::string& name) { - return _toEventCode(EV_KEY, std::move(name)); + return _toEventCode(EV_KEY, name); } -uint InputDevice::toAxisCode(std::string name) +uint InputDevice::toAxisCode(const std::string& name) { - return _toEventCode(EV_REL, std::move(name)); + return _toEventCode(EV_REL, name); } uint InputDevice::_toEventCode(uint type, const std::string& name) diff --git a/src/logid/InputDevice.h b/src/logid/InputDevice.h index 0072271..5b2cd22 100644 --- a/src/logid/InputDevice.h +++ b/src/logid/InputDevice.h @@ -25,22 +25,20 @@ extern "C" { #include #include -}; +} namespace logid { - typedef uint keycode; - class InputDevice { public: class InvalidEventCode : public std::exception { public: - explicit InvalidEventCode(std::string name); + explicit InvalidEventCode(const std::string& name); const char* what() const noexcept override; private: - std::string _what; + const std::string _what; }; explicit InputDevice(const char *name); ~InputDevice(); @@ -49,15 +47,15 @@ namespace logid void pressKey(uint code); void releaseKey(uint code); - static uint toKeyCode(std::string name); - static uint toAxisCode(std::string name); + static uint toKeyCode(const std::string& name); + static uint toAxisCode(const 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; + libevdev_uinput* ui_device{}; }; extern std::unique_ptr virtual_input; diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index a3f8adb..499cd9b 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -16,7 +16,6 @@ * */ -#include #include "Receiver.h" #include "util/log.h" #include "backend/hidpp10/Error.h" @@ -26,7 +25,8 @@ using namespace logid; using namespace logid::backend; -Receiver::Receiver(std::string path) : dj::ReceiverMonitor(path), _path (path) +Receiver::Receiver(const std::string& path) : + dj::ReceiverMonitor(path), _path (path) { } diff --git a/src/logid/Receiver.h b/src/logid/Receiver.h index 15c0f9b..844a42d 100644 --- a/src/logid/Receiver.h +++ b/src/logid/Receiver.h @@ -28,7 +28,7 @@ namespace logid class Receiver : public backend::dj::ReceiverMonitor { public: - Receiver(std::string path); + Receiver(const std::string& path); protected: void addDevice(backend::hidpp::DeviceConnectionEvent event) override; diff --git a/src/logid/backend/dj/ReceiverMonitor.cpp b/src/logid/backend/dj/ReceiverMonitor.cpp index d3f9fb2..80a9283 100644 --- a/src/logid/backend/dj/ReceiverMonitor.cpp +++ b/src/logid/backend/dj/ReceiverMonitor.cpp @@ -40,6 +40,11 @@ ReceiverMonitor::ReceiverMonitor(std::string path) : _receiver ( _receiver->enableHidppNotifications(notification_flags); } +ReceiverMonitor::~ReceiverMonitor() +{ + this->stop(); +} + void ReceiverMonitor::run() { _receiver->listen(); diff --git a/src/logid/backend/dj/ReceiverMonitor.h b/src/logid/backend/dj/ReceiverMonitor.h index d5deb98..b56aced 100644 --- a/src/logid/backend/dj/ReceiverMonitor.h +++ b/src/logid/backend/dj/ReceiverMonitor.h @@ -33,6 +33,7 @@ namespace dj { public: explicit ReceiverMonitor(std::string path); + ~ReceiverMonitor(); void enumerate(); void run(); diff --git a/src/logid/backend/hidpp/defs.h b/src/logid/backend/hidpp/defs.h index 1590e06..3a85470 100644 --- a/src/logid/backend/hidpp/defs.h +++ b/src/logid/backend/hidpp/defs.h @@ -16,8 +16,8 @@ * */ -#ifndef LOGID_HIDPP_DEFS_H -#define LOGID_HIDPP_DEFS_H +#ifndef LOGID_BACKEND_HIDPP_DEFS_H +#define LOGID_BACKEND_HIDPP_DEFS_H #define LOGID_HIDPP_SOFTWARE_ID 0 @@ -52,4 +52,4 @@ namespace hidpp static constexpr std::size_t LongParamLength = 16; } } } -#endif //LOGID_HIDPP_DEFS_H \ No newline at end of file +#endif //LOGID_BACKEND_HIDPP_DEFS_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Device.h b/src/logid/backend/hidpp20/Device.h index 871363d..747d36d 100644 --- a/src/logid/backend/hidpp20/Device.h +++ b/src/logid/backend/hidpp20/Device.h @@ -16,8 +16,8 @@ * */ -#ifndef LOGID_HIDPP20_DEVICE_H -#define LOGID_HIDPP20_DEVICE_H +#ifndef LOGID_BACKEND_HIDPP20_DEVICE_H +#define LOGID_BACKEND_HIDPP20_DEVICE_H #include "../hidpp/Device.h" #include @@ -37,4 +37,4 @@ namespace hidpp20 { }; }}} -#endif //LOGID_HIDPP20_DEVICE_H \ No newline at end of file +#endif //LOGID_BACKEND_HIDPP20_DEVICE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Error.cpp b/src/logid/backend/hidpp20/Error.cpp index 19a0ad8..eea11a9 100644 --- a/src/logid/backend/hidpp20/Error.cpp +++ b/src/logid/backend/hidpp20/Error.cpp @@ -17,7 +17,6 @@ */ #include -#include #include "Error.h" using namespace logid::backend::hidpp20; diff --git a/src/logid/backend/hidpp20/EssentialFeature.h b/src/logid/backend/hidpp20/EssentialFeature.h index f72a244..bfc578f 100644 --- a/src/logid/backend/hidpp20/EssentialFeature.h +++ b/src/logid/backend/hidpp20/EssentialFeature.h @@ -16,8 +16,8 @@ * */ -#ifndef LOGID_HIDPP20_ESSENTIAL_FEATURE_H -#define LOGID_HIDPP20_ESSENTIAL_FEATURE_H +#ifndef LOGID_BACKEND_HIDPP20_ESSENTIAL_FEATURE_H +#define LOGID_BACKEND_HIDPP20_ESSENTIAL_FEATURE_H // WARNING: UNSAFE @@ -47,4 +47,4 @@ namespace hidpp20 }; }}} -#endif //LOGID_HIDPP20_ESSENTIAL_FEATURE_H \ No newline at end of file +#endif //LOGID_BACKEND_HIDPP20_ESSENTIAL_FEATURE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/Feature.h b/src/logid/backend/hidpp20/Feature.h index cc4732b..06ec38f 100644 --- a/src/logid/backend/hidpp20/Feature.h +++ b/src/logid/backend/hidpp20/Feature.h @@ -16,8 +16,8 @@ * */ -#ifndef LOGID_HIDPP20_FEATURE_H -#define LOGID_HIDPP20_FEATURE_H +#ifndef LOGID_BACKEND_HIDPP20_FEATURE_H +#define LOGID_BACKEND_HIDPP20_FEATURE_H #include #include "Device.h" @@ -51,4 +51,4 @@ namespace hidpp20 { }; }}} -#endif //LOGID_HIDPP20_FEATURE_H \ No newline at end of file +#endif //LOGID_BACKEND_HIDPP20_FEATURE_H \ No newline at end of file diff --git a/src/logid/backend/hidpp20/features/HiresScroll.h b/src/logid/backend/hidpp20/features/HiresScroll.h index f008c64..69dee11 100644 --- a/src/logid/backend/hidpp20/features/HiresScroll.h +++ b/src/logid/backend/hidpp20/features/HiresScroll.h @@ -85,7 +85,6 @@ namespace hidpp20 void setMode(uint8_t mode); bool getRatchetState(); - ///TODO: Event handlers static WheelStatus wheelMovementEvent(const hidpp::Report& report); static RatchetState ratchetSwitchEvent(const hidpp::Report& report); }; diff --git a/src/logid/backend/hidpp20/features/ReprogControls.cpp b/src/logid/backend/hidpp20/features/ReprogControls.cpp index 34334c4..1bf0fcc 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.cpp +++ b/src/logid/backend/hidpp20/features/ReprogControls.cpp @@ -44,10 +44,10 @@ DEFINE_REPROG(ReprogControlsV4, ReprogControlsV3) std::shared_ptr ReprogControls::autoVersion(Device *dev) { - MAKE_REPROG(ReprogControlsV4, dev); - MAKE_REPROG(ReprogControlsV3, dev); - MAKE_REPROG(ReprogControlsV2_2, dev); - MAKE_REPROG(ReprogControlsV2, dev); + MAKE_REPROG(ReprogControlsV4, dev) + MAKE_REPROG(ReprogControlsV3, dev) + MAKE_REPROG(ReprogControlsV2_2, dev) + MAKE_REPROG(ReprogControlsV2, dev) // If base version cannot be made, throw error return std::make_shared(dev); diff --git a/src/logid/features/RemapButton.cpp b/src/logid/features/RemapButton.cpp index 13204b0..3078084 100644 --- a/src/logid/features/RemapButton.cpp +++ b/src/logid/features/RemapButton.cpp @@ -43,7 +43,7 @@ RemapButton::~RemapButton() void RemapButton::configure() { ///TODO: DJ reporting trickery if cannot be remapped - for(auto& i : _config.buttons()) { + for(const auto& i : _config.buttons()) { hidpp20::ReprogControls::ControlInfo info{}; try { info = _reprog_controls->getControlIdInfo(i.first); @@ -89,23 +89,23 @@ void RemapButton::listen() report)); else { // RawXY auto divertedXY = _reprog_controls->divertedRawXYEvent(report); - for(auto& button : this->_config.buttons()) + for(const 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 new_state) +void RemapButton::_buttonEvent(const std::set& new_state) { // Ensure I/O doesn't occur while updating button state std::lock_guard lock(_button_lock); // Press all added buttons - for(auto& i : new_state) { + for(const auto& i : new_state) { auto old_i = _pressed_buttons.find(i); if(old_i != _pressed_buttons.end()) { _pressed_buttons.erase(old_i); diff --git a/src/logid/features/RemapButton.h b/src/logid/features/RemapButton.h index a45e767..f4aa4b6 100644 --- a/src/logid/features/RemapButton.h +++ b/src/logid/features/RemapButton.h @@ -44,7 +44,7 @@ namespace features std::map> _buttons; }; private: - void _buttonEvent(std::set new_state); + void _buttonEvent(const std::set& new_state); Config _config; std::shared_ptr _reprog_controls; std::set _pressed_buttons; diff --git a/src/logid/logid.cpp b/src/logid/logid.cpp index 42c75e4..ddb77ec 100644 --- a/src/logid/logid.cpp +++ b/src/logid/logid.cpp @@ -37,7 +37,10 @@ using namespace logid; -std::string config_file = DEFAULT_CONFIG_FILE; +struct CmdlineOptions +{ + std::string config_file = DEFAULT_CONFIG_FILE; +}; LogLevel logid::global_loglevel = INFO; std::shared_ptr logid::global_config; @@ -72,7 +75,7 @@ void logid::reload() } */ -void readCliOptions(int argc, char** argv) +void readCliOptions(const int argc, char** argv, CmdlineOptions& options) { for(int i = 1; i < argc; i++) { Option option = Option::None; @@ -132,7 +135,7 @@ void readCliOptions(int argc, char** argv) logPrintf(ERROR, "Config file is not specified."); exit(EXIT_FAILURE); } - config_file = argv[i]; + options.config_file = argv[i]; break; } case Option::Help: @@ -157,16 +160,18 @@ Possible options are: int main(int argc, char** argv) { - readCliOptions(argc, argv); + CmdlineOptions options{}; + readCliOptions(argc, argv, options); // Read config try { - global_config = std::make_shared(config_file); + global_config = std::make_shared(options.config_file); } catch (std::exception &e) { global_config = std::make_shared(); } - global_workqueue = std::make_shared(global_config->workerCount()); + global_workqueue = std::make_shared( + global_config->workerCount()); //Create a virtual input device try { diff --git a/src/logid/util/thread.cpp b/src/logid/util/thread.cpp index 3460bd7..be496dc 100644 --- a/src/logid/util/thread.cpp +++ b/src/logid/util/thread.cpp @@ -45,8 +45,8 @@ void thread::spawn(const std::function& function, void thread::run() { - _thread = std::make_shared([f=this->_function, - eh=this->_exception_handler]() { + _thread = std::make_shared( + [f=this->_function,eh=this->_exception_handler]() { try { (*f)(); } catch (std::exception& e) { diff --git a/src/logid/util/workqueue.cpp b/src/logid/util/workqueue.cpp index a7e04f7..2bf409d 100644 --- a/src/logid/util/workqueue.cpp +++ b/src/logid/util/workqueue.cpp @@ -28,7 +28,7 @@ workqueue::workqueue(std::size_t thread_count) : _manager_thread ( )), _continue_run (false), _worker_count (thread_count) { _workers.reserve(_worker_count); - for(std::size_t i = 0; i < thread_count; i++) + for(std::size_t i = 0; i < _worker_count; i++) _workers.push_back(std::make_unique(this, i)); _manager_thread->run(); } From 89b9829b844ff725b3ebc690d06a01a8d9b6c761 Mon Sep 17 00:00:00 2001 From: pixl Date: Tue, 14 Jul 2020 16:20:06 -0400 Subject: [PATCH 78/81] Fix bug where GetPairingInfo would be offset by -1. --- src/logid/backend/dj/Receiver.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/logid/backend/dj/Receiver.cpp b/src/logid/backend/dj/Receiver.cpp index 0377778..78909da 100644 --- a/src/logid/backend/dj/Receiver.cpp +++ b/src/logid/backend/dj/Receiver.cpp @@ -157,17 +157,17 @@ struct Receiver::PairingInfo { std::vector request(1); request[0] = index; - request[0] += 0x19; + request[0] += 0x1f; auto response = _hidpp10_device.getRegister(PairingInfo, request, hidpp::ReportType::Long); struct PairingInfo info{}; - info.destinationId = response[0]; - info.reportInterval = response[1]; - info.pid = response[2]; + info.destinationId = response[1]; + info.reportInterval = response[2]; + info.pid = response[4]; info.pid |= (response[3] << 8); - info.deviceType = static_cast(response[6]); + info.deviceType = static_cast(response[7]); return info; } @@ -177,7 +177,7 @@ struct Receiver::ExtendedPairingInfo { std::vector request(1); request[0] = index; - request[0] += 0x29; + request[0] += 0x2f; auto response = _hidpp10_device.getRegister(PairingInfo, request, hidpp::ReportType::Long); @@ -186,10 +186,10 @@ struct Receiver::ExtendedPairingInfo info.serialNumber = 0; for(uint8_t i = 0; i < 4; i++) - info.serialNumber |= (response[i] << 8*i); + info.serialNumber |= (response[i+1] << 8*i); for(uint8_t i = 0; i < 4; i++) - info.reportTypes[i] = response[i + 4]; + info.reportTypes[i] = response[i + 5]; uint8_t psl = response[8] & 0xf; if(psl > 0xc) @@ -204,17 +204,17 @@ std::string Receiver::getDeviceName(hidpp::DeviceIndex index) { std::vector request(1); request[0] = index; - request[0] += 0x39; + request[0] += 0x3f; auto response = _hidpp10_device.getRegister(PairingInfo, request, hidpp::ReportType::Long); - uint8_t size = response[0]; + uint8_t size = response[1]; assert(size <= 14); std::string name(size, ' '); for(std::size_t i = 0; i < size; i++) - name[i] = response[i + 1]; + name[i] = response[i + 2]; return name; } From 497ec07bdf67ba0cac59dac162a3d8e787dc611b Mon Sep 17 00:00:00 2001 From: pixl Date: Tue, 14 Jul 2020 16:20:58 -0400 Subject: [PATCH 79/81] Properly set PID on dummy connect events --- src/logid/backend/hidpp/Device.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/logid/backend/hidpp/Device.cpp b/src/logid/backend/hidpp/Device.cpp index 5e62a66..d4bf994 100644 --- a/src/logid/backend/hidpp/Device.cpp +++ b/src/logid/backend/hidpp/Device.cpp @@ -71,7 +71,10 @@ Device::Device(std::shared_ptr receiver, if(!event.linkEstablished) throw InvalidDevice(InvalidDevice::Asleep); - _pid = event.pid; + if(!event.fromTimeoutCheck) + _pid = event.pid; + else + _pid = receiver->getPairingInfo(_index).pid; _init(); } From 018bdb83addfd0112a334f8e564a35bcf2407f58 Mon Sep 17 00:00:00 2001 From: pixl Date: Tue, 14 Jul 2020 16:21:14 -0400 Subject: [PATCH 80/81] Add ability to ignore devices --- src/logid/Configuration.cpp | 45 +++++++++++++++++++++++++++++++++++++ src/logid/Configuration.h | 3 +++ src/logid/DeviceManager.cpp | 11 +++++++++ src/logid/Receiver.cpp | 7 ++++++ 4 files changed, 66 insertions(+) diff --git a/src/logid/Configuration.cpp b/src/logid/Configuration.cpp index c287441..a5fad09 100644 --- a/src/logid/Configuration.cpp +++ b/src/logid/Configuration.cpp @@ -96,6 +96,46 @@ Configuration::Configuration(const std::string& config_file) catch(const SettingNotFoundException &e) { logPrintf(WARN, "No devices listed in config file."); } + + try { + auto& ignore = root.lookup("ignore"); + if(ignore.getType() == libconfig::Setting::TypeInt) { + _ignore_list.insert((int)ignore); + } else if(ignore.isList() || ignore.isArray()) { + int ignore_count = ignore.getLength(); + for(int i = 0; i < ignore_count; i++) { + if(ignore[i].getType() != libconfig::Setting::TypeInt) { + logPrintf(WARN, "Line %d: ignore must refer to device PIDs", + ignore[i].getSourceLine()); + if(ignore.isArray()) + break; + } else + _ignore_list.insert((int)ignore[i]); + } + } + } catch(const SettingNotFoundException& e) { + // May be called blacklist + try { + auto& ignore = root.lookup("blacklist"); + if(ignore.getType() == libconfig::Setting::TypeInt) { + _ignore_list.insert((int)ignore); + } else if(ignore.isList() || ignore.isArray()) { + int ignore_count = ignore.getLength(); + for(int i = 0; i < ignore_count; i++) { + if(ignore[i].getType() != libconfig::Setting::TypeInt) { + logPrintf(WARN, "Line %d: blacklist must refer to " + "device PIDs", + ignore[i].getSourceLine()); + if(ignore.isArray()) + break; + } else + _ignore_list.insert((int)ignore[i]); + } + } + } catch(const SettingNotFoundException& e) { + // Ignore + } + } } libconfig::Setting& Configuration::getSetting(const std::string& path) @@ -112,6 +152,11 @@ std::string Configuration::getDevice(const std::string& name) return it->second; } +bool Configuration::isIgnored(uint16_t pid) const +{ + return _ignore_list.find(pid) != _ignore_list.end(); +} + Configuration::DeviceNotFound::DeviceNotFound(std::string name) : _name (std::move(name)) { diff --git a/src/logid/Configuration.h b/src/logid/Configuration.h index 7a196ed..03eb62d 100644 --- a/src/logid/Configuration.h +++ b/src/logid/Configuration.h @@ -23,6 +23,7 @@ #include #include #include +#include #define LOGID_DEFAULT_IO_TIMEOUT std::chrono::seconds(2) #define LOGID_DEFAULT_WORKER_COUNT 4 @@ -36,6 +37,7 @@ namespace logid Configuration() = default; libconfig::Setting& getSetting(const std::string& path); std::string getDevice(const std::string& name); + bool isIgnored(uint16_t pid) const; class DeviceNotFound : public std::exception { @@ -50,6 +52,7 @@ namespace logid int workerCount() const; private: std::map _device_paths; + std::set _ignore_list; std::chrono::milliseconds _io_timeout = LOGID_DEFAULT_IO_TIMEOUT; int _worker_threads = LOGID_DEFAULT_WORKER_COUNT; libconfig::Config _config; diff --git a/src/logid/DeviceManager.cpp b/src/logid/DeviceManager.cpp index 40185df..e8016d4 100644 --- a/src/logid/DeviceManager.cpp +++ b/src/logid/DeviceManager.cpp @@ -32,6 +32,17 @@ void DeviceManager::addDevice(std::string path) { bool defaultExists = true; bool isReceiver = false; + + // Check if device is ignored before continuing + { + raw::RawDevice raw_dev(path); + if(global_config->isIgnored(raw_dev.productId())) { + logPrintf(DEBUG, "%s: Device 0x%04x ignored.", + path.c_str(), raw_dev.productId()); + return; + } + } + try { hidpp::Device device(path, hidpp::DefaultDevice); isReceiver = device.version() == std::make_tuple(1, 0); diff --git a/src/logid/Receiver.cpp b/src/logid/Receiver.cpp index 499cd9b..15595e5 100644 --- a/src/logid/Receiver.cpp +++ b/src/logid/Receiver.cpp @@ -34,6 +34,13 @@ void Receiver::addDevice(hidpp::DeviceConnectionEvent event) { std::unique_lock lock(_devices_change); try { + // Check if device is ignored before continuing + if(global_config->isIgnored(event.pid)) { + logPrintf(DEBUG, "%s:%d: Device 0x%04x ignored.", + _path.c_str(), event.index, event.pid); + return; + } + auto dev = _devices.find(event.index); if(dev != _devices.end()) { if(event.linkEstablished) From 56d10898fbab54b07b62c56be3b92b8db0aa419d Mon Sep 17 00:00:00 2001 From: pixl Date: Wed, 15 Jul 2020 00:26:12 -0400 Subject: [PATCH 81/81] Print CIDs on device connect --- .../hidpp20/features/ReprogControls.cpp | 6 ++++++ .../backend/hidpp20/features/ReprogControls.h | 2 ++ src/logid/backend/raw/RawDevice.cpp | 4 ++-- src/logid/features/RemapButton.cpp | 19 +++++++++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/logid/backend/hidpp20/features/ReprogControls.cpp b/src/logid/backend/hidpp20/features/ReprogControls.cpp index 1bf0fcc..ce10bb0 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.cpp +++ b/src/logid/backend/hidpp20/features/ReprogControls.cpp @@ -92,6 +92,12 @@ void ReprogControls::initCidMap() _cids_initialized = true; } +const std::map& + ReprogControls::getControls() const +{ + return _cids; +} + ReprogControls::ControlInfo ReprogControls::getControlIdInfo(uint16_t cid) { if(!_cids_initialized) diff --git a/src/logid/backend/hidpp20/features/ReprogControls.h b/src/logid/backend/hidpp20/features/ReprogControls.h index a3d342c..a62b464 100644 --- a/src/logid/backend/hidpp20/features/ReprogControls.h +++ b/src/logid/backend/hidpp20/features/ReprogControls.h @@ -97,6 +97,8 @@ namespace hidpp20 virtual void initCidMap(); + const std::map& getControls() const; + // Onlu controlId and flags will be set virtual ControlInfo getControlReporting(uint16_t cid); diff --git a/src/logid/backend/raw/RawDevice.cpp b/src/logid/backend/raw/RawDevice.cpp index e4f919a..c7d5ce5 100644 --- a/src/logid/backend/raw/RawDevice.cpp +++ b/src/logid/backend/raw/RawDevice.cpp @@ -311,7 +311,7 @@ std::vector RawDevice::_respondToReport int RawDevice::_sendReport(const std::vector& report) { std::lock_guard lock(_dev_io); - if(logid::global_loglevel == LogLevel::RAWREPORT) { + if(logid::global_loglevel <= LogLevel::RAWREPORT) { printf("[RAWREPORT] %s OUT: ", _path.c_str()); for(auto &i : report) printf("%02x ", i); @@ -389,7 +389,7 @@ int RawDevice::_readReport(std::vector &report, if(0 == ret) throw backend::TimeoutError(); - if(logid::global_loglevel == LogLevel::RAWREPORT) { + if(logid::global_loglevel <= LogLevel::RAWREPORT) { printf("[RAWREPORT] %s IN: ", _path.c_str()); for(auto &i : report) printf("%02x ", i); diff --git a/src/logid/features/RemapButton.cpp b/src/logid/features/RemapButton.cpp index 3078084..9f8f813 100644 --- a/src/logid/features/RemapButton.cpp +++ b/src/logid/features/RemapButton.cpp @@ -33,6 +33,25 @@ RemapButton::RemapButton(Device *dev): DeviceFeature(dev), _config (dev), _reprog_controls (hidpp20::ReprogControls::autoVersion(&dev->hidpp20())) { _reprog_controls->initCidMap(); + + if(global_loglevel <= DEBUG) { + #define FLAG(x) control.second.flags & hidpp20::ReprogControls::x ? \ + "YES" : "" + #define ADDITIONAL_FLAG(x) control.second.additionalFlags & \ + hidpp20::ReprogControls::x ? "YES" : "" + + // Print CIDs, originally by zv0n + logPrintf(DEBUG, "%s:%d remappable buttons:", + dev->hidpp20().devicePath().c_str(), + dev->hidpp20().deviceIndex()); + logPrintf(DEBUG, "CID | reprog? | fn key? | mouse key? | " + "gesture support?"); + for(const auto & control : _reprog_controls->getControls()) + logPrintf(DEBUG, "0x%02x | %-7s | %-7s | %-10s | %s", + control.first, FLAG(TemporaryDivertable), FLAG(FKey), + FLAG(MouseButton), ADDITIONAL_FLAG(RawXY)); + #undef FLAG + } } RemapButton::~RemapButton()